SpecEnvoy
EN·中文

战略

瞭望台数据 API

无密钥。无配额。无需注册。数据集就是放在 CDN 上的静态 JSON —— 因此它比计量式 API 更快,我们不可能向你收费,别人也不可能把它打垮。

为什么没有 x-api-key

这类产品的常见做法是计量式 REST API:发放密钥、每天几百次请求、按月收费。我们刻意没有这么做,原因有两个。

那对你更差。我们的数据按周变化,而非按请求变化。查询型接口只会给你一个速率上限和一个需要轮换的密钥,换来的东西你直接取一个文件就能得到。

那对我们也更差。在免费服务上放一个按计算量计费的接口,等于主动招来账单。放在 Cloudflare CDN 上的静态文件对我们而言每次请求都是零成本,而且不可能被人做成有成本 —— 所以我们可以坦然地完全开放,而不必为了自保而限制你。

如果你确实需要服务端查询 —— 边界框、增量时间戳、条件筛选 —— 告个知,我们会评估。那需要独立的 worker、按 IP 限流和硬性成本上限;不是能挂在这上面的东西。

接口

所有路径相对于 https://www.specenvoy.com。全部为 GET,且开放 CORS(Access-Control-Allow-Origin: *),浏览器可直接 fetch()

  • /data/vantage/data/meta.json —— 从这里开始。构建日期、schema 版本、每个图层的记录数、精度构成与主要国家,以及带许可信息的完整来源清单。
  • /data/vantage/data/layers/<layerId>.json —— 单个图层的记录。图层 id:nppicbmspaceradarcmdnavalbaseairbasegarrisonmilbasedampowerfaboilgasportchoke
  • /data/vantage/data/base/{land,lakes,places,admin1}-<lod>.json —— 底图,源自 Natural Earth,公有领域。坐标为增量编码的整数;累加后除以 q。LOD 0/1/2 分别为粗/中/细。
  • /vantage/live/mil —— 军机实时 ADS-B,约每两分钟刷新。尽力而为,无 SLA,也是我们保留随时关闭权利的唯一接口。上游中断时返回 {"stale":true} 而不是报错。

版本约定

meta.json 带有 "schema": 1。增量变更 —— 新字段、新图层、新记录 —— 会在 schema 1 内直接发生,不另行通知。

破坏性变更会以新文件名发布(layers/npp-v2.json)并提升 schema;旧文件继续提供服务。所以不必锁定版本,读取 schema 即可,我们不会把你弄坏。

记录的 id 是永久的。自动采集记录的 id 形如 <layer>:q<wikidata-qid>,正是为了在上游改名后依然有效。

许可 —— 按文件区分,且并不相同

每个文件在 licence 字段中标明自身许可。请查看该字段;不能把整个数据集当作同一种许可对待。

  • 底图 —— 源自 Natural Earth,公有领域。无附加条件。
  • 图层文件 —— 许可并不统一,请逐条查看记录的 src。来自 Wikidata 的记录为 CC0。状态、装机容量或年份来自 Global Energy Monitor 的记录为 CC BY 4.0再分发时必须署名 GEM —— 约 5,500 条,分布于 nppdampower 图层,每条的 src 中均注明所用追踪数据集与版本。人工核实的记录与全部撰写的说明文字属本站,署名 SpecEnvoy 后可复用。所引用的政府来源(EIA、美国国防部、CASI)属美国公有领域。
  • 实时 ADS-B —— ODbL 1.0,上游为 adsb.lol 贡献者。相同方式共享条款适用于派生数据库,因此它位于独立接口,绝不并入图层文件。署名信息随载荷及 X-Data-Licence 响应头一同提供。

建议引用格式:Vantage, SpecEnvoy —— 获取于 <日期>, https://www.specenvoy.com/data/vantage/,并附上你所用文件对应的上游致谢。

值得理解的字段

  • prec —— exactsiteareaapprox。位置精度。若不按此字段筛选就直接聚合,得出的数字含义会低于你的预期。
  • sg —— 0–100 的重要度百分位,仅在本图层内部计算。地图据此决定低缩放级别下绘制哪些点;人工整理条目固定为 100。请勿跨图层比较 sg:水坝与军用机场的排序基于完全不同的总体,跨层比较没有意义。
  • 图层文件中的 counttotal —— count 为对外计数,不含 approx 记录total 为文件实际包含的全部记录。meta.json 中对应字段为 nall,并另有 countries 索引,按 ISO 3166-1 二位国家代码给出跨全部图层的对外计数。
  • tier —— A 人工核实,B 机器采集。
  • ra —— 为 1 时,说明文字由所引用的结构化字段生成,而非人工撰写。
  • weak —— 为 1 时,仅有检索型来源;尚缺权威原始出处。
  • src —— 必定存在,至少一条,且必定带 URL 与日期。
  • conflict —— 当公开来源存在分歧时出现,并附说明。我们记录分歧,而不是替读者选一个答案。
  • ll —— [经度, 纬度],WGS84。经度在前。

