16c62a97d5
- 新增时标、ΔT 模型、质心时间与 UT1 支持 - 改进日月食、月掩、行星事件及路径边界计算 - 完善恒星三维自行与动态距离传播 - 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具 - 整理中英文手册、示例资源及回归测试
486 lines
25 KiB
Markdown
486 lines
25 KiB
Markdown
# 天象地图、GeoJSON 与 KML
|
||
|
||
[English](en/map-geojson.md) | [返回 README](../../README.md)
|
||
|
||
> 本手册的完整示例以仓库根目录为工作目录执行,生成的图片写入 `doc/img/`。
|
||
|
||
## 目录
|
||
|
||
- [导出日食 GeoJSON](#导出日食-geojson)
|
||
- [API 参考](#api-参考)
|
||
- [常用场景](#常用场景)
|
||
- [给事件选投影](#给事件选投影)
|
||
- [导出 GeoJSON 与时间标记](#导出-geojson-与时间标记)
|
||
- [时标声明与 UT1](#时标声明与-ut1)
|
||
- [单时刻足迹与图层词表](#单时刻足迹与图层词表)
|
||
- [地图投影](#地图投影)
|
||
- [时标声明](#时标声明)
|
||
- [GeoJSON](#geojson)
|
||
- [图层筛选](#图层筛选)
|
||
- [坐标、时间与反经线](#坐标时间与反经线)
|
||
- [按时刻计算日食阴影](#按时刻计算日食阴影)
|
||
- [地平线闭合与插值](#地平线闭合与插值)
|
||
- [ΔT 与地面位置](#δt-与地面位置)
|
||
- [月掩瞬时足迹](#月掩瞬时足迹)
|
||
- [先查事件再计算几何](#先查事件再计算几何)
|
||
- [KML](#kml)
|
||
- [转换文件](#转换文件)
|
||
- [Options](#options)
|
||
- [图层与样式](#图层与样式)
|
||
- [时间轴与静态叠加](#时间轴与静态叠加)
|
||
- [控制文件大小和取景](#控制文件大小和取景)
|
||
- [属性与输入校验](#属性与输入校验)
|
||
|
||
## 导出日食 GeoJSON
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"time"
|
||
|
||
"b612.me/astro/eclipse"
|
||
"b612.me/astro/geojson"
|
||
)
|
||
|
||
func main() {
|
||
cst := time.FixedZone("CST", 8*3600)
|
||
date := time.Date(2009, 7, 22, 0, 0, 0, 0, cst)
|
||
partial, ok := eclipse.SolarEclipsePartialFootprints(date,
|
||
eclipse.SolarEclipsePartialFootprintOptions{Step: 10 * time.Minute, BoundaryPoints: 180})
|
||
if !ok {
|
||
log.Fatal("no solar eclipse")
|
||
}
|
||
path, ok := eclipse.SolarEclipseCentralPath(date,
|
||
eclipse.SolarEclipsePathOptions{Step: time.Minute, TargetSpacingKM: 20})
|
||
var centralPath *eclipse.SolarEclipsePath
|
||
if ok {
|
||
centralPath = &path
|
||
}
|
||
data, err := geojson.MarshalSolarEclipseWithTimeMarkers(partial, centralPath,
|
||
geojson.TimeMarkerOptions{Step: 30 * time.Minute, Location: cst})
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
fmt.Println(string(data))
|
||
}
|
||
```
|
||
|
||
偏食可以没有中心路径,因此 `centralPath` 允许为 `nil`。本例把 GeoJSON 写到标准输出,可重定向到文件;SVG 与 KML 用法见后文。
|
||
|
||
## API 参考
|
||
|
||
SVG 片段使用导入别名 `eclipsesvg "b612.me/astro/eclipse/svg"`;月掩图使用 `moonsvg "b612.me/astro/moon/svg"`。日期和时区沿用首例。
|
||
|
||
| 名称 | 用途 | 备注 |
|
||
| --- | --- | --- |
|
||
| `eclipsesvg.EclipseMapProjectionAuto` / `...Equirectangular` / `...NorthPolar` / `...SouthPolar` / `...Orthographic` | 日月食地图投影 | 零值即自动 |
|
||
| `moonsvg.MapProjectionAuto` / `...Equirectangular` / `...NorthPolar` / `...SouthPolar` / `...Orthographic` | 月掩地图投影 | 同上 |
|
||
| `SolarEclipseMapSVG` / `LunarEclipseMapSVG` | 日食、月食全球图 | 返回 `(string, bool)` |
|
||
| `StarOccultationPathSVG` / `PlanetOccultationPathSVG` | 月掩全球掩带图 | 返回 `(string, error)` |
|
||
| `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers` | 日食 GeoJSON | 无/带时间标记 |
|
||
| `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers` / `MarshalLunarEclipseWithOptions` | 月食 GeoJSON | 同上 |
|
||
| `MarshalStarOccultation` / `MarshalPlanetOccultation`(及 `...WithTimeMarkers`) | 月掩 GeoJSON | 同上 |
|
||
| `NewSolarEclipseShadowSolver` / `MarshalSolarEclipseShadowInstant` | 单时刻足迹及其 GeoJSON | 拖动时间轴用 |
|
||
| `TimeMarkerOptions` | 时间标记 `Step`、`Location`、`TimeScale` | `Step` 零值 30 分钟,最多 1440 个 |
|
||
| `astro.TimeScaleUT1` / `astro.DUT1` | UT1 口径与 DUT1 差值 | UT1 时 `Location` 必须为 UTC |
|
||
| `kml.FromGeoJSON` | 把 GeoJSON 转成 KML 2.2 | 只依赖标准库;按 `role` 分层并给默认调色板 |
|
||
|
||
## 常用场景
|
||
|
||
### 给事件选投影
|
||
|
||
```go
|
||
for _, spec := range []struct {
|
||
name string
|
||
p eclipsesvg.EclipseMapProjection
|
||
}{
|
||
{"equirectangular", eclipsesvg.EclipseMapProjectionEquirectangular},
|
||
{"north-polar", eclipsesvg.EclipseMapProjectionNorthPolar},
|
||
{"south-polar", eclipsesvg.EclipseMapProjectionSouthPolar},
|
||
{"orthographic", eclipsesvg.EclipseMapProjectionOrthographic},
|
||
} {
|
||
svg, ok := eclipsesvg.SolarEclipseMapSVG(date, eclipsesvg.SolarEclipseMapSVGOptions{
|
||
Width: 1200, Height: 800, Location: cst, Projection: spec.p,
|
||
})
|
||
fmt.Println(spec.name, ok, len(svg))
|
||
}
|
||
```
|
||
|
||
```text
|
||
equirectangular true 265827
|
||
north-polar true 181602
|
||
south-polar true 91902
|
||
orthographic true 537001
|
||
```
|
||
|
||
投影只影响 SVG 表达、不改变底层 WGS84 地理结果;`Auto`(零值)按事件自动选极图,需要固定版式时才显式指定。正射球面视点取食甚点、只画朝向视点的半球,并切换成 NASA 摆法版式——代价与底图反解过程见[地图投影](#地图投影)。
|
||
|
||
### 导出 GeoJSON 与时间标记
|
||
|
||
```go
|
||
data, err := geojson.MarshalSolarEclipseWithTimeMarkers(partial, centralPath,
|
||
geojson.TimeMarkerOptions{Step: 30 * time.Minute, Location: cst})
|
||
fmt.Println(err, json.Valid(data), len(data))
|
||
```
|
||
|
||
```text
|
||
<nil> true 426253
|
||
```
|
||
|
||
- `WithTimeMarkers` 会追加 `role=time-marker` 的 Point 要素:`label` 按 `Location` 本地化显示,`time` 始终是 UTC RFC 3339。
|
||
- `Step` 零值为 30 分钟,正值至少 1 分钟,单次导出最多 1440 个标记;不需要标记就用不带后缀的 `MarshalSolarEclipse`。
|
||
- 月食与月掩有对称入口(`MarshalLunarEclipse*`、`MarshalStarOccultation*`、`MarshalPlanetOccultation*`);GeoJSON 不携带底图、边界、样式或投影,Web Mercator 与瓦片选择由应用决定。
|
||
|
||
### 时标声明与 UT1
|
||
|
||
```go
|
||
fmt.Println(astro.DUT1(date)) // UT1−UTC,单位秒
|
||
```
|
||
|
||
```text
|
||
0.23
|
||
```
|
||
|
||
出图默认按 UTC 口径并在图上或图注声明;`TimeScale: astro.TimeScaleUT1` 改成 UT1 读数并在图里写出 `DUT1 = UT1−UTC = +0.23 s`,此时 `Location` 必须是 UTC。GeoJSON 的对应成员是 `time_scale`(UTC 口径省略,UT1 口径写 `"UT1"`),完整约定见[时标声明](#时标声明)。
|
||
|
||
### 单时刻足迹与图层词表
|
||
|
||
```go
|
||
solver := eclipse.NewSolarEclipseShadowSolver(eclipse.SolarEclipseShadowSolverOptions{})
|
||
instant, ok := solver.ShadowAt(date)
|
||
shadow, err := geojson.MarshalSolarEclipseShadowInstant(instant)
|
||
fmt.Println(ok, err, json.Valid(shadow), len(shadow))
|
||
```
|
||
|
||
```text
|
||
true <nil> true 4217
|
||
```
|
||
|
||
- 单时刻接口只算"这一瞬间的本影/半影足迹"与站心日月几何,不产生可见带、食分线、升落边界或南北界;本影不在地球上时返回空值而不是错误。
|
||
- 插值前先比 `interp_signature`:只有相同的相邻时刻才适合按顶点插值,`closed` 翻转、段数/顶点数变化、空↔非空(U1/U4 附近)都必须改取精确几何。
|
||
- 可降级图层用 `data-source` 标注实际几何来源,取值词表见[地图投影](#地图投影)。
|
||
|
||
## 地图投影
|
||
|
||
日食、月食与月掩的 SVG 共用同一套投影:等经纬、北极方位等距、南极方位等距与正射球面。投影只影响 SVG 表达,不改变底层 WGS84 地理结果;日食与月掩的自动投影(零值 `...Auto`)会在适合时选择极图,月食默认使用等经纬投影。
|
||
|
||
| 投影 | 常量(日食/月食 · 月掩) | 适用场景 | 画布建议 |
|
||
| --- | --- | --- | --- |
|
||
| 自动 | `EclipseMapProjectionAuto` · `MapProjectionAuto` | 默认;按事件自动选极图 | 1200×800 |
|
||
| 等经纬 | `...Equirectangular` | 跨反经线的长食带或掩带 | 1200×800、1414×1000 |
|
||
| 北极/南极方位等距 | `...NorthPolar` / `...SouthPolar` | 事件整体落在高纬 | 1200×800、1000×1414 |
|
||
| 正射球面 | `...Orthographic` | NASA 版式的半球图,视点取事件中心 | 1000×1414 |
|
||
|
||
`EclipseMapProjectionOrthographic` / `MapProjectionOrthographic` 以食甚点(月掩取事件中心)为视点,只显示朝向视点的半球。该投影使用 NASA 风格的居中球面版式,建议画布为 `1000×1414`。
|
||
|
||
同一事件批量出四种投影:
|
||
|
||
```go
|
||
date := time.Date(2009, 7, 22, 12, 0, 0, 0, cst)
|
||
for _, spec := range []struct {
|
||
name string
|
||
p eclipsesvg.EclipseMapProjection
|
||
}{
|
||
{"equirectangular", eclipsesvg.EclipseMapProjectionEquirectangular},
|
||
{"north-polar", eclipsesvg.EclipseMapProjectionNorthPolar},
|
||
{"south-polar", eclipsesvg.EclipseMapProjectionSouthPolar},
|
||
{"orthographic", eclipsesvg.EclipseMapProjectionOrthographic},
|
||
} {
|
||
options := eclipsesvg.SolarEclipseMapSVGOptions{
|
||
Width: 1200, Height: 800, Location: cst, Projection: spec.p,
|
||
}
|
||
svg, ok := eclipsesvg.SolarEclipseMapSVG(date, options)
|
||
if !ok {
|
||
continue
|
||
}
|
||
_ = os.WriteFile("solar-eclipse-"+spec.name+".svg", []byte(svg), 0o644)
|
||
}
|
||
```
|
||
|
||
各族的选项、图层开关与带图示例见[日食与月食手册](eclipse.md#全球见食图与月食出图)与[月掩手册](occultation.md#月掩出图);GeoJSON 侧只携带地理结果,投影由应用自行选择。
|
||
|
||
可降级的图层用 `data-source` 标注实际几何来源,取值词表见 `eclipse/svg` 包注释:`partial-band-union`、`sampled-footprint-sweep`、`partial-band-contours`、`rise-set-phase-lines`、`magnitude-contours`、`greatest-time-isochrones`、`besselian-critical-envelope`、`paired-limit-chords`、`sampled-open-sweep`、`central-path-limits`、`penumbral-outlines`、`central-shadow-outlines`、`p1-p4-visibility-regions`、`p1-p4-horizon-boundaries`。
|
||
|
||
## 时标声明
|
||
|
||
图内时刻的口径由 `TimeScale` 选项决定,日食全球图、月食全球图、月食详细版式与月掩三族图都会在图上或图注里写明:
|
||
|
||
- 默认(`astro.TimeScaleUTC`,零值):写 `图中时刻为 UTC`;展示时区不是 UTC 时写 `图中时刻为 UTC(显示时区 CST,UTC+08:00)`,把时标与展示时区分开声明。
|
||
- `astro.TimeScaleUT1`:写 `图中时刻为 UT1(世界时),DUT1 = UT1−UTC = +0.05 s。`,差值随事件日期变化;此时 `Location` 必须是 UTC,否则渲染返回 `false`(月掩侧直接返回错误)。
|
||
|
||
GeoJSON 侧的对应成员是 `time_scale`:UTC 口径省略该成员,UT1 口径写 `"UT1"`,表示所有 `time` 字符串与 `HH:MM` 标注都是 UT1 读数(RFC 3339 的 `Z` 后缀严格说不等于 UT1)。切时标不改掩带、足迹、地平线与等值线几何,只有写出的时刻文字换成 UT1 读数;时间标记按输出时标的整点取点(整点读数本身就是另一个物理时刻),位置随所标时刻移动。
|
||
|
||
## GeoJSON
|
||
|
||
`geojson` 接收已经计算好的日食、月食或月掩结果,返回 `[]byte`。这段字节是完整的 UTF-8 RFC 7946 `FeatureCollection` JSON,不是图片,也不是压缩数据,可以直接写入 `.geojson`、交给 `encoding/json`,或发送给前端地图组件。
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"time"
|
||
|
||
"b612.me/astro/eclipse"
|
||
"b612.me/astro/geojson"
|
||
)
|
||
|
||
func main() {
|
||
date := time.Date(2024, 4, 8, 0, 0, 0, 0, time.UTC)
|
||
partial, ok := eclipse.SolarEclipsePartialFootprints(
|
||
date,
|
||
eclipse.SolarEclipsePartialFootprintOptions{
|
||
Step: 10 * time.Minute, BoundaryPoints: 180,
|
||
},
|
||
)
|
||
if !ok {
|
||
return
|
||
}
|
||
central, hasCentral := eclipse.SolarEclipseCentralPath(
|
||
date,
|
||
eclipse.SolarEclipsePathOptions{Step: time.Minute, TargetSpacingKM: 20},
|
||
)
|
||
var centralPath *eclipse.SolarEclipsePath
|
||
if hasCentral {
|
||
centralPath = ¢ral
|
||
}
|
||
|
||
data, err := geojson.MarshalSolarEclipseWithTimeMarkers(
|
||
partial, centralPath,
|
||
geojson.TimeMarkerOptions{
|
||
Step: 30 * time.Minute,
|
||
Location: time.FixedZone("CST", 8*3600),
|
||
},
|
||
)
|
||
fmt.Println(err, json.Valid(data))
|
||
}
|
||
```
|
||
|
||
对应的无时间标记和带时间标记入口包括:
|
||
|
||
- `MarshalSolarEclipse` / `MarshalSolarEclipseWithTimeMarkers`
|
||
- `MarshalLunarEclipse` / `MarshalLunarEclipseWithTimeMarkers`
|
||
- `MarshalLunarEclipseWithOptions`:用 `LunarEclipseOptions` 一次给出时间标记、`SkipRoles` 与时间包络的采样档位。`SkipRoles` 可跳过时间包络和仅见半影带;跳过两个时间包络时不会执行包络采样。`EnvelopeSweepSamples` 默认 48,限制在 `[2, 192]`;`EnvelopeLongitudePoints` 默认 `max(360, boundaryPoints)`,限制在 `[12, 720]`。
|
||
- `MarshalStarOccultation` / `MarshalStarOccultationWithTimeMarkers`
|
||
- `MarshalPlanetOccultation` / `MarshalPlanetOccultationWithTimeMarkers`
|
||
- `MarshalSolarEclipseWithOptions`:用 `SolarEclipseOptions` 一次给出时间标记与 `SkipRoles`,等价于上面的日食入口再加图层裁剪。`SkipRoles` 里最常用的是 `partial-footprint`(瞬时半影轮廓);`partial-band` 是日食带边缘(食分 0 界限),通常要留着。
|
||
|
||
### 图层筛选
|
||
|
||
`SolarEclipseOptions.SkipRoles` 按 `role` 排除输出要素,零值保留全部图层。可通过 `MarshalSolarEclipseWithOptions` 同时指定时间标记与筛选条件。
|
||
|
||
筛选发生在编码阶段,几何仍按完整采样计算。因此,跳过 `partial-footprint` 会去掉逐时刻的半影足迹,但不会降低总偏食区边缘 `partial-band` 的精度,也不影响中心带、食分线和升落边界。全部要素被排除时返回错误。
|
||
|
||
### 坐标、时间与反经线
|
||
|
||
坐标为 WGS84 `[经度, 纬度]`,单位度。跨反经线的线和面会拆分,并在接缝两侧补出同一个交点。触及极点的环用 `[±180, ±90]` 两个坐标表示同一极点。
|
||
|
||
闭合环含有用于闭合的 ±180° 接缝段。填充时需要这些边;描绘真实边界时应跳过接缝。`band-outline`、`total-band-outline` 等闭合轮廓线也需按此规则处理。
|
||
|
||
带时间路径的 `times` 与各段坐标逐点对应。`WithTimeMarkers` 额外加入 `role=time-marker` 的 Point 要素:`label` 用于显示,默认 `time` 为 UTC RFC 3339;显式选择 UT1 时由 `time_scale` 区分。详见[时标](timescale.md)。
|
||
|
||
| `TimeMarkerOptions` 字段 | 行为 |
|
||
| --- | --- |
|
||
| `Step` | 零值为 30 分钟,正值至少 1 分钟;一次导出最多 1440 个标记 |
|
||
| `Location` | 标签的显示时区,`nil` 使用 UTC |
|
||
| `TimeScale` | 默认 UTC;UT1 要求 `Location` 为 `nil` 或 `time.UTC` |
|
||
|
||
GeoJSON 不携带底图、样式或投影,应用可自行选择瓦片、Web Mercator 或极区投影。
|
||
|
||
### 按时刻计算日食阴影
|
||
|
||
交互地图按时间查询阴影时,可复用 `eclipse.NewSolarEclipseShadowSolver` 返回的求解器:
|
||
|
||
| 方法 | 输入与结果 |
|
||
| --- | --- |
|
||
| `ShadowAt(date)` | 民用 `time.Time`,返回瞬时全球阴影足迹 |
|
||
| `ShadowAtJDE(jdeTT)` | TT 儒略日,返回相同类型的足迹 |
|
||
| `StationStateAt` / `StationStateAtJDE` | 指定时刻与站点的食分、遮蔽率、站心角距、视半径、太阳高度/方位与全食/环食状态 |
|
||
| `ShadowBetween(start, end, step)` | 按步长返回足迹序列,无阴影的时刻保留空条目 |
|
||
| `StationStatesBetween` | 按时间采样站点状态 |
|
||
|
||
默认计算本影或反本影;`Kind: SolarEclipseShadowPenumbra` 改为半影。单时刻接口不计算整场事件的可见带、食分线、南北限或中心线;阴影不在地球上时返回空结果。
|
||
|
||
`geojson.MarshalSolarEclipseShadowInstant(instant)` 将结果编码为 GeoJSON。输出属性包括 `time`、`source_boundary_closed`、`geometry_role`、`closure`、`delta_t_seconds`、`model` 与 `interp_signature`。没有阴影时返回空 FeatureCollection。
|
||
|
||
| 阴影 | 区域 role | 物理边界 role |
|
||
| --- | --- | --- |
|
||
| 本影/反本影 | `central-shadow-footprint` | `central-shadow-boundary` |
|
||
| 半影 | `partial-footprint` | `partial-footprint-boundary` |
|
||
|
||
单时刻半影与整场偏食采样使用相同的默认边界参数(96 点、200 km 加密),可以对照同一时刻的结果。已有单机测量中,96 点瞬时足迹约 64 µs,站点瞬时状态约 20 µs;这些数据只用于估算成本,首次查询和批量查询会受缓存状态影响。
|
||
|
||
### 地平线闭合与插值
|
||
|
||
`central-shadow-footprint` 只输出 Polygon 或 MultiPolygon。被地平线切断时,物理边界延伸到两个地平擦地点,再沿地平弧闭合为覆盖区域;此时 `source_boundary_closed=false`,`closure` 用 `kind`、`time`、`subsolar` 描述闭合弧。
|
||
|
||
只画真实阴影边缘时使用 `central-shadow-boundary`;需要填色时使用 footprint。阴影在 U1/U4 收缩为空时不输出该要素,不会退化成折线。`source_boundary_closed=true` 表示物理边界本身已经闭合。
|
||
|
||
采样的半影足迹也带这些属性。足迹是瞬时覆盖区域,静态掩带是整场事件的包络,两者不能互相替代。
|
||
|
||
相邻帧可先比较 `interp_signature`,例如 `umbra-closed-seg1-pt97`。只有签名一致、分段数与顶点数对应时才适合按顶点插值;这不代表插值具有严格误差上界。闭合状态改变、反经线分段改变或空/非空切换时,应查询精确几何。
|
||
|
||
移动距离也会随事件阶段变化:已有两分钟采样的对照中,足迹质心在中段移动约 78–232 km,接触附近可达约 520 km。因此不能仅凭固定时间步长判断动画误差。
|
||
|
||
### ΔT 与地面位置
|
||
|
||
求解器的 `DeltaTSeconds > 0` 可为该句柄指定 ΔT;小于等于 0 时采用进程级模型。结果回传实际使用的值。
|
||
|
||
同一 TT 下,改变 ΔT 会改变地球自转相位,而不改变日月在空间中的相对几何。地面经向位移的近似量级为 `0.4651 × |ΔΔT| × cos(纬度)` 千米,函数 `basic.DeltaTGroundShiftKM` 可用于换算。库不提供 ΔT 不确定度模型,误差输入需由调用方给出。
|
||
|
||
### 月掩瞬时足迹
|
||
|
||
`moon.StarOccultationFootprintAt` / `moon.PlanetOccultationFootprintsAt` 的结果可传给 `geojson.MarshalStarOccultationFootprint` / `MarshalPlanetOccultationFootprints`。
|
||
|
||
它们同样输出 `delta_t_seconds`、`source_boundary_closed`、`geometry_role` 和 `interp_signature`。月球地平线闭合采用 `closure.kind=target-horizon`、`body=moon` 与 `sublunar` 月下点。月掩计算沿用进程级 ΔT,并在结果中报告实际值。
|
||
|
||
### 先查事件再计算几何
|
||
|
||
只需要事件列表时,可先用 `eclipse.SolarEclipseCandidates(start, end, options)` 获取食甚时刻、食型、中心食类型、食分、伽马和可选沙罗信息;这个结果不包含地理几何。
|
||
|
||
固定地点的中心食搜索使用 `SearchLocalCentralSolarEclipse`,选项包括 `Kind`、`MaxYears`、`Backward`、`Geometric`、`Model`,返回 `(info, status)`。`status.Exhausted` 表示已用尽搜索范围。
|
||
|
||
`MaxYears<=0` 使用默认搜索预算(6000 次候选步进,约 992 年)。找到事件后再计算路径、足迹或 SVG,可以避免为不需要的事件生成地图数据。
|
||
|
||
## KML
|
||
|
||
`kml.FromGeoJSON(data []byte, options kml.Options) ([]byte, error)` 把 GeoJSON FeatureCollection 转成 KML 2.2。输入可来自本库,也可以是第三方文件;转换器读取已有几何和时间属性,不计算新的星历或动画帧。
|
||
|
||
### 转换文件
|
||
|
||
下面的程序把 `eclipse.geojson` 转成 `eclipse.kml`,可在 Google Earth 中打开:
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"log"
|
||
"os"
|
||
|
||
"b612.me/astro/kml"
|
||
)
|
||
|
||
func main() {
|
||
data, err := os.ReadFile("eclipse.geojson")
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
result, err := kml.FromGeoJSON(data, kml.Options{
|
||
Name: "2009-07-22 日食",
|
||
})
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
if err := os.WriteFile("eclipse.kml", result, 0644); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|
||
```
|
||
|
||
### Options
|
||
|
||
| 字段 | 零值或默认行为 | 设置后的作用 |
|
||
| --- | --- | --- |
|
||
| `Name` | 按事件类型与最早时刻推导文档名 | 指定 `Document/name` |
|
||
| `Language` | `"zh"` | `"en"` 使用英文图层名;未收录的角色保留原始 `role` |
|
||
| `Styles` | 内置配色 | 以 `role` 或 `event/role` 为键覆盖样式,后者优先 |
|
||
| `NoLookAt` | 自动设置取景 | `true` 不写 `Document/LookAt` |
|
||
| `SkipRoles` | 保留所有角色 | 按 `role` 删除整层,包括几何、样式和取景贡献 |
|
||
| `FillContext` | 只填充高亮中心带和掩星带 | `true` 为其他面图层添加浅灰半透明填充 |
|
||
| `NoTimes` | 写入输入中存在的时间 | `true` 不输出时间元素,生成静态叠加 |
|
||
|
||
### 图层与样式
|
||
|
||
要素按 `event` 和 `role` 分组到 Folder。同一集合含多个事件类型时,图层名带事件前缀;第三方 `event` 与 `role` 也可使用相同的样式覆盖规则。
|
||
|
||
| 图层 | role | 默认样式 |
|
||
| --- | --- | --- |
|
||
| 日食中心带与本影 | `central-band`、`central-shadow`、`central-shadow-footprint`、`central-shadow-sweep`、`total-footprint` | 红色;面填充约 35% 不透明度 |
|
||
| 中心线 | `center-line` | 黑色,3 px |
|
||
| 日食食甚等时线 | `greatest-time-line` | 绿色 |
|
||
| 食分线 | `magnitude-line`、`magnitude-one-envelope` | 黄色 |
|
||
| 掩星全掩与偏掩带 | `total-band`、`partial-band`、`total-band-outline`、`occultation-band` | 黄色,带半透明填充 |
|
||
| 升落可见边界、月食时间包络、日食偏食带边缘 | `visibility-boundary`、`p1-horizon`、`p4-horizon`、`visible-at-p1`、`visible-at-p4`、`visible-during-eclipse`、`visible-throughout-eclipse`、日食的 `partial-band` | 橙色 |
|
||
| 仅见半影的月出/月落带 | `penumbra-moonrise`、`penumbra-moonset` | 月出蓝紫、月落紫红,带半透明填充 |
|
||
| 其他限界、轮廓、足迹与时间标记 | 其他 `role` | 灰色,面默认只画轮廓 |
|
||
|
||
`Style` 的零值字段沿用默认样式:
|
||
|
||
| 字段 | 含义 |
|
||
| --- | --- |
|
||
| `LineColor` | 线色,KML `aabbggrr` 十六进制 |
|
||
| `FillColor` | 面填充色;点和线忽略此项 |
|
||
| `LineWidth` | 像素;小于等于 0 时使用默认线宽 |
|
||
| `NoFill` | 强制不填充,优先于 `FillColor` 与默认配色 |
|
||
|
||
KML 颜色顺序是透明通道、蓝、绿、红。例如 `ff0000ff` 为不透明红,`590000ff` 为约 35% 不透明度的红;它与 CSS `rrggbb` 的顺序不同。
|
||
|
||
```go
|
||
options := kml.Options{
|
||
Language: "zh",
|
||
Styles: map[string]kml.Style{
|
||
"center-line": {LineColor: kml.ColorBlack, LineWidth: 4},
|
||
"solar-eclipse/central-band": {FillColor: "590000ff"},
|
||
"partial-footprint": {NoFill: true},
|
||
},
|
||
}
|
||
result, err := kml.FromGeoJSON(data, options)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
fmt.Println(string(result))
|
||
```
|
||
|
||
无填充的面会转成轮廓线。有填充的面与描边分别表示,反经线拆分产生的接缝不作为真实边界绘制。这样既可保持面闭合,也可避免沿 ±180° 经线出现贯穿地图的描边。
|
||
|
||
### 时间轴与静态叠加
|
||
|
||
| GeoJSON 时间数据 | KML 输出 |
|
||
| --- | --- |
|
||
| 单个 `time` 属性 | 包含该要素全部子几何的 Placemark 带一个 `TimeStamp` |
|
||
| MultiLineString 的 `times` | 按线段拆成 Placemark,各段取首个时间作为 `TimeStamp` |
|
||
| 至少一个有效时间戳 | Document 带覆盖最早至最晚时间戳的 `TimeSpan` |
|
||
| `time_scale: "UT1"` | 先按当前时标模型换回 UTC,再写 `<when>` |
|
||
| `NoTimes: true` | 不输出 `TimeStamp` 或 `TimeSpan` |
|
||
|
||
时间戳保留源数据的小数秒。要素含 `label` 时用它作名称;自动生成的名称只显示到秒。UT1 数据的原 `time_scale` 保留在属性中;转换时应与生成 GeoJSON 时使用同一 ΔT 模型。
|
||
|
||
时间轴中的帧来自输入数据。`times` 不会让折线上的每个顶点自动变成独立动画帧;需要本影或半影逐时刻变化时,应先用 GeoJSON 侧的采样选项生成这些要素。
|
||
|
||
Google Earth 会按所选时间窗口隐藏带时间戳的要素。查看静态全路径时可设置 `NoTimes: true`;播放时则将时间窗口移到事件日期,并调整可见时间段的宽度。
|
||
|
||
### 控制文件大小和取景
|
||
|
||
半影足迹通常覆盖很大区域。密集采样、大量顶点与半透明面重叠都会增加客户端绘制开销;可增大采样步长,或按用途去掉部分图层。
|
||
|
||
只需要总偏食区边缘和中心食路径时,可跳过 `partial-footprint`,保留 `partial-band`:
|
||
|
||
```go
|
||
result, err := kml.FromGeoJSON(data, kml.Options{
|
||
SkipRoles: []string{"partial-footprint"},
|
||
NoTimes: true,
|
||
})
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
fmt.Println(string(result))
|
||
```
|
||
|
||
若在生成 GeoJSON 时已经确定不需要某层,可通过 `geojson.MarshalSolarEclipseWithOptions` 的 `SkipRoles` 先排除,减少中间数据。KML 的 `SkipRoles` 适用于已有文件。
|
||
|
||
自动取景优先使用中心线、中心带、限界和本影等路径图层,缺少这些图层时再使用全部要素。包络考虑跨反经线情况,`LookAt/range` 的单位为米,下限 200 km。需要客户端自行定位时设 `NoLookAt: true`。
|
||
|
||
### 属性与输入校验
|
||
|
||
通过 `SkipRoles` 筛选后,所有保留要素都具有且值相同的属性才提升到 `Document/ExtendedData`。其余属性留在各自 Placemark,嵌套对象也会保留;`times` 已转换成时间戳,不重复写入属性。
|
||
|
||
坐标至少含经纬度两项。经度超出 ±180° 时回绕,原本的 +180° 与 −180° 保持不变;纬度必须在 ±90° 内。额外的高度分量被忽略,输出采用 `clampToGround`。
|
||
|
||
多边形环会补闭合,并调整为外环逆时针、内环顺时针。支持 Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon 和 GeometryCollection。
|
||
|
||
以下情况返回错误:JSON 或几何类型无效、坐标非有限或纬度越界、时间数组与线段结构不匹配、过滤后没有剩余要素,或所有要素均没有可绘制几何。两个相同坐标组成的单点路径片段会被跳过。输入本来就是空 FeatureCollection 时,返回合法的空 Document。
|