@3clear/basegis 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -17
- package/dist/WindDataGrid-DA9pzsfX.js +508 -0
- package/dist/basegis.js +3578 -2636
- package/dist/methods.js +1232 -935
- package/package.json +1 -1
- package/dist/WindDataGrid-B1KZflZc.js +0 -438
package/README.md
CHANGED
|
@@ -4526,8 +4526,10 @@ await rasterContour.loadGrid({
|
|
|
4526
4526
|
| `thresholds/levels` | `number[]` | 三选一 | - | 显式等值线级别,仅保留实际值域内的值。 |
|
|
4527
4527
|
| `interval/contourInterval` | `number` | 三选一 | - | 固定等值距。 |
|
|
4528
4528
|
| `thresholdCount/levelCount` | `number` | 三选一 | `12` | 自动均分级别数量,范围 1~100。 |
|
|
4529
|
-
| `smooth` | `boolean` | 否 | `true` |
|
|
4529
|
+
| `smooth` | `boolean` | 否 | `true` | 是否使用 d3 的边上线性插值定位等值点;与高斯滤波、Chaikin 开关独立。 |
|
|
4530
4530
|
| `smoothingIterations` | `number` | 否 | `0` | Chaikin 补点平滑次数,范围 0~3;气象格点通常取 1~2,次数越高,点数越多。 |
|
|
4531
|
+
| `gridSmoothing` | `object` | 否 | 见下表 | 提线前的可分离高斯滤波;默认关闭,保留原始网格与 NoData。 |
|
|
4532
|
+
| `wrapLongitude` | `boolean` | 否 | `false` | 对经度跨度为 360°(容差 0.001°)的栅格启用周期取样、补列、完整线拼接和平滑;区域数据不启用。 |
|
|
4531
4533
|
| `excludeBoundary` | `boolean` | 否 | `true` | 去掉 d3 面边界中贴着栅格四边的伪矩形线。 |
|
|
4532
4534
|
| `maxCells` | `number` | 否 | `1000000` | 超过该像元数时先等比例抽样,避免大图阻塞页面。 |
|
|
4533
4535
|
| `minimumLinePoints` | `number` | 否 | `3` | 最短线点数,用于过滤栅格四角的两点补边。 |
|
|
@@ -4537,24 +4539,60 @@ await rasterContour.loadGrid({
|
|
|
4537
4539
|
|
|
4538
4540
|
级别选择优先级为 `thresholds` → `interval` → `thresholdCount`。显式传入 `thresholds` 时,只保留实际值域内的级别。
|
|
4539
4541
|
|
|
4542
|
+
### 格点平滑与全球接缝
|
|
4543
|
+
|
|
4544
|
+
| `gridSmoothing` 参数 | 默认值 | 说明 |
|
|
4545
|
+
| --- | --- | --- |
|
|
4546
|
+
| `enabled` | `false` | 显式开启格点高斯滤波。 |
|
|
4547
|
+
| `radius` | `2` | 邻域半径,以 `maxCells` 降采样后的格点为单位,取整并限制为 0~6;0 跳过滤波。 |
|
|
4548
|
+
| `sigma` | `null` | 正数;省略、null 或非正数时自动取 `Math.max(radius / 2, 0.8)`,单位同样为格点。 |
|
|
4549
|
+
| `passes` | `1` | 水平和垂直方向各处理一次为一轮,取整并限制为 1~3 轮。 |
|
|
4550
|
+
|
|
4551
|
+
```js
|
|
4552
|
+
await rasterContour.loadGrid({
|
|
4553
|
+
values, width, height, area,
|
|
4554
|
+
interval: 2,
|
|
4555
|
+
maxCells: 360000,
|
|
4556
|
+
gridSmoothing: { enabled: true, radius: 2, sigma: null, passes: 1 },
|
|
4557
|
+
smooth: true,
|
|
4558
|
+
smoothingIterations: 1,
|
|
4559
|
+
wrapLongitude: true,
|
|
4560
|
+
})
|
|
4561
|
+
|
|
4562
|
+
// 嵌套参数按字段合并,保留 enabled / passes;sigma 为 null 时随半径自动调整。
|
|
4563
|
+
await rasterContour.update({ gridSmoothing: { radius: 3 } })
|
|
4564
|
+
// 关闭额外平滑,仍保留等值点线性插值和全球接缝连续性。
|
|
4565
|
+
await rasterContour.update({ gridSmoothing: { enabled: false }, smoothingIterations: 0 })
|
|
4566
|
+
// 标签、样式和渲染简化更新直接复用已有等值线。
|
|
4567
|
+
await rasterContour.update({ showLabel: false, smoothFactor: 0.15 })
|
|
4568
|
+
```
|
|
4569
|
+
|
|
4570
|
+
处理顺序是原始网格 → 降采样 → 高斯滤波 → 全球补列(可选)→ d3 提线 → 拼接完整周期线(可选)→ Chaikin → 短线过滤 → 裁切和坐标取整。周期线在拼接前保留精度,避免低 `coordinatePrecision` 合并不同接缝端点;只按完整曲线判断 `minimumLinePoints`,切回边界后的片段允许小于该长度。源数据为 0°~360° 且需要左右半幅交换时,继续使用 `isSplit: true`。
|
|
4571
|
+
|
|
4572
|
+
滤波跳过无效邻点并重新归一化权重,原始 NoData 中心保持为空。`getRasterData()` 始终返回未经高斯滤波、未经周期补列的标准原始网格,反复调参不会累积平滑。高斯改变用于提线的数值场,Chaikin 调整线形,因此输出是平滑后的近似等值线。H/L 的 `closed-contour` 模式共用降采样和高斯滤波后的标量场;原 `neighborhood` 模式仍独立使用原始网格和 `centerDetection.smoothingIterations`。
|
|
4573
|
+
|
|
4574
|
+
`test-page-18` 的“平滑与接缝”“高 / 低值中心”“计算与渲染控制”面板可调整上述参数,滑杆松开时自动应用,并显示当前配置 JSON、中心筛选统计、线点数与耗时。演示数据为 ECMWF 海平面气压灰度图(`2026091015`),值差单位是 hPa;灰度 0~255 对应 950.9156494140625~1040.6756591796875 hPa。计算目前仍在主线程执行;可以通过 `maxCells` 和迭代次数限制开销。
|
|
4575
|
+
|
|
4540
4576
|
### 自动检测 H/L 中心
|
|
4541
4577
|
|
|
4542
|
-
|
|
4578
|
+
该能力默认关闭。它从数值网格查找局部极大值(H)和局部极小值(L),支持原邻域均值模式和闭合值差模式;仅在需要表达高低值中心时开启。下面是全球气压数据的闭合值差配置示例,输入单位为 hPa:
|
|
4543
4579
|
|
|
4544
4580
|
```js
|
|
4545
4581
|
await rasterContour.loadGrid({
|
|
4546
4582
|
values: pressureValues,
|
|
4547
4583
|
width,
|
|
4548
4584
|
height,
|
|
4549
|
-
area: [
|
|
4550
|
-
|
|
4585
|
+
area: [-180, -90, 180, 90],
|
|
4586
|
+
wrapLongitude: true,
|
|
4587
|
+
interval: 2,
|
|
4588
|
+
gridSmoothing: { enabled: true, radius: 2, sigma: null, passes: 1 },
|
|
4551
4589
|
showLabel: true,
|
|
4552
4590
|
centerDetection: {
|
|
4553
4591
|
enabled: true,
|
|
4554
|
-
|
|
4555
|
-
minProminence:
|
|
4556
|
-
|
|
4557
|
-
maxCount:
|
|
4592
|
+
method: 'closed-contour',
|
|
4593
|
+
minProminence: 1,
|
|
4594
|
+
minDistanceKm: 250,
|
|
4595
|
+
maxCount: 256,
|
|
4558
4596
|
},
|
|
4559
4597
|
centerAnnotation: {
|
|
4560
4598
|
visible: true,
|
|
@@ -4568,15 +4606,23 @@ await rasterContour.loadGrid({
|
|
|
4568
4606
|
| `centerDetection` 参数 | 默认值 | 说明 |
|
|
4569
4607
|
| --- | --- | --- |
|
|
4570
4608
|
| `enabled` | `false` | 开启 H/L 自动检测。 |
|
|
4571
|
-
| `
|
|
4572
|
-
| `minProminence` | `2` |
|
|
4573
|
-
| `
|
|
4574
|
-
| `maxCount` | `12` | H/L
|
|
4609
|
+
| `method` | `'neighborhood'` | `'neighborhood'` 保留原邻域均值筛选;`'closed-contour'` 检查闭合值差。 |
|
|
4610
|
+
| `minProminence` | `2` | 单位与输入网格一致。闭合模式中是最小闭合值差;邻域模式中是中心与邻域均值的差。 |
|
|
4611
|
+
| `minDistanceKm` | `250` | 仅闭合模式:同类中心最小球面距离,单位 km;`0` 关闭距离筛选,距离过近时优先保留更高的 H 或更低的 L。 |
|
|
4612
|
+
| `maxCount` | `12` | 全部 H/L 合计最大输出数;全球图建议显式提高并通过诊断计数检查是否触及上限。闭合模式按 H/L 交替取数,各类型内部按更高 H、更低 L 排序。 |
|
|
4575
4613
|
| `includeHigh/includeLow` | `true/true` | 是否输出 H 或 L。 |
|
|
4576
|
-
| `
|
|
4577
|
-
| `
|
|
4614
|
+
| `radius` | `6` | 仅邻域模式:计算邻域均值的网格半径。 |
|
|
4615
|
+
| `minGridDistance` | `20` | 仅邻域模式:同类中心的最小网格距离。 |
|
|
4616
|
+
| `excludeBoundary` | `true` | 仅邻域模式:排除贴近边界或 NoData 空洞的候选。闭合模式始终排除开口。 |
|
|
4617
|
+
| `smoothingIterations` | `1` | 仅邻域模式:独立的 3×3 有效值平滑次数,范围 0~3。闭合模式共用顶层 `gridSmoothing`,不额外平滑。 |
|
|
4578
4618
|
|
|
4579
|
-
|
|
4619
|
+
闭合模式先合并八连通的同值平台,每个平台只保留一个位于平台内部、接近平台中心的代表点,再检查整个边界是否满足局部极值。从 H 向外下降 `minProminence`、从 L 向外上升 `minProminence`,用漫水检查这段值差范围内是否遇到区域边界、缺测区或更强的同类中心。出现开口、或在达到阈值之前并入更强中心则过滤;恰好在阈值处合并允许通过。独立同值极值若在阈值内相连,只保留一个。它检查的是给定值差下的闭合性和合并通路,不输出完整的等值线树或真实鞍点显著度,也不要求当前 `thresholds` 正好绘制出那一条闭合线。
|
|
4620
|
+
|
|
4621
|
+
闭合模式与等值线共用 `maxCells` 降采样及高斯滤波后的标量场,中心值取该场的代表格点值,中心位置与 d3 一致采用像元中心坐标。`wrapLongitude: true` 且数据覆盖完整 360° 时,左右邻域周期相接;南北边界不循环。距离筛选用球面公里距离,因此同一经度差在不同纬度不会当作相同距离。最后再应用 `maxCount`。
|
|
4622
|
+
|
|
4623
|
+
原邻域模式先独立进行轻量平滑,再按中心与窗口均值的差过滤、合并过近同类中心并限制数量;超过 100 万格时只对候选搜索做块均值抽样,最终回到原网格取值和坐标。两种模式的 `minProminence` 含义不同,调参时不应直接等同。关闭检测后会清理上一次自动中心。
|
|
4624
|
+
|
|
4625
|
+
新模式通过 `getState().centerDiagnostics` 返回 `candidates`(极值候选)、`closed`(通过闭合检查)、`rejectedOpen`(边界或缺测开口)、`rejectedWeak`(阈值内合并)、`rejectedDistance`(距离去重)及 `rejectedLimit`(数量上限)。`closed` 包含之后被距离和数量筛掉的点。原模式返回 `null`;`clear/destroy` 清空诊断。`centerCount` 是实际输出中心数,`centerLabelCount` 是经过当前视野和碰撞避让后的可见标注数,两者可以不同。
|
|
4580
4626
|
|
|
4581
4627
|
`RasterContourController` 也可直接传顶层 `centers`。手工数组(包括 `[]`)优先于自动检测;传 `centers: null` 可恢复使用当前 `centerDetection` 的结果。检测只产生中心数据,仍需 `centerAnnotation.visible: true` 才会显示。
|
|
4582
4628
|
|
|
@@ -4589,12 +4635,14 @@ await rasterContour.loadGrid({
|
|
|
4589
4635
|
| `loadGeoTiff(payload, options)` | 从 GeoTIFF 生成。 |
|
|
4590
4636
|
| `loadGrayImage(payload, options)` | 从灰度图生成。 |
|
|
4591
4637
|
| `loadGrid(payload, options)` | 从数值网格生成。 |
|
|
4592
|
-
| `update(payload)` |
|
|
4638
|
+
| `update(payload)` | 算法参数变化时从原始网格重算;标签、颜色、线宽、渲染简化等显示参数直接复用等值线;修改数据源参数时重新加载。`gridSmoothing` 支持字段级合并。 |
|
|
4593
4639
|
| `show/hide/toggle` | 统一控制等值线、线值标签和中心标注显隐。 |
|
|
4594
4640
|
| `clear/destroy` | 清空或销毁控制器。 |
|
|
4595
|
-
| `getState()` | 返回尺寸、值域、级别、线数、`centerCount`、标注数、投影、耗时和底层 contour
|
|
4641
|
+
| `getState()` | 返回尺寸、值域、级别、线数、`pointCount`、`centerCount`、标注数、投影、耗时和底层 contour 状态;另含实际 `gridSmoothing`、`wrapLongitude`、`gridSmoothingElapsed`、`contoursReused`、`centerDiagnostics`。 |
|
|
4596
4642
|
| `getRasterData()` | 返回当前标准栅格,包含 `values/width/height/area`。 |
|
|
4597
4643
|
|
|
4644
|
+
`gridSmoothingElapsed` 已包含在 `generateElapsed` 中,不能重复相加;显示更新复用几何时,两项均为 0,`contoursReused` 为 true。`renderWidth/renderHeight` 不包含内部周期补列。`clear()` 释放等值线缓存并保留原始网格;`destroy()` 同时释放两者。
|
|
4645
|
+
|
|
4598
4646
|
```js
|
|
4599
4647
|
// clear 只清空当前渲染结果,控制器仍可继续 update/load。
|
|
4600
4648
|
rasterContour.clear()
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
const C = {
|
|
2
|
+
OK: "OK",
|
|
3
|
+
CANCELLED: "CANCELLED",
|
|
4
|
+
FAILED: "FAILED",
|
|
5
|
+
NOT_INITIALIZED: "NOT_INITIALIZED",
|
|
6
|
+
INVALID_CONTAINER: "INVALID_CONTAINER",
|
|
7
|
+
ENGINE_NOT_REGISTERED: "ENGINE_NOT_REGISTERED",
|
|
8
|
+
ENGINE_RESOURCE_MISSING: "ENGINE_RESOURCE_MISSING",
|
|
9
|
+
METHOD_NOT_FOUND: "METHOD_NOT_FOUND",
|
|
10
|
+
UNSUPPORTED_CAPABILITY: "UNSUPPORTED_CAPABILITY",
|
|
11
|
+
INVALID_DEM: "INVALID_DEM"
|
|
12
|
+
};
|
|
13
|
+
function v({
|
|
14
|
+
success: t = !0,
|
|
15
|
+
message: n = "",
|
|
16
|
+
data: e = null,
|
|
17
|
+
code: i = C.OK
|
|
18
|
+
} = {}) {
|
|
19
|
+
return { success: t, message: n, data: e, code: i };
|
|
20
|
+
}
|
|
21
|
+
function k(t = null, n = "OK", e = C.OK) {
|
|
22
|
+
return v({ success: !0, message: n, data: t, code: e });
|
|
23
|
+
}
|
|
24
|
+
function U(t = "Operation failed.", n = C.FAILED, e = null) {
|
|
25
|
+
return v({ success: !1, message: t, data: e, code: n });
|
|
26
|
+
}
|
|
27
|
+
function tt(t, n, e = "") {
|
|
28
|
+
const i = `${n} adapter does not support "${t}". ${e}`.trim();
|
|
29
|
+
return console.warn(`[3clear-dt-base] ${i}`), U(i, C.UNSUPPORTED_CAPABILITY, {
|
|
30
|
+
methodName: t,
|
|
31
|
+
engineType: n,
|
|
32
|
+
detail: e
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function nt(t, n = {}) {
|
|
36
|
+
var i, s, o, r, l;
|
|
37
|
+
if (n.currentTime !== void 0 && n.currentTime !== null && !Number.isFinite(n.currentTime))
|
|
38
|
+
return "currentTime must be a finite timestamp in milliseconds or null.";
|
|
39
|
+
if (t.times !== void 0) {
|
|
40
|
+
if (!t.colors || !Array.isArray(t.times) || t.times.length !== ((i = t.positions) == null ? void 0 : i.length))
|
|
41
|
+
return "line.times requires vertex colors and one timestamp for each position.";
|
|
42
|
+
for (let a = 0; a < t.times.length; a += 1)
|
|
43
|
+
if (!Number.isFinite(t.times[a]) || a > 0 && t.times[a] <= t.times[a - 1])
|
|
44
|
+
return "line.times must contain strictly increasing finite timestamps in milliseconds.";
|
|
45
|
+
}
|
|
46
|
+
if (t.colors === void 0) return "";
|
|
47
|
+
if (!Array.isArray(t.colors) || t.colors.length !== ((s = t.positions) == null ? void 0 : s.length))
|
|
48
|
+
return "line.colors must have one color for each position.";
|
|
49
|
+
if (t.colors.some((a) => typeof a != "string" || !/^#[0-9a-f]{6}$/i.test(a)))
|
|
50
|
+
return "line.colors requires six-digit HEX colors.";
|
|
51
|
+
const e = { ...n.style, ...t.style };
|
|
52
|
+
return e.pattern === "dashed" || (r = (o = e.color) == null ? void 0 : o.flow) != null && r.enabled ? "Vertex colors support solid lines only." : (e.clampToGround ?? n.clampToGround) === !0 ? "Vertex colors do not support clampToGround; use false for the thematic overlay." : (l = n.animation) != null && l.enabled && (!n.animation.lineId || String(n.animation.lineId) === String(t.id)) ? "Vertex-colored lines do not support reveal animation." : "";
|
|
53
|
+
}
|
|
54
|
+
const V = {
|
|
55
|
+
TD: "#e6d64f",
|
|
56
|
+
TS: "#2f8df4",
|
|
57
|
+
STS: "#28a46a",
|
|
58
|
+
TY: "#f59e0b",
|
|
59
|
+
STY: "#d946ef",
|
|
60
|
+
SuperTY: "#ef4444"
|
|
61
|
+
}, L = {
|
|
62
|
+
trackLine: {
|
|
63
|
+
color: "#ffffff",
|
|
64
|
+
width: 3,
|
|
65
|
+
opacity: 0.96,
|
|
66
|
+
outlineColor: "#08131f",
|
|
67
|
+
outlineWidth: 1
|
|
68
|
+
},
|
|
69
|
+
forecastLine: {
|
|
70
|
+
color: "#f8fafc",
|
|
71
|
+
width: 2,
|
|
72
|
+
opacity: 0.92,
|
|
73
|
+
dashLength: 12,
|
|
74
|
+
gapLength: 8
|
|
75
|
+
},
|
|
76
|
+
point: {
|
|
77
|
+
radius: 5,
|
|
78
|
+
outlineColor: "#07111f",
|
|
79
|
+
outlineWidth: 1
|
|
80
|
+
},
|
|
81
|
+
label: {
|
|
82
|
+
visible: !0,
|
|
83
|
+
color: "#ffffff",
|
|
84
|
+
fontSize: 12,
|
|
85
|
+
backgroundColor: "rgba(2, 20, 42, 0.82)",
|
|
86
|
+
offset: [0, -22]
|
|
87
|
+
},
|
|
88
|
+
center: {
|
|
89
|
+
iconUrl: "",
|
|
90
|
+
iconSize: [36, 36],
|
|
91
|
+
rotate: !0,
|
|
92
|
+
rotationDurationMs: 1400,
|
|
93
|
+
showLabel: !0,
|
|
94
|
+
labelColor: "#07111f",
|
|
95
|
+
labelBackgroundColor: "rgba(255, 255, 255, 0.88)",
|
|
96
|
+
labelOffset: [30, -30]
|
|
97
|
+
},
|
|
98
|
+
windCircle: {
|
|
99
|
+
visible: !0,
|
|
100
|
+
r7: { color: "#c8c537", fillOpacity: 0.26, outlineOpacity: 0.9 },
|
|
101
|
+
r10: { color: "#ffaa00", fillOpacity: 0.22, outlineOpacity: 0.9 },
|
|
102
|
+
r12: { color: "#cc4400", fillOpacity: 0.18, outlineOpacity: 0.95 }
|
|
103
|
+
},
|
|
104
|
+
intensityColors: V
|
|
105
|
+
}, R = {
|
|
106
|
+
enabled: !0,
|
|
107
|
+
durationMs: 15e3,
|
|
108
|
+
autoplay: !1,
|
|
109
|
+
loop: !1,
|
|
110
|
+
currentIndex: 0
|
|
111
|
+
};
|
|
112
|
+
function c(t) {
|
|
113
|
+
return !!(t && typeof t == "object" && !Array.isArray(t));
|
|
114
|
+
}
|
|
115
|
+
function y(t, n) {
|
|
116
|
+
return Object.prototype.hasOwnProperty.call(t || {}, n);
|
|
117
|
+
}
|
|
118
|
+
function f(t, n, e) {
|
|
119
|
+
return Math.min(Math.max(t, n), e);
|
|
120
|
+
}
|
|
121
|
+
function u(t, n = 0) {
|
|
122
|
+
const e = Number(t);
|
|
123
|
+
return Number.isFinite(e) ? e : n;
|
|
124
|
+
}
|
|
125
|
+
function p(t, n) {
|
|
126
|
+
return {
|
|
127
|
+
...c(t) ? t : {},
|
|
128
|
+
...c(n) ? n : {}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function _(t = {}, n = {}) {
|
|
132
|
+
const e = c(t) ? t : {}, i = c(n) ? n : {}, s = {
|
|
133
|
+
...e,
|
|
134
|
+
...i
|
|
135
|
+
};
|
|
136
|
+
["trackLine", "forecastLine", "point", "label", "center", "intensityColors"].forEach((l) => {
|
|
137
|
+
s[l] = p(e[l], i[l]);
|
|
138
|
+
});
|
|
139
|
+
const o = c(e.windCircle) ? e.windCircle : {}, r = c(i.windCircle) ? i.windCircle : {};
|
|
140
|
+
return s.windCircle = {
|
|
141
|
+
...o,
|
|
142
|
+
...r,
|
|
143
|
+
r7: p(o.r7, r.r7),
|
|
144
|
+
r10: p(o.r10, r.r10),
|
|
145
|
+
r12: p(o.r12, r.r12)
|
|
146
|
+
}, s;
|
|
147
|
+
}
|
|
148
|
+
function et(t = {}, n = {}) {
|
|
149
|
+
const e = c(t) ? t : {}, i = c(n) ? n : {}, s = {
|
|
150
|
+
...e,
|
|
151
|
+
...i,
|
|
152
|
+
typhoon: p(e.typhoon, i.typhoon),
|
|
153
|
+
style: _(e.style, i.style),
|
|
154
|
+
playback: p(e.playback, i.playback)
|
|
155
|
+
};
|
|
156
|
+
return !y(i, "tracks") && y(e, "tracks") && (s.tracks = e.tracks), !y(i, "forecastPaths") && y(e, "forecastPaths") && (s.forecastPaths = e.forecastPaths), s;
|
|
157
|
+
}
|
|
158
|
+
function G(t, n) {
|
|
159
|
+
if (!Array.isArray(t) || t.length < 2)
|
|
160
|
+
throw new Error(`${n} must be [longitude, latitude, height?].`);
|
|
161
|
+
const e = Number(t[0]), i = Number(t[1]), s = t.length > 2 ? Number(t[2]) : 0;
|
|
162
|
+
if (!Number.isFinite(e) || !Number.isFinite(i) || !Number.isFinite(s) || i < -90 || i > 90)
|
|
163
|
+
throw new Error(`${n} must contain finite coordinates and latitude must be between -90 and 90.`);
|
|
164
|
+
return [e, i, s];
|
|
165
|
+
}
|
|
166
|
+
function Y(t, n) {
|
|
167
|
+
if (t == null) return null;
|
|
168
|
+
if (!c(t))
|
|
169
|
+
throw new Error(`${n} must be { ne, se, sw, nw } in kilometres.`);
|
|
170
|
+
const e = {};
|
|
171
|
+
for (const i of ["ne", "se", "sw", "nw"]) {
|
|
172
|
+
const s = Number(t[i]);
|
|
173
|
+
if (!Number.isFinite(s) || s < 0)
|
|
174
|
+
throw new Error(`${n}.${i} must be a non-negative number.`);
|
|
175
|
+
e[i] = s;
|
|
176
|
+
}
|
|
177
|
+
return e;
|
|
178
|
+
}
|
|
179
|
+
function W(t, n) {
|
|
180
|
+
if (t == null) return {};
|
|
181
|
+
if (!c(t)) throw new Error(`${n} must be an object.`);
|
|
182
|
+
return ["r7", "r10", "r12"].reduce((e, i) => {
|
|
183
|
+
const s = Y(t[i], `${n}.${i}`);
|
|
184
|
+
return s && (e[i] = s), e;
|
|
185
|
+
}, {});
|
|
186
|
+
}
|
|
187
|
+
function $(t, n, e = "tracks") {
|
|
188
|
+
if (!c(t)) throw new Error(`${e}[${n}] must be an object.`);
|
|
189
|
+
const i = t.time ?? "", s = Date.parse(i), o = i == null || String(i) === "" ? `${e}-${n}` : i;
|
|
190
|
+
return {
|
|
191
|
+
...t,
|
|
192
|
+
id: String(t.id === void 0 || t.id === null || String(t.id) === "" ? o : t.id),
|
|
193
|
+
time: i,
|
|
194
|
+
timestamp: Number.isFinite(s) ? s : null,
|
|
195
|
+
position: G(t.position, `${e}[${n}].position`),
|
|
196
|
+
level: String(t.level || "TD"),
|
|
197
|
+
windSpeed: t.windSpeed === void 0 ? null : u(t.windSpeed, null),
|
|
198
|
+
pressure: t.pressure === void 0 ? null : u(t.pressure, null),
|
|
199
|
+
windCircles: W(t.windCircles, `${e}[${n}].windCircles`)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function j(t, n, e) {
|
|
203
|
+
if (!c(t)) throw new Error(`forecastPaths[${n}] must be an object.`);
|
|
204
|
+
if (!Array.isArray(t.points) || t.points.length === 0)
|
|
205
|
+
throw new Error(`forecastPaths[${n}].points requires at least one point.`);
|
|
206
|
+
const i = String(t.id ?? `forecast-${n}`), s = t.points.map((o, r) => {
|
|
207
|
+
const l = $(o, r, `forecastPaths[${n}].points`), a = o == null ? void 0 : o.uncertaintyRadiusKm;
|
|
208
|
+
return {
|
|
209
|
+
...l,
|
|
210
|
+
uncertaintyRadiusKm: a == null ? 0 : Math.max(u(a, 0), 0)
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
...t,
|
|
215
|
+
id: i,
|
|
216
|
+
points: s,
|
|
217
|
+
style: p(e, t.style)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function q(t = {}) {
|
|
221
|
+
const n = _(L, t);
|
|
222
|
+
return n.trackLine.width = Math.max(u(n.trackLine.width, 3), 0.5), n.trackLine.opacity = f(u(n.trackLine.opacity, 0.96), 0, 1), n.trackLine.outlineWidth = Math.max(u(n.trackLine.outlineWidth, 1), 0), n.forecastLine.width = Math.max(u(n.forecastLine.width, 2), 0.5), n.forecastLine.opacity = f(u(n.forecastLine.opacity, 0.92), 0, 1), n.forecastLine.dashLength = Math.max(u(n.forecastLine.dashLength, 12), 1), n.forecastLine.gapLength = Math.max(u(n.forecastLine.gapLength, 8), 1), n.point.radius = Math.max(u(n.point.radius, 5), 1), n.point.outlineWidth = Math.max(u(n.point.outlineWidth, 1), 0), n.label.fontSize = Math.max(u(n.label.fontSize, 12), 8), n.label.offset = Array.isArray(n.label.offset) ? n.label.offset.slice(0, 2) : [0, -22], n.center.iconSize = Array.isArray(n.center.iconSize) ? n.center.iconSize.slice(0, 2).map((e) => Math.max(u(e, 36), 1)) : [36, 36], n.center.rotationDurationMs = Math.max(u(n.center.rotationDurationMs, 1400), 1), n.center.labelOffset = Array.isArray(n.center.labelOffset) ? n.center.labelOffset.slice(0, 2) : [30, -30], ["r7", "r10", "r12"].forEach((e) => {
|
|
223
|
+
n.windCircle[e].fillOpacity = f(
|
|
224
|
+
u(n.windCircle[e].fillOpacity, L.windCircle[e].fillOpacity),
|
|
225
|
+
0,
|
|
226
|
+
1
|
|
227
|
+
), n.windCircle[e].outlineOpacity = f(
|
|
228
|
+
u(n.windCircle[e].outlineOpacity, L.windCircle[e].outlineOpacity),
|
|
229
|
+
0,
|
|
230
|
+
1
|
|
231
|
+
);
|
|
232
|
+
}), n;
|
|
233
|
+
}
|
|
234
|
+
function it(t = {}, n = {}) {
|
|
235
|
+
const e = c(t) ? t : {}, i = Array.isArray(e.tracks) ? e.tracks.map((l, a) => $(l, a)) : [];
|
|
236
|
+
if (!n.allowEmpty && i.length === 0)
|
|
237
|
+
throw new Error("Typhoon path requires a non-empty tracks array.");
|
|
238
|
+
const s = q(e.style), o = Array.isArray(e.forecastPaths) ? e.forecastPaths.map((l, a) => j(l, a, s.forecastLine)) : [], r = p(R, e.playback);
|
|
239
|
+
return r.enabled = r.enabled !== !1, r.durationMs = Math.max(u(r.durationMs, R.durationMs), 1), r.autoplay = r.autoplay === !0, r.loop = r.loop === !0, r.currentIndex = i.length ? f(Math.floor(u(r.currentIndex, 0)), 0, i.length - 1) : 0, {
|
|
240
|
+
...e,
|
|
241
|
+
layerId: String(e.typhoonPathLayerId || e.layerId || e.id || "typhoon-path-default"),
|
|
242
|
+
typhoon: p({}, e.typhoon),
|
|
243
|
+
tracks: i,
|
|
244
|
+
forecastPaths: o,
|
|
245
|
+
style: s,
|
|
246
|
+
playback: r,
|
|
247
|
+
visible: e.visible !== !1,
|
|
248
|
+
onCurrentChange: typeof e.onCurrentChange == "function" ? e.onCurrentChange : null,
|
|
249
|
+
onPointClick: typeof e.onPointClick == "function" ? e.onPointClick : null
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function m(t) {
|
|
253
|
+
return t * Math.PI / 180;
|
|
254
|
+
}
|
|
255
|
+
function N(t) {
|
|
256
|
+
return t * 180 / Math.PI;
|
|
257
|
+
}
|
|
258
|
+
function K(t, n) {
|
|
259
|
+
const e = m(t[1]), i = m(n[1]), s = i - e, o = m(n[0] - t[0]), r = Math.sin(s / 2) ** 2 + Math.cos(e) * Math.cos(i) * Math.sin(o / 2) ** 2;
|
|
260
|
+
return 6371e3 * 2 * Math.atan2(Math.sqrt(r), Math.sqrt(Math.max(1 - r, 0)));
|
|
261
|
+
}
|
|
262
|
+
function F(t) {
|
|
263
|
+
if (t.length <= 1) return [0];
|
|
264
|
+
if (t.every(
|
|
265
|
+
(s, o) => s.timestamp !== null && (o === 0 || s.timestamp > t[o - 1].timestamp)
|
|
266
|
+
)) {
|
|
267
|
+
const s = t[0].timestamp, o = t[t.length - 1].timestamp - s;
|
|
268
|
+
if (o > 0) return t.map((r) => (r.timestamp - s) / o);
|
|
269
|
+
}
|
|
270
|
+
const e = [0];
|
|
271
|
+
for (let s = 1; s < t.length; s += 1) {
|
|
272
|
+
const o = K(t[s - 1].position, t[s].position), r = t[s].position[2] - t[s - 1].position[2];
|
|
273
|
+
e.push(e[s - 1] + Math.hypot(o, r));
|
|
274
|
+
}
|
|
275
|
+
const i = e[e.length - 1];
|
|
276
|
+
return i > 0 ? e.map((s) => s / i) : e.map((s, o) => o / (t.length - 1));
|
|
277
|
+
}
|
|
278
|
+
function H(t, n, e) {
|
|
279
|
+
const i = m(t[1]), s = m(t[0]), o = m(n[1]), r = m(n[0]), l = 2 * Math.asin(Math.sqrt(
|
|
280
|
+
Math.sin((o - i) / 2) ** 2 + Math.cos(i) * Math.cos(o) * Math.sin((r - s) / 2) ** 2
|
|
281
|
+
));
|
|
282
|
+
if (!Number.isFinite(l) || l < 1e-9)
|
|
283
|
+
return [t[0], t[1], t[2] + (n[2] - t[2]) * e];
|
|
284
|
+
const a = Math.sin(l), h = Math.sin((1 - e) * l) / a, d = Math.sin(e * l) / a, M = h * Math.cos(i) * Math.cos(s) + d * Math.cos(o) * Math.cos(r), g = h * Math.cos(i) * Math.sin(s) + d * Math.cos(o) * Math.sin(r), b = h * Math.sin(i) + d * Math.sin(o);
|
|
285
|
+
return [
|
|
286
|
+
N(Math.atan2(g, M)),
|
|
287
|
+
N(Math.atan2(b, Math.hypot(M, g))),
|
|
288
|
+
t[2] + (n[2] - t[2]) * e
|
|
289
|
+
];
|
|
290
|
+
}
|
|
291
|
+
function st(t = [], n = 0) {
|
|
292
|
+
if (!t.length) return null;
|
|
293
|
+
const e = f(u(n, 0), 0, 1);
|
|
294
|
+
if (t.length === 1)
|
|
295
|
+
return { progress: e, index: 0, nextIndex: 0, segmentProgress: 0, position: [...t[0].position], track: t[0] };
|
|
296
|
+
const i = F(t);
|
|
297
|
+
let s = i.length - 2;
|
|
298
|
+
for (let h = 1; h < i.length; h += 1)
|
|
299
|
+
if (e < i[h]) {
|
|
300
|
+
s = h - 1;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
e >= 1 && (s = t.length - 1);
|
|
304
|
+
const o = Math.min(s + 1, t.length - 1), r = i[s], l = i[o], a = o === s ? 0 : f((e - r) / Math.max(l - r, Number.EPSILON), 0, 1);
|
|
305
|
+
return {
|
|
306
|
+
progress: e,
|
|
307
|
+
index: s,
|
|
308
|
+
nextIndex: o,
|
|
309
|
+
segmentProgress: a,
|
|
310
|
+
position: H(t[s].position, t[o].position, a),
|
|
311
|
+
track: t[s]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function ot(t = [], n = 0) {
|
|
315
|
+
if (!t.length) return 0;
|
|
316
|
+
const e = f(Math.floor(u(n, 0)), 0, t.length - 1);
|
|
317
|
+
return F(t)[e] || 0;
|
|
318
|
+
}
|
|
319
|
+
function rt(t = [], n) {
|
|
320
|
+
if (!t.length) return -1;
|
|
321
|
+
const e = Date.parse(n);
|
|
322
|
+
return Number.isFinite(e) ? t.reduce((i, s, o) => {
|
|
323
|
+
if (s.timestamp === null) return i;
|
|
324
|
+
const r = Math.abs(s.timestamp - e), l = t[i].timestamp === null ? Number.POSITIVE_INFINITY : Math.abs(t[i].timestamp - e);
|
|
325
|
+
return r < l ? o : i;
|
|
326
|
+
}, 0) : -1;
|
|
327
|
+
}
|
|
328
|
+
function z(t, n, e, i) {
|
|
329
|
+
const s = i / 6371, o = m(e), r = m(n), l = m(t), a = Math.asin(
|
|
330
|
+
Math.sin(r) * Math.cos(s) + Math.cos(r) * Math.sin(s) * Math.cos(o)
|
|
331
|
+
), h = l + Math.atan2(
|
|
332
|
+
Math.sin(o) * Math.sin(s) * Math.cos(r),
|
|
333
|
+
Math.cos(s) - Math.sin(r) * Math.sin(a)
|
|
334
|
+
);
|
|
335
|
+
return [(N(h) + 540) % 360 - 180, N(a)];
|
|
336
|
+
}
|
|
337
|
+
function at(t, n, e, i = 4) {
|
|
338
|
+
if (!c(e)) return [];
|
|
339
|
+
const s = [], o = f(Math.floor(u(i, 4)), 1, 30);
|
|
340
|
+
for (let r = 0; r < 360; r += o) {
|
|
341
|
+
const l = r < 90 ? e.ne : r < 180 ? e.se : r < 270 ? e.sw : e.nw;
|
|
342
|
+
s.push(z(t, n, r, u(l, 0)));
|
|
343
|
+
}
|
|
344
|
+
return s;
|
|
345
|
+
}
|
|
346
|
+
function lt(t, n, e, i = 6) {
|
|
347
|
+
const s = [], o = f(Math.floor(u(i, 6)), 1, 30);
|
|
348
|
+
for (let r = 0; r < 360; r += o)
|
|
349
|
+
s.push(z(t, n, r, Math.max(u(e, 0), 0)));
|
|
350
|
+
return s;
|
|
351
|
+
}
|
|
352
|
+
function ut(t = [], n = []) {
|
|
353
|
+
const e = t.map((i) => i.position);
|
|
354
|
+
return n.forEach((i) => i.points.forEach((s) => e.push(s.position))), e.length ? e.reduce((i, s) => ({
|
|
355
|
+
west: Math.min(i.west, s[0]),
|
|
356
|
+
south: Math.min(i.south, s[1]),
|
|
357
|
+
east: Math.max(i.east, s[0]),
|
|
358
|
+
north: Math.max(i.north, s[1])
|
|
359
|
+
}), { west: 1 / 0, south: 1 / 0, east: -1 / 0, north: -1 / 0 }) : null;
|
|
360
|
+
}
|
|
361
|
+
function ht(t = {}, n = {}) {
|
|
362
|
+
const e = [t.code, t.name].filter(Boolean).join(" "), i = [n.stage || n.level, n.windSpeed === null ? "" : `${n.windSpeed}m/s`].filter(Boolean).join(" ");
|
|
363
|
+
return [e, i].filter(Boolean).join(`
|
|
364
|
+
`);
|
|
365
|
+
}
|
|
366
|
+
function Z(t = [-180, 180]) {
|
|
367
|
+
if (!Array.isArray(t) || t.length !== 2 || !(Number(t[0]) === -180 && Number(t[1]) === 180 || Number(t[0]) === 0 && Number(t[1]) === 360))
|
|
368
|
+
throw new Error("longitudeRange must be [-180, 180] or [0, 360].");
|
|
369
|
+
return t.map(Number);
|
|
370
|
+
}
|
|
371
|
+
function B(t, n = -180) {
|
|
372
|
+
return n + ((t - n) % 360 + 360) % 360;
|
|
373
|
+
}
|
|
374
|
+
function Q(t, n, e) {
|
|
375
|
+
return t + 360 * Math.round(((n + e) / 2 - t) / 360);
|
|
376
|
+
}
|
|
377
|
+
function X(t, n, e) {
|
|
378
|
+
const i = n - t, s = i / (e - 1), o = Math.max(1e-5, s * 1e-4);
|
|
379
|
+
return Math.abs(i - 360) <= o || Math.abs(i + s - 360) <= o ? 360 : 0;
|
|
380
|
+
}
|
|
381
|
+
function J(t, n, e, i = [-180, 180]) {
|
|
382
|
+
if (e) return { min: i[0], max: i[1] };
|
|
383
|
+
const s = B(t, i[0]);
|
|
384
|
+
return { min: s, max: s + n - t };
|
|
385
|
+
}
|
|
386
|
+
function ct(t, n, e, i = 0) {
|
|
387
|
+
if (i) return { ...t };
|
|
388
|
+
const s = 360 * Math.round((t.min + t.max - (n + e)) / 720), o = [];
|
|
389
|
+
for (const a of [s - 360, s, s + 360]) {
|
|
390
|
+
const h = Math.max(t.min, n + a), d = Math.min(t.max, e + a);
|
|
391
|
+
h < d && o.push({ min: h, max: d });
|
|
392
|
+
}
|
|
393
|
+
if (!o.length) return null;
|
|
394
|
+
const r = Math.min(...o.map((a) => a.min)), l = Math.max(...o.map((a) => a.max));
|
|
395
|
+
return e - n < l - r ? { min: n + s, max: e + s } : { min: r, max: l };
|
|
396
|
+
}
|
|
397
|
+
class dt {
|
|
398
|
+
constructor(n = {}, e = {}) {
|
|
399
|
+
this.rawData = n, this.bound = Array.isArray(n.Bound) ? n.Bound : [], this.data = Array.isArray(n.DataAry) ? n.DataAry : [], this.valid = !1, this.errorMessage = "", this.lonMin = 0, this.latMin = 0, this.lonMax = 0, this.latMax = 0, this.lonCount = 0, this.latCount = 0, this.lonSpan = 0, this.latSpan = 0, this.lonStep = 0, this.latStep = 0, this.valueScale = 1, this.longitudePeriod = 0, this.longitudeRange = [-180, 180], this.flipY = !1;
|
|
400
|
+
try {
|
|
401
|
+
this.longitudeRange = Z(e.longitudeRange);
|
|
402
|
+
} catch (i) {
|
|
403
|
+
this.errorMessage = i.message;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
this.bound.length ? this.parse() : this.parseComponents(e), this.valid && (this.longitudePeriod = X(this.lonMin, this.lonMax, this.lonCount));
|
|
407
|
+
}
|
|
408
|
+
parse() {
|
|
409
|
+
if (this.bound.length < 6) {
|
|
410
|
+
this.errorMessage = "Wind field Bound must contain at least 6 values.";
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const [n, e, i, s, o, r, l = 1] = this.bound, a = Number(i) * Number(s) * 2;
|
|
414
|
+
if (!Number.isFinite(Number(n)) || !Number.isFinite(Number(e)) || Number(i) < 2 || Number(s) < 2 || Number(o) <= 0 || Number(r) <= 0 || this.data.length !== a) {
|
|
415
|
+
this.errorMessage = `Invalid wind field data. Expected DataAry length ${a}, got ${this.data.length}.`;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
this.lonMin = Number(n), this.latMin = Number(e), this.lonCount = Number(i), this.latCount = Number(s), this.lonSpan = Number(o), this.latSpan = Number(r), this.valueScale = Number(l) || 1, this.lonMax = this.lonMin + this.lonSpan, this.latMax = this.latMin + this.latSpan, this.lonStep = this.lonSpan / (this.lonCount - 1), this.latStep = this.latSpan / (this.latCount - 1), this.valid = !0;
|
|
419
|
+
}
|
|
420
|
+
parseComponents(n) {
|
|
421
|
+
const { width: e, height: i, bounds: s = {} } = this.rawData, o = this.rawData.u || this.rawData.U, r = this.rawData.v || this.rawData.V;
|
|
422
|
+
this.u = (o == null ? void 0 : o.array) || o, this.v = (r == null ? void 0 : r.array) || r, this.lonMin = Number(s.west), this.lonMax = Number(s.east), this.latMin = Number(s.south), this.latMax = Number(s.north), this.lonMax < this.lonMin && (this.lonMax += 360), this.lonCount = Number(e), this.latCount = Number(i), this.lonSpan = this.lonMax - this.lonMin, this.latSpan = this.latMax - this.latMin;
|
|
423
|
+
const l = (a) => Array.isArray(a) || ArrayBuffer.isView(a);
|
|
424
|
+
if (!Number.isInteger(this.lonCount) || this.lonCount < 2 || !Number.isInteger(this.latCount) || this.latCount < 2 || ![this.lonMin, this.lonMax, this.latMin, this.latMax].every(Number.isFinite) || this.lonSpan <= 0 || this.latSpan <= 0 || !l(this.u) || !l(this.v) || this.u.length !== this.lonCount * this.latCount || this.v.length !== this.u.length) {
|
|
425
|
+
this.errorMessage = "U/V wind data requires width, height, bounds, u and v.";
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.lonStep = this.lonSpan / (this.lonCount - 1), this.latStep = this.latSpan / (this.latCount - 1), this.flipY = n.flipY === !0 || this.rawData.rowOrder === "north-to-south", this.valid = !0;
|
|
429
|
+
}
|
|
430
|
+
normalizeLongitude(n) {
|
|
431
|
+
return this.longitudePeriod ? B(n, this.lonMin) : Q(n, this.lonMin, this.lonMax);
|
|
432
|
+
}
|
|
433
|
+
contains(n, e) {
|
|
434
|
+
const i = this.normalizeLongitude(n);
|
|
435
|
+
return Number.isFinite(n) && Number.isFinite(e) && (this.longitudePeriod > 0 || i >= this.lonMin && i <= this.lonMax) && e >= this.latMin && e <= this.latMax;
|
|
436
|
+
}
|
|
437
|
+
getBounds() {
|
|
438
|
+
const n = J(
|
|
439
|
+
this.lonMin,
|
|
440
|
+
this.lonMax,
|
|
441
|
+
this.longitudePeriod,
|
|
442
|
+
this.longitudeRange
|
|
443
|
+
);
|
|
444
|
+
return {
|
|
445
|
+
west: n.min,
|
|
446
|
+
south: this.latMin,
|
|
447
|
+
east: n.max,
|
|
448
|
+
north: this.latMax
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
getCenter() {
|
|
452
|
+
const n = this.getBounds();
|
|
453
|
+
return {
|
|
454
|
+
lon: (n.west + n.east) / 2,
|
|
455
|
+
lat: this.latMin + this.latSpan / 2
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
getVector(n, e) {
|
|
459
|
+
if (this.u) {
|
|
460
|
+
const o = (this.flipY ? this.latCount - n - 1 : n) * this.lonCount + e;
|
|
461
|
+
return { u: Number(this.u[o]), v: Number(this.v[o]) };
|
|
462
|
+
}
|
|
463
|
+
const i = (n * this.lonCount + e) * 2;
|
|
464
|
+
return {
|
|
465
|
+
u: Number(this.data[i]) / this.valueScale,
|
|
466
|
+
v: Number(this.data[i + 1]) / this.valueScale
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* 双线性插值。
|
|
471
|
+
*
|
|
472
|
+
* row 始终按从南到北计算;U/V 的北到南行序在 getVector 中换算。
|
|
473
|
+
* 全球网格跨末列与首列插值,不把 0…359 的最后一度当成无数据区域。
|
|
474
|
+
*/
|
|
475
|
+
sample(n, e) {
|
|
476
|
+
if (!this.valid || !this.contains(n, e))
|
|
477
|
+
return null;
|
|
478
|
+
const i = (this.normalizeLongitude(n) - this.lonMin) / this.lonStep, s = (e - this.latMin) / this.latStep, o = this.longitudePeriod ? Math.round(this.longitudePeriod / this.lonStep) : this.lonCount, r = Math.max(0, Math.min(Math.floor(i), o - (this.longitudePeriod ? 1 : 2))), l = Math.max(0, Math.min(Math.floor(s), this.latCount - 2)), a = this.longitudePeriod ? (r + 1) % o : r + 1, h = l + 1, d = Math.max(0, Math.min(i - r, 1)), M = Math.max(0, Math.min(s - l, 1)), g = this.getVector(l, r), b = this.getVector(l, a), x = this.getVector(h, r), I = this.getVector(h, a), E = 1 - d, A = 1 - M, T = E * A, O = d * A, P = E * M, D = d * M, S = g.u * T + b.u * O + x.u * P + I.u * D, w = g.v * T + b.v * O + x.v * P + I.v * D;
|
|
479
|
+
return !Number.isFinite(S) || !Number.isFinite(w) ? null : {
|
|
480
|
+
u: S,
|
|
481
|
+
v: w,
|
|
482
|
+
speed: Math.hypot(S, w)
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
export {
|
|
487
|
+
C as R,
|
|
488
|
+
dt as W,
|
|
489
|
+
at as a,
|
|
490
|
+
lt as b,
|
|
491
|
+
st as c,
|
|
492
|
+
ut as d,
|
|
493
|
+
Q as e,
|
|
494
|
+
ht as f,
|
|
495
|
+
ot as g,
|
|
496
|
+
X as h,
|
|
497
|
+
J as i,
|
|
498
|
+
ct as j,
|
|
499
|
+
Z as k,
|
|
500
|
+
U as l,
|
|
501
|
+
et as m,
|
|
502
|
+
it as n,
|
|
503
|
+
rt as r,
|
|
504
|
+
k as s,
|
|
505
|
+
tt as u,
|
|
506
|
+
nt as v,
|
|
507
|
+
B as w
|
|
508
|
+
};
|