示例代码

以下代码可直接运行。无需密钥、无需注册、无需构建步骤。

1 —— 先读清单,只重新获取有变化的部分。这是我们唯一的请求:built 字段会告诉你某个图层自上次查看后是否更新。图层按周更新,因此高于每日一次的频率对双方都只是浪费流量。

const meta = await (await fetch(
  "https://www.specenvoy.com/data/vantage/data/meta.json"
)).json();

console.log(meta.total, "条记录,共", Object.keys(meta.layers).length, "个图层");
console.log(meta.layers.npp);
// { n: 368, tierA: 0, weak: 368, prec: { site: 368 }, top: ["US:94","CN:32", ...] }

2 —— 取单个图层并按国家筛选。每条记录都带 cc(ISO 3166-1 alpha-2)。

const npp = await (await fetch(
  "https://www.specenvoy.com/data/vantage/data/layers/npp.json"
)).json();

const chinese = npp.records.filter(r => r.cc === "CN");
console.log(chinese.map(r => `${r.zn || r.n} —— ${r.cap?.mw ?? "?"} MW`));

3 —— 聚合之前先按精度筛选。若跳过这一步,得出的数字含义会低于你的预期:approx 记录是依据一句描述标出的点,误差 ±50 公里。我们不会把它计入任何对外公布的统计,你也不应该。

const solid = npp.records.filter(r => r.prec === "exact" || r.prec === "site");
const hand  = npp.records.filter(r => r.tier === "A");   // 人工核实
const owed  = npp.records.filter(r => r.weak);           // 仅有检索型来源

4 —— 直接画到地图上。ll 的顺序是 [经度, 纬度] —— 经度在前,与 GeoJSON 一致。多数地图库要求纬度在前,这是最容易出错的一处。

const geojson = {
  type: "FeatureCollection",
  features: npp.records.map(r => ({
    type: "Feature",
    geometry: { type: "Point", coordinates: r.ll },   // 已是 [经度, 纬度]
    properties: { name: r.n, name_zh: r.zn, status: r.status, precision: r.prec },
  })),
};
// Leaflet 需要 [纬度, 经度]:L.marker([r.ll[1], r.ll[0]])

5 —— 也可以用 Python。

import requests, pandas as pd

B = "https://www.specenvoy.com/data/vantage/data"
dams = requests.get(f"{B}/layers/dam.json").json()["records"]
df = pd.DataFrame(dams)
df[["lon", "lat"]] = pd.DataFrame(df["ll"].tolist(), index=df.index)

# 坝高(米)位于 cap.h
df["height_m"] = df["cap"].apply(lambda c: (c or {}).get("h"))
print(df.nlargest(10, "height_m")[["n", "cc", "height_m"]])

6 —— 实时数据,以及如何诚实地解读它。尽力而为、无 SLA,也是我们保留随时关闭权利的唯一接口。上游不可用时它返回 HTTP 200 并带 {"stale": true},而不是报错 —— 请检查该标志,而不是状态码。

const live = await (await fetch("https://www.specenvoy.com/vantage/live/mil")).json();

if (!live.ok || live.stale) {
  console.warn("数据降级:", live.detail || "上游不可用");
} else {
  console.log("当前正在发射信号的军用航空器:", live.total, "架");
}
// live.caveat 中的说明,请一并传递给你自己的用户。

请务必保留该说明。ADS-B 只显示选择发射信号的航空器;军机是否发射由其自行决定,且欧洲与北美以外的接收站覆盖稀疏。把它当作完整图景呈现,是你能用这些数据做的唯一真正有害的事。

署名。请查看每个文件的 licence 字段 —— 它们确实不同。Vantage, SpecEnvoy —— 获取于 <日期>, https://www.specenvoy.com/data/vantage/,并附上你所用文件对应的上游致谢(Natural Earth、Wikidata、Global Energy Monitor、EIA、adsb.lol)。

合理使用,以及我们唯一的请求

随意取用 —— CDN 就是为此而生。若你在轮询,请先读 meta.json,只重新获取 built 日期发生变化的图层;图层按周更新,因此高于每日一次的频率对双方都只是浪费流量。

请不要把实时数据源大规模转发给你自己的用户 —— 请直接访问 adsb.lol,更好的做法是为他们供数。这套接收站网络是这一切的基础。

另外,请把实时载荷中附带的说明一并传递下去。ADS-B 只显示正在发射信号的航空器;军机是否发射由其自行决定。把这些数据当作完整图景转述出去,是你能用它做的唯一真正有害的事。