WORLDVIEW
将世界视为一个实时系统。
在一个交互式 3D 地球仪上探索航班、卫星、船舶、地震、交通和公共 CCTV 直播流。
打开实时地球仪
·
部署你自己的副本
https://github.com/user-attachments/assets/b2bd05d2-f7be-49c8-a8c6-452b6b60cb34
## 概述
Worldview 是一个全栈态势感知仪表板,它将多个实时的公开数据源聚合到一个交互式 3D 地球仪上。它将高密度的战术界面与可选的 CRT、夜视和热成像渲染模式结合在一起——既不隐藏底层数据源,也不要求必须配置所有 API key 才能开始使用。
### 核心功能
| 图层 | 数据源 | 更新频率 | 描述 |
|---|---|---|---|
| ✈️ **航班** | FlightRadar24 + adsb.fi | 5–20 秒 | 全球约 27,000 架航空器,包含高度分层、航线弧、航位推算 |
| 🛰️ **卫星** | CelesTrak TLE + SGP4 | 2 秒推算 | 实时轨道位置、轨道路径、地面轨迹、天底线 |
| 🌋 **地震** | USGS GeoJSON | 60 秒 | 过去 24 小时内,震级缩放的脉冲标记并带有颜色编码 |
| 🚗 **交通** | OpenStreetMap Overpass | 按需 | 道路网络覆盖层,带有动态车辆粒子模拟 |
| 🚢 **海军 / AIS** | AISStream.io WebSocket | 30 秒(20 秒突发 + 60 秒缓存) | 全球船舶追踪,包含船舶类型分类、航向轨迹、航位推算 |
| 📹 **CCTV** | TfL, Austin TX, Transport NSW | 5 分钟 | 来自伦敦、奥斯汀和新南威尔士州的实时摄像头流 |
## 截图
界面特性包括:
- **启动画面** — 采用打字机动画的军用风格启动序列
- **操作面板**(左侧) — 图层开关、着色器模式、高度过滤器
- **情报流**(右侧) — 来自所有数据源的实时事件流
- **状态栏**(底部) — 摄像机坐标(DMS)、UTC 时钟、实体计数
- **追踪实体面板** — 锁定详情视图(按 ESC 解锁)
- **十字准星覆盖层** — 屏幕中心的瞄准目标
## 技术栈
### 前端
- **React 19** — 带有 hooks 的函数式组件
- **TypeScript 5.9** — 严格模式,bundler 模块解析
- **CesiumJS 1.138**(通过 **Resium**) — 3D 地球仪渲染
- **Tailwind CSS v4** — 实用优先的样式,带有自定义战术颜色 token
- **Vite 7** — 带有 HMR、Cesium 插件、API 代理的开发服务器
- **satellite.js** — SGP4/SDP4 卫星轨道推算
### 后端
- **Express 5** — API 代理服务器
- **node-cache** — 带有 TTL 的内存响应缓存
- **WebSocket (ws)** — 实时航班数据推送通道 + 用于 AIS 船舶数据的 AISStream.io 突发 WebSocket
- **dotenv** — 环境变量管理
### 渲染技术
- **命令式 Cesium primitives** — 使用 `BillboardCollection`、`PointPrimitiveCollection`、`PolylineCollection`、`LabelCollection` 实现 27K+ 实体的高性能渲染
- **航位推算** — 在 API 更新之间以 60 fps 推算航空器位置
- **GLSL 后处理** — 通过 `PostProcessStage` 实现 CRT 扫描线、夜视绿色荧光屏、FLIR 热成像调色板
- **CallbackProperty** — 在不触发 React 重新渲染的情况下实现平滑的实体追踪
## 快速开始
### 前置条件
- **Node.js** ≥ 18
- **npm** ≥ 9
### 安装
```
# 克隆 repository
git clone https://github.com/kevtoe/worldview.git
cd worldview
# 安装 dependencies
npm install
```
### 环境配置
复制示例文件并填入你的凭据:
```
cp .env.example .env
cp server/.env.example server/.env
```
然后使用你自己的 API key 编辑每个文件(参见下方的[获取 API Keys](#obtaining-api-keys))。
**`.env`** — 客户端(Vite 在构建时注入 `VITE_*` 变量):
| 变量 | 是否必需? | 用途 |
|---|---|---|
| `VITE_GOOGLE_API_KEY` | 可选 | Google Maps 3D 拟真图块(回退至 OpenStreetMap) |
| `VITE_CESIUM_ION_TOKEN` | 可选 | Cesium Ion 地形/影像服务 |
| `WINDY_API_KEY` | 可选 | Windy 网络摄像头 API(保留,尚未激活) |
| `NSW_TRANSPORT_API_KEY` | 可选 | Transport for NSW 摄像头 |
| `AISSTREAM_API_KEY` | 可选 | AISStream.io 全球 AIS 船舶追踪 |
**`server/.env`** — 服务器端(由 `dotenv` 加载):
| 变量 | 是否必需? | 用途 |
|---|---|---|
| `GOOGLE_MAPS_API_KEY` | 可选 | 服务器端 Google Maps(当前未使用) |
| `OPENSKY_CLIENT_ID` | 可选 | OpenSky Network OAuth2 凭据 |
| `OPENSKY_CLIENT_SECRET` | 可选 | OpenSky Network OAuth2 凭据 |
| `AISSTREAM_API_KEY` | 可选 | AISStream.io 全球 AIS 船舶追踪 |
## 获取 API Keys
### 🗺️ Google Maps API Key(用于 3D 拟真图块)
1. 前往 [Google Cloud Console](https://console.cloud.google.com/)
2. 创建一个新项目(或选择现有项目)
3. 导航至 **APIs & Services → Library**
4. 启用 **Map Tiles API**
5. 前往 **APIs & Services → Credentials → Create Credentials → API Key**
6. **重要提示 — 立即限制你的 key**(参见下方的[保护你的 Google API Key](#securing-your-google-api-key))
7. 将 key 复制到 `.env` 文件中的 `VITE_GOOGLE_API_KEY` 里
### 🛰️ Cesium Ion Token(可选)
1. 在 [cesium.com/ion](https://ion.cesium.com/tokens) 注册一个免费账号
2. 前往 **Access Tokens** → 复制你的默认 token
3. 粘贴到 `.env` 中的 `VITE_CESIUM_ION_TOKEN` 里
### 📹 Transport for NSW API Key(用于澳大利亚 CCTV 摄像头)
1. 在 [opendata.transport.nsw.gov.au](https://opendata.transport.nsw.gov.au/) 注册
2. 前往 **My Applications** → **Create Application**
3. 订阅 **Traffic & Cameras** API
4. 将你的 API key 复制到 `.env` 中的 `NSW_TRANSPORT_API_KEY` 里
### 🚢 AISStream.io API Key(用于海军 / AIS 船舶追踪)
1. 在 [aisstream.io](https://aisstream.io/) 注册一个免费账号
2. 登录 → 导航至你的 Dashboard
3. 生成一个 API key
4. 粘贴到 `.env` 和 `server/.env` 的 `AISSTREAM_API_KEY` 中
### ✈️ OpenSky Network(可选,用于 WebSocket 航班数据)
1. 在 [opensky-network.org](https://opensky-network.org/) 注册
2. 登录 → 前往 **OAuth** → **Create Client**
3. 将 client ID 和 secret 复制到 `server/.env` 中
### 运行应用程序
```
# 启动 backend proxy server(端口 3001)
npm run dev:server
# 在单独的终端中,启动 Vite dev server(端口 5173)
npm run dev
# 或者同时启动两者
npm run dev:all
```
在浏览器中打开 [http://localhost:5173](http://localhost:5173)。
### 生产环境构建
```
npm run build
npm run preview
```
## 部署到 Vercel
本项目通过 `vercel.json` 预配置了 [Vercel](https://vercel.com/)。Express 后端将作为 Vercel Serverless Function 运行。
### 1. 部署你的副本
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fkevtoe%2Fworldview)
模板会导入此代码仓库,通过 `vercel.json` 识别 Vite,并立即部署无需 key 的回退体验。之后可添加可选的 API 凭据以解锁更丰富的数据源。
### 2. 设置环境变量
在你的 Vercel 项目仪表板中:
1. 前往 **Settings → Environment Variables**
2. 添加来自两个 `.env` 文件的每个 key:
| 变量 | 环境 |
|---|---|
| `VITE_GOOGLE_API_KEY` | Production, Preview |
| `VITE_CESIUM_ION_TOKEN` | Production, Preview |
| `WINDY_API_KEY` | Production, Preview |
| `NSW_TRANSPORT_API_KEY` | Production, Preview |
| `GOOGLE_MAPS_API_KEY` | Production, Preview |
| `OPENSKY_CLIENT_ID` | Production, Preview |
| `OPENSKY_CLIENT_SECRET` | Production, Preview |
| `AISSTREAM_API_KEY` | Production, Preview |
## 架构
### 系统架构
```
┌──────────────────────────────────────────────────────────┐
│ Browser (Vite Dev) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ React Hooks │ │ App.tsx │ │ UI Components │ │
│ │ (data fetch) │──│ (state mgr) │──│ OperationsPanel │ │
│ │ │ │ │ │ IntelFeed │ │
│ └──────┬───────┘ └──────┬──────┘ │ StatusBar │ │
│ │ │ │ CCTVPanel │ │
│ │ ┌──────┴──────┐ │ TrackedEntity │ │
│ │ │ GlobeViewer │ └──────────────────┘ │
│ │ │ (Cesium) │ │
│ │ │ ┌─────────┐ │ │
│ │ │ │ Layers │ │ │
│ │ │ │ Flight │ │ │
│ │ │ │ Sats │ │ │
│ │ │ │ Quakes │ │ │
│ │ │ │ Traffic │ │ │
│ │ │ │ CCTV │ │ │
│ │ │ └─────────┘ │ │
│ │ └─────────────┘ │
└─────────┼──────────────────────────────────────────────────┘
│ /api/* proxy
┌─────────▼──────────────────────────────────────────────────┐
│ Express Proxy Server (:3001) │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────────────────┐ │
│ │ node-cache│ │ WebSocket │ │ REST Endpoints │ │
│ │ (TTL) │ │ (ws) │ │ /api/flights │ │
│ └──────────┘ └───────────┘ │ /api/flights/live │ │
│ │ /api/satellites │ │
│ │ /api/earthquakes │ │
│ │ /api/traffic/roads │ │
│ │ /api/ships │ │
│ │ /api/cctv │ │
│ │ /api/cctv/image (proxy) │ │
│ │ /api/health │ │
│ └──────────────────────────┘ │
└─────────────────────────┬──────────────────────────────────┘
│
┌───────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ FlightRadar │ │ USGS │ │ TfL / Austin │ │ AISStream.io │
│ adsb.fi │ │ CelesTrak │ │ NSW Transport│ │ (AIS WSS) │
│ OpenSky │ │ Overpass API│ │ │ │ │
└─────────────┘ └─────────────┘ └──────────────┘ └──────────────┘
```
### 数据流
1. **React hooks** 以特定于图层的间隔轮询 Express 代理服务器
2. **Express** 缓存上游 API 响应,管理 OAuth2 token,并隐藏凭据
3. **App.tsx** 管理全局状态 — 图层可见性、摄像机位置、追踪的实体、着色器模式
4. **图层组件** 通过 props 接收数据,并以命令式方式渲染到 Cesium 场景中
5. **UI 组件** 以 React DOM 覆盖层的形式显示控件、流和状态信息
### 双重航班数据策略
WorldView 融合了两个航空器数据源以实现最佳覆盖:
| 数据源 | 覆盖范围 | 更新频率 | 数据丰富度 |
|---|---|---|---|
| FlightRadar24 | 全球(7 个区域) | 20 秒 | 出发/目的地机场、航空公司 |
| adsb.fi | 区域(250 海里半径) | 5 秒 | 高频位置更新 |
- 缩小视图时:仅使用 FR24 全球数据
- 放大视图时:adsb.fi 替换 FR24 显示附近的航空器(通过 ICAO24 去重)
- 来自 FR24 的航线信息会进行交叉引用,以丰富 adsb.fi 的数据
## 项目结构
```
worldview/
├── server/ # Backend proxy (Node.js ESM)
│ ├── index.js # All routes, WebSocket, AIS burst, caching
│ ├── .env # Server secrets
│ └── data/
│ └── sydneyRoads.js # Static fallback road geometry
├── src/
│ ├── App.tsx # Root component — state, hooks, composition
│ ├── main.tsx # ReactDOM entrypoint
│ ├── index.css # Tailwind v4 + tactical theme + Cesium overrides
│ ├── components/
│ │ ├── globe/
│ │ │ ├── GlobeViewer.tsx # Cesium Viewer (3D tiles, OSM, shader mgmt)
│ │ │ └── EntityClickHandler.tsx # Click-to-track, ESC unlock
│ │ ├── layers/
│ │ │ ├── FlightLayer.tsx # 27K aircraft (imperative, dead-reckoning)
│ │ │ ├── SatelliteLayer.tsx # SGP4 orbit propagation
│ │ │ ├── EarthquakeLayer.tsx # Pulsing seismic markers
│ │ │ ├── TrafficLayer.tsx # Roads + animated vehicles
│ │ │ ├── ShipLayer.tsx # AIS vessels (imperative, dead-reckoning)
│ │ │ └── CCTVLayer.tsx # Camera markers (imperative)
│ │ └── ui/
│ │ ├── OperationsPanel.tsx # Layer/shader/filter controls
│ │ ├── IntelFeed.tsx # Real-time event feed
│ │ ├── CCTVPanel.tsx # Camera grid + preview
│ │ ├── StatusBar.tsx # Coords, clock, data counts
│ │ ├── SplashScreen.tsx # Boot sequence
│ │ ├── TrackedEntityPanel.tsx # Lock-on detail
│ │ └── Crosshair.tsx # SVG targeting reticle
│ ├── data/
│ │ └── airports.ts # IATA → coordinates lookup
│ ├── hooks/
│ │ ├── useFlights.ts # Global FR24 polling
│ │ ├── useFlightsLive.ts # Regional adsb.fi polling
│ │ ├── useSatellites.ts # TLE fetch + SGP4 pipeline
│ │ ├── useEarthquakes.ts # USGS polling
│ │ ├── useTraffic.ts # Road fetch + vehicle simulation
│ │ ├── useShips.ts # AIS vessel polling + burst WebSocket
│ │ └── useCameras.ts # CCTV aggregation
│ ├── shaders/
│ │ └── postprocess.ts # GLSL: CRT, NVG, FLIR
│ └── types/
│ └── camera.ts # CameraFeed, CameraSource types
├── .env # Client-side env vars
├── package.json
├── vite.config.ts # Vite + React + Cesium + Tailwind + proxy
├── tsconfig.json # Project references
├── tsconfig.app.json # Strict TS for src/
└── eslint.config.js
```
## 功能详情
### 🎯 实体追踪
点击地球仪上的任何实体即可将摄像机锁定在其上。视图将以适当的偏移量实时跟随该实体:
- **航空器** — 50 公里 trailing offset 及航向对齐
- **卫星** — 200 公里偏移量,用于轨道观赏
- **地震** — 震中上方 100 公里的俯视图
- **船舶** — 20 公里偏移量,低角度并带有航向对齐
- **CCTV 摄像头** — 2 公里偏移量,45° 视角
按 **ESC** 键解锁追踪,而不移动摄像机。
### 🔭 光学模式(后处理)
| 模式 | 效果 |
|---|---|
| **Standard** | 无后处理 |
| **CRT** | 扫描线、色差、桶形畸变、暗角 |
| **NVG** | 绿色荧光屏、噪点颗粒、泛光、暗角 |
| **FLIR** | 白热热成像调色板、Sobel 边缘检测、高对比度 |
### 🗺️ 地图图块
| 模式 | 描述 |
|---|---|
| **Google 3D** | Google 拟真 3D 图块(需要 API key) |
| **OSM** | OpenStreetMap 2D 影像(无需 key — 默认回退) |
### ✈️ 航班图层详情
- **高度带过滤:** 巡航 (>35K ft)、高 (25–35K)、中 (10–25K低 (1–10K)、地面 (<1K)
- **航线弧:** 出发/目的地机场之间的大圆路径,带有高度曲线
- **航位推算:** 在数据更新之间以 60 fps 平滑进行位置插值
- **颜色编码:** 青色(巡航) → 绿色(高) → 琥珀色(中) → 橙色(低)
### 🛰️ 卫星图层详情
- **SGP4 推算:** 根据 TLE 轨道根数获取实时位置,每 2 秒更新一次
- **轨道路径:** 90 点折线,显示预测 90 分钟的轨迹
- **地面轨迹:** 轨道路径的表面投影
- **天底线:** 从卫星垂直向下指向正下方地面的线
- **ISS 高亮:** 国际空间站采用独特的样式
### 🚢 海军 / AIS 图层详情
- **突发 WebSocket 模式:** 在缓存未命中时连接到 AISStream.io 20 秒,在全球范围内收集 2,000–4,000 艘船舶,然后缓存 60 秒
- **仅移动过滤:** `?moving=1` 排除系泊、抛锚和搁浅的船舶(navStatus 代码)+ SOG < 0.5 节阈值
- **船舶类型分类:** 9 个类别 — 货船(蓝色)、油轮(橙色)、客船(绿色)、渔船(琥珀色)、军舰(红色)、拖船/引航船(紫色)、游艇(青色)、高速船(粉色)、其他(灰色)
- **航向轨迹:** 基于对地航向,在每艘船后方形成的短尾迹折线
- **航位推算:** 在数据更新之间使用 SOG 和 COG 以 60 fps 平滑进行位置插值
- **地球仪遮挡:** 地球背面的船舶会自动隐藏
- **加载指示器:** 在首次突发 WebSocket 完成时显示琥珀色脉冲“LOADING”状态
### 📹 CCTV 系统
- **多源聚合:** 伦敦(TfL JamCams)、奥斯汀(Open Data)、澳大利亚新南威尔士州(Transport API)
- **国家/地区过滤:** 按国家/地区切换摄像头(GB、US、AU)
- **图片代理:** 后端代理摄像头图片以避免 CORS 问题
- **缩略图网格:** 分页摄像头网格(每页 30 个),带有懒加载预览
- **飞至:** 点击任何摄像头即可将地球仪视图锁定到其位置
## API 端点(后端代理)
| 方法 | 端点 | 缓存 TTL | 描述 |
|---|---|---|---|
| `GET` | `/api/flights` | 30 秒 | 全球航空器(FR24 → adsb.fi 回退) |
| `GET` | `/api/flights/live?lat=X&lon=Y&dist=Z` | 4 秒 | 区域高频航空器(adsb.fi) |
| `GET` | `/api/satellites?group=stations` | 2 小时 | TLE 文本数据(3 行格式) |
| `GET` | `/api/earthquakes` | 60 秒 | USGS GeoJSON 订阅流(过去 24 小时) |
| `GET` | `/api/traffic/roads?south=X&west=Y&north=Z&east=W` | 24 小时 | 来自 Overpass API 的道路网络 |
| `GET` | `/api/ships?moving=1` | 60 秒(20 秒突发) | 通过 AISStream.io 突发 WebSocket 获取全球 AIS 船舶位置 |
| `GET` | `/api/cctv?country=XX&source=YY` | 5 分钟 | 聚合的 CCTV 摄像头流 |
| `GET` | `/api/cctv/image?url=ENCODED_URL` | 60 秒 | CORS 图片代理 |
| `GET` | `/api/health` | — | 服务器健康状态 + 缓存统计 |
| `WS` | `/ws` | — | 实时航班推送(通过 JSON 订阅) |
## 设计系统
### 调色板
| Token | Hex | 用法 |
|---|---|---|
| `wv-black` | `#0A0A0A` | 背景 |
| `wv-dark` | `#111111` | 面板背景 |
| `wv-panel` | `#1A1A1A` | 凸起的表面 |
| `wv-border` | `#2A2A2A` | 边框、分隔线 |
| `wv-muted` | `#666666` | 禁用/次要文本 |
| `wv-text` | `#CCCCCC` | 主要文本 |
| `wv-cyan` | `#00D4FF` | 主要强调色、航班 |
| `wv-green` | `#39FF14` | 卫星、成功状态 |
| `wv-amber` | `#FF9500` | 警告、地震 |
| `wv-red` | `#FF3B30` | 错误、CCTV、警报 |
| `wv-teal` | `#00BFA5` | 次要强调色 |
### 字体
等宽字体栈:`JetBrains Mono`、`Fira Code`、`SF Mono`、`monospace`
### UI 效果
- **面板玻璃效果** — `backdrop-blur(12px)` 搭配 85% 黑色背景
- **扫描线覆盖层** — 8 秒钟从上到下的动态扫描
- **辉光类** — `.glow-cyan`、`.glow-green`、`.glow-amber` 文本阴影效果
## 开发
### 脚本
| 命令 | 描述 |
|---|---|
| `npm run dev` | 启动 Vite 开发服务器(端口 5173) |
| `npm run dev:server` | 启动 Express 代理(端口 3001) |
| `npm run dev:all` | 并发启动两个服务器 |
| `npm run build` | TypeScript 编译 + Vite 生产环境构建 |
| `npm run lint` | 对所有 `.ts`/`.tsx` 文件运行 ESLint |
| `npm run preview` | 在本地提供生产环境构建的服务 |
### 添加新的数据图层
1. 在 `src/hooks/` 中**创建一个 hook** — 从后端获取数据,返回类型化状态
2. 在 `src/components/layers/` 中**创建一个图层组件** — 渲染 Cesium primitives
3. 在 `server/index.js` 中**添加一个代理端点** — 缓存上游 API,隐藏凭据
4. **接入 App.tsx** — 添加图层切换状态,调用 hook,将数据传递给图层组件
5. **更新 OperationsPanel** — 为新图层添加切换控件
6. **更新 StatusBar** — 添加实体计数显示
### 性能指南
- 对于包含 >100 个实体的图层,请使用**命令式 Cesium primitives**(`BillboardCollection`、`PointPrimitiveCollection`)
- 避免在循环中为大型数据集创建 **Resium `
`** 元素
- 大量使用 `useCallback` 和 `useMemo` — Cesium 渲染循环对引用变化很敏感
- 对于 Cesium 实体位置,优先使用 `CallbackProperty` 而非 React state
- 为移动的实体实现**航位推算**,以在数据更新之间保持 60 fps
## 故障排除
| 问题 | 解决方案 |
|---|---|
| 空白的地球仪 / 无图块 | 检查 `VITE_GOOGLE_API_KEY` 是否有效且启用了 Maps JavaScript API;应用程序会自动回退到 OSM |
| 无航班/卫星/地震数据 | 确保后端代理正在运行(`npm run dev:server`) |
| CCTV 图片不加载 | 后端必须正在运行才能通过 `/api/cctv/image` 代理图片 |
| 控制台出现 "429 Too Many Requests" | 触发上游 API 速率限制;缓存层降低了频率,请等待 TTL 到期 |
| Overpass API 超时 | 交通图层回退到静态的悉尼 CBD 道路数据 |
| 卫星未显示 | TLE API 可能暂时宕机;CelesTrak 被用作自动回退 |
| 船舶未加载 / 图层为空 | 确保在 `server/.env` 中设置了 `AISSTREAM_API_KEY`;在突发 WebSocket 收集数据期间,初始加载大约需要 20 秒 |
| 只能看到少数船舶 | 通过移除 `?moving=1` 关闭仅移动过滤器;某些地区的 AIS 覆盖较少 |
| Google 3D 图块错误 | API key 可能无效或配额已超;系统会自动应用 OSM |
## 致谢
### 数据来源
- [FlightRadar24](https://www.flightradar24.com/) — 全球航班追踪
- [adsb.fi](https://adsb.fi/) — 开放 ADS-B 航空器数据
- [OpenSky Network](https://opensky-network.org/) — 开放航空器监视数据
- [USGS Earthquake Hazards](https://earthquake.usgs.gov/) — 实时地震流
- [CelesTrak](https://celestrak.org/) — 卫星 TLE 轨道数据
- [TLE API](https://tle.ivanstanojevic.me/) — 卫星 TLE 数据服务
- [OpenStreetMap / Overpass API](https://overpass-api.de/) — 道路网络数据
- [Transport for London](https://api.tfl.gov.uk/) — 伦敦交通摄像头
- [City of Austin Open Data](https://data.austintexas.gov/) — 奥斯汀交通摄像头
- [Transport for NSW](https://opendata.transport.nsw.gov.au/) — 新南威尔士州交通摄像头
- [AISStream.io](https://aisstream.io/) — 通过 WebSocket 进行全球 AIS 船舶追踪
### 技术
- [CesiumJS](https://cesium.com/) + [Resium](https://resium.reearth.io/) — 3D 地球仪渲染
- [satellite.js](https://github.com/shashwatak/satellite-js) — SGP4/SDP4 轨道推算
- [Turf.js](https://turfjs.org/) — 地理空间分析实用工具
## 安全
所有敏感值均从 `.env` 文件中加载,这些文件通过 `.gitignore` 排除。如果你 fork 或 clone 此代码仓库,你必须提供自己的 API key。
如果你发现凭据泄露或安全问题,请立即提 issue。
### 推送前的快速检查清单
- [ ] `.env` 和 `server/.env` 已在 `.gitignore` 中(默认已包含)
- [ ] 源代码文件中没有硬编码 API key
- [ ] Google API key 已应用 HTTP referrer 限制
- [ ] Google API key 仅限于 Map Tiles API
- [ ] 已在 Google Cloud Console 中配置预算警报
## 许可证
基于 [MIT 许可证](LICENSE) 发布 — 可免费使用、修改、fork 并在此基础上进行构建,包括商业用途。
### 第三方数据和 API 条款
MIT 许可证涵盖**本项目自身的源代码**。它没有也无法对本应用程序调用的外部服务授予任何权利。在[致谢](#acknowledgements)下列出的每一个数据源 — Google 拟真 3D 图块、FlightRadar24、adsb.fi、OpenSky、CelesTrak、USGS、Overpass、TfL、Transport NSW、AISStream 及其他 — 都有其各自的服务条款、许可和速率限制。其中几项限制商业或高频大量使用。**在部署超出个人或演示用途的任何内容之前,请检查你启用的每个提供商的条款。**
**你有责任保护自己的 API key 并管理自己的 API 使用费用。** 对于因配置错误或不受限制的 API 凭据产生的费用,作者不承担任何责任。