@idm-plugin/geo 2.2.2 → 2.2.4
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/dist/index.js +999 -920
- package/dist/index.umd.cjs +6 -6
- package/dist/lane/src/index.d.ts +14 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as T from "@turf/turf";
|
|
2
2
|
import b from "moment";
|
|
3
|
-
import
|
|
3
|
+
import G from "@log4js-node/log4js-api";
|
|
4
4
|
import "moment-timezone";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import V from "tz-lookup";
|
|
6
|
+
import J from "shpjs";
|
|
7
7
|
class d {
|
|
8
8
|
/**
|
|
9
9
|
* 基于输入的经度,计算出时区
|
|
10
10
|
* @param lng
|
|
11
11
|
* @param lat
|
|
12
12
|
*/
|
|
13
|
-
static guessTimeZoneOffset(
|
|
14
|
-
|
|
15
|
-
const n =
|
|
16
|
-
return d.roundPrecision(
|
|
13
|
+
static guessTimeZoneOffset(e, t) {
|
|
14
|
+
e = d.convertToStdLng(e);
|
|
15
|
+
const n = V(t, e), s = b().tz(n).utcOffset();
|
|
16
|
+
return d.roundPrecision(s / 60, 1);
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* 将时间offset转换为时区,例如:8.5 => +08:30
|
|
20
20
|
* @param offset => 8.5
|
|
21
21
|
* @return timezone => +08:30
|
|
22
22
|
*/
|
|
23
|
-
static prettyTimeZoneOffset(
|
|
24
|
-
let
|
|
25
|
-
return n = n > 9 ? n : `0${n}`,
|
|
23
|
+
static prettyTimeZoneOffset(e) {
|
|
24
|
+
let t = Math.floor(Math.abs(e)), n = Math.round((Math.abs(e) - t) * 60);
|
|
25
|
+
return n = n > 9 ? n : `0${n}`, t = t > 9 ? t : `0${t}`, e > 0 ? `+${t}:${n}` : `-${t}:${n}`;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* @param lng
|
|
29
29
|
* @param precision 3位小数在百米级,4位小数在十米级, 5位小数在米级
|
|
30
30
|
* @param format
|
|
31
31
|
*/
|
|
32
|
-
static lng2pretty(
|
|
33
|
-
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
let o =
|
|
37
|
-
|
|
38
|
-
const
|
|
32
|
+
static lng2pretty(e, t = 6, n = "H°M′") {
|
|
33
|
+
e = d.convertToStdLng(e, t);
|
|
34
|
+
let s = "E";
|
|
35
|
+
e < 0 && (s = "W"), e = Math.abs(e), n = n.toUpperCase();
|
|
36
|
+
let o = e * 3600, i, r, c, a, l, u;
|
|
37
|
+
i = o % 3600 % 60, n.indexOf("S") !== -1 && (o = o - i, r = d.padNumber(i, 2, 2)), c = o / 60 % 60, n.indexOf("M") !== -1 && (n.indexOf("S") !== -1 ? a = d.roundPrecision(c, t).toString().padStart(2, "0") : a = d.padNumber(c, 2, 3), o = o - c * 60), l = o / 3600, n.indexOf("M") !== -1 ? u = d.roundPrecision(l, t).toString().padStart(3, "0") : u = d.padNumber(l, 3, 6), Number(r) >= 60 && (a = Number(a) + 1, r = 0), Number(a) >= 60 && (u = Number(u) + 1, a = 0);
|
|
38
|
+
const f = `${n.replace(/S+/gi, r).replace(/M+/gi, a).replace(/H+/gi, u)}${s}`;
|
|
39
39
|
return {
|
|
40
|
-
direction:
|
|
41
|
-
degree: d.roundPrecision(
|
|
42
|
-
minute: d.roundPrecision(c,
|
|
43
|
-
second: d.roundPrecision(
|
|
44
|
-
pretty:
|
|
45
|
-
S:
|
|
40
|
+
direction: s,
|
|
41
|
+
degree: d.roundPrecision(l, t),
|
|
42
|
+
minute: d.roundPrecision(c, t),
|
|
43
|
+
second: d.roundPrecision(i, t),
|
|
44
|
+
pretty: f,
|
|
45
|
+
S: r,
|
|
46
46
|
M: a,
|
|
47
|
-
H:
|
|
47
|
+
H: u
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
@@ -53,104 +53,104 @@ class d {
|
|
|
53
53
|
* @param precision 精确度
|
|
54
54
|
* @param format 格式化
|
|
55
55
|
*/
|
|
56
|
-
static lat2pretty(
|
|
57
|
-
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
let o =
|
|
61
|
-
|
|
62
|
-
const
|
|
56
|
+
static lat2pretty(e, t = 6, n = "H°M′") {
|
|
57
|
+
e = e % 180;
|
|
58
|
+
let s = "N";
|
|
59
|
+
e < 0 && (s = "S"), e = Math.abs(e), n = n.toUpperCase();
|
|
60
|
+
let o = e * 3600, i, r, c, a, l, u;
|
|
61
|
+
i = o % 3600 % 60, n.indexOf("S") !== -1 && (o = o - i, r = d.padNumber(i, 2, 2)), c = o / 60 % 60, n.indexOf("M") !== -1 && (n.indexOf("S") !== -1 ? a = d.roundPrecision(c, t).toString().padStart(2, "0") : a = d.padNumber(c, 2, 3), o = o - c * 60), l = o / 3600, n.indexOf("M") !== -1 ? u = d.roundPrecision(l, t).toString().padStart(2, "0") : u = d.padNumber(l, 2, 6), Number(r) >= 60 && (a = Number(a) + 1, r = 0), Number(a) >= 60 && (u = Number(u) + 1, a = 0);
|
|
62
|
+
const f = `${n.replace(/S+/gi, r).replace(/M+/gi, a).replace(/H+/gi, u)}${s}`;
|
|
63
63
|
return {
|
|
64
|
-
direction:
|
|
65
|
-
degree: d.roundPrecision(
|
|
66
|
-
minute: d.roundPrecision(c,
|
|
67
|
-
second: d.roundPrecision(
|
|
68
|
-
pretty:
|
|
69
|
-
S:
|
|
64
|
+
direction: s,
|
|
65
|
+
degree: d.roundPrecision(l, t),
|
|
66
|
+
minute: d.roundPrecision(c, t),
|
|
67
|
+
second: d.roundPrecision(i, t),
|
|
68
|
+
pretty: f,
|
|
69
|
+
S: r,
|
|
70
70
|
M: a,
|
|
71
|
-
H:
|
|
71
|
+
H: u
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
static str2Lng(
|
|
74
|
+
static str2Lng(e, t = 6) {
|
|
75
75
|
let n;
|
|
76
|
-
if (isNaN(
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
const o =
|
|
81
|
-
let [
|
|
82
|
-
if (
|
|
83
|
-
const a = this.roundPrecision(
|
|
84
|
-
|
|
76
|
+
if (isNaN(e)) {
|
|
77
|
+
e = d.strReplace(e, "LNG");
|
|
78
|
+
const s = e[e.length - 1].toUpperCase();
|
|
79
|
+
e = e.substring(0, e.length - 1).trim();
|
|
80
|
+
const o = e.split(" ").filter((a) => a !== "").map((a) => Math.abs(Number(a)));
|
|
81
|
+
let [i, r, c] = o;
|
|
82
|
+
if (r = r || 0, r = r > 60 ? r / Math.pow(10, String(r).length - 2) : r, c = c || 0, c = c > 60 ? c / Math.pow(10, String(c).length - 2) : c, i > 360 && !r) {
|
|
83
|
+
const a = this.roundPrecision(i / 100, 0);
|
|
84
|
+
r = i - a * 100, i = a;
|
|
85
85
|
}
|
|
86
|
-
n =
|
|
86
|
+
n = i + r / 60 + c / 3600, s === "W" && (n = n * -1);
|
|
87
87
|
} else
|
|
88
|
-
n = Number(
|
|
89
|
-
return d.convertToStdLng(n,
|
|
88
|
+
n = Number(e);
|
|
89
|
+
return d.convertToStdLng(n, t);
|
|
90
90
|
}
|
|
91
|
-
static str2Lat(
|
|
91
|
+
static str2Lat(e, t = 6) {
|
|
92
92
|
let n;
|
|
93
|
-
if (isNaN(
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const o =
|
|
98
|
-
let [
|
|
99
|
-
if (c = c || 0,
|
|
100
|
-
const a = this.roundPrecision(
|
|
101
|
-
|
|
93
|
+
if (isNaN(e)) {
|
|
94
|
+
e = d.strReplace(e, "LAT");
|
|
95
|
+
const s = e[e.length - 1].toUpperCase();
|
|
96
|
+
e = e.substring(0, e.length - 1).trim();
|
|
97
|
+
const o = e.split(" ").filter((a) => a !== "").map((a) => Math.abs(Number(a)));
|
|
98
|
+
let [i, r, c] = o;
|
|
99
|
+
if (c = c || 0, r = r || 0, r = r > 60 ? r / Math.pow(10, String(r).length - 2) : r, c = c > 60 ? c / Math.pow(10, String(c).length - 2) : c, i > 90 && !r) {
|
|
100
|
+
const a = this.roundPrecision(i / 100, 0);
|
|
101
|
+
r = i - a * 100, i = a;
|
|
102
102
|
}
|
|
103
|
-
if (n =
|
|
104
|
-
throw new Error(`latitude out of range: ${
|
|
105
|
-
|
|
103
|
+
if (n = i + r / 60 + c / 3600, n > 90)
|
|
104
|
+
throw new Error(`latitude out of range: ${e}${s}`);
|
|
105
|
+
s === "S" && (n = n * -1);
|
|
106
106
|
} else
|
|
107
|
-
n = Number(
|
|
108
|
-
return d.roundPrecision(n,
|
|
109
|
-
}
|
|
110
|
-
static str2LngOrLat(
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
return ["N", "S"].includes(
|
|
114
|
-
lat: d.str2Lat(
|
|
107
|
+
n = Number(e);
|
|
108
|
+
return d.roundPrecision(n, t);
|
|
109
|
+
}
|
|
110
|
+
static str2LngOrLat(e, t = 6, n = "LAT") {
|
|
111
|
+
e = d.strReplace(e, n);
|
|
112
|
+
const s = e[e.length - 1].toUpperCase();
|
|
113
|
+
return ["N", "S"].includes(s) ? {
|
|
114
|
+
lat: d.str2Lat(e, t)
|
|
115
115
|
} : {
|
|
116
|
-
lng: d.str2Lng(
|
|
116
|
+
lng: d.str2Lng(e, t)
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
-
static convertToStdLng(
|
|
120
|
-
return
|
|
119
|
+
static convertToStdLng(e, t = 6) {
|
|
120
|
+
return e > 180 ? (e = e % 360, e = e > 180 ? e - 360 : e) : e < -180 && (e = e % 360, e = e < -180 ? e + 360 : e), d.roundPrecision(e, t);
|
|
121
121
|
}
|
|
122
|
-
static roundPrecision(
|
|
123
|
-
const n = Number("1".padEnd(
|
|
124
|
-
return Math.round(
|
|
122
|
+
static roundPrecision(e, t = 6) {
|
|
123
|
+
const n = Number("1".padEnd(t + 1, "0"));
|
|
124
|
+
return Math.round(e * n) / n;
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
127
|
* 转换为按经度单调增/减的坐标数组
|
|
128
128
|
* @param coordinates
|
|
129
129
|
*/
|
|
130
|
-
static convertToMonotonicLng2(
|
|
131
|
-
for (let
|
|
132
|
-
t
|
|
133
|
-
return
|
|
130
|
+
static convertToMonotonicLng2(e) {
|
|
131
|
+
for (let t = 1; t < e.length; t++)
|
|
132
|
+
e[t][0] += Math.round((e[t - 1][0] - e[t][0]) / 360) * 360;
|
|
133
|
+
return e;
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* 转移为按经度单调增/减的坐标数组
|
|
137
137
|
* @param coordinates
|
|
138
138
|
*/
|
|
139
|
-
static convertToMonotonicLng(
|
|
140
|
-
for (let
|
|
141
|
-
t
|
|
142
|
-
return
|
|
139
|
+
static convertToMonotonicLng(e) {
|
|
140
|
+
for (let t = 1; t < e.length; t++)
|
|
141
|
+
e[t].lng += Math.round((e[t - 1].lng - e[t].lng) / 360) * 360;
|
|
142
|
+
return e;
|
|
143
143
|
}
|
|
144
|
-
static strReplace(
|
|
145
|
-
|
|
146
|
-
const n =
|
|
144
|
+
static strReplace(e, t = "LAT") {
|
|
145
|
+
e = e.replace(/([0-9]+)\.([0-9]+\.[0-9]+)/g, "$1 $2").replace(/([0-9]+)-([0-9]+\.[0-9]+)/g, "$1 $2").replace(/°/, " ").replace(/(\d+)-(\d?)/g, "$1 $2").replace(/'/g, " ").replace(/′/g, " ").replace(/"/g, " ").replace(/∼/g, " ").replace(/°/g, " ").replace(/,/g, ".").replace(/^ /g, "").replace(/ $/g, "").trim();
|
|
146
|
+
const n = e[e.length - 1].toUpperCase();
|
|
147
147
|
if (!["N", "S", "E", "W"].includes(n)) {
|
|
148
|
-
const
|
|
148
|
+
const s = e, o = Number(s.split(" ")[0]);
|
|
149
149
|
if (isNaN(o))
|
|
150
|
-
throw new Error(`invalid Lat/Lng: ${
|
|
151
|
-
o >= 90 ?
|
|
150
|
+
throw new Error(`invalid Lat/Lng: ${e}`);
|
|
151
|
+
o >= 90 ? e = `${s}E` : o <= -90 ? e = `${s}W` : ["LAN", "LNG"].includes(t == null ? void 0 : t.toUpperCase()) ? e = `${s}${o > 0 ? "E" : "W"}` : e = `${s}${o > 0 ? "N" : "S"}`;
|
|
152
152
|
}
|
|
153
|
-
return
|
|
153
|
+
return e;
|
|
154
154
|
}
|
|
155
155
|
/**
|
|
156
156
|
* 数据格式
|
|
@@ -159,78 +159,78 @@ class d {
|
|
|
159
159
|
* @param intPrecision 整数位数
|
|
160
160
|
* @param dcmPrecision 小数位数
|
|
161
161
|
*/
|
|
162
|
-
static padNumber(
|
|
163
|
-
const
|
|
164
|
-
return
|
|
162
|
+
static padNumber(e, t = 2, n = 2) {
|
|
163
|
+
const s = d.roundPrecision(e - Math.trunc(e), n), o = s >= 1 ? Math.trunc(e + 1).toString().padStart(t, "0") : Math.trunc(e).toString().padStart(t, "0");
|
|
164
|
+
return s >= 1 ? o : n > 0 ? `${o}.${Math.trunc(s * Math.pow(10, n)).toString().padStart(n, "0")}` : o;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
class
|
|
168
|
-
static json2Str(
|
|
169
|
-
const
|
|
170
|
-
return `${
|
|
167
|
+
class K {
|
|
168
|
+
static json2Str(e) {
|
|
169
|
+
const t = e.type ? e.type[0].toUpperCase() : "A";
|
|
170
|
+
return `${e.lat}|${e.lng}|${e.positionTime}|${e.sog}|${e.cog}|${e.hdg}|${e.draught}|${t}|${JSON.stringify(e.meteo || {})}|${e.vendor}|${e.deleted}`;
|
|
171
171
|
}
|
|
172
|
-
static str2Json(
|
|
173
|
-
const [
|
|
172
|
+
static str2Json(e) {
|
|
173
|
+
const [t, n, s, o, i, r, c, a, l, u, f] = e.split("|");
|
|
174
174
|
return {
|
|
175
|
-
lat: Number(
|
|
175
|
+
lat: Number(t),
|
|
176
176
|
lng: Number(n),
|
|
177
|
-
positionTime: Number(
|
|
177
|
+
positionTime: Number(s),
|
|
178
178
|
sog: Number(o),
|
|
179
|
-
cog: Number(
|
|
180
|
-
hdg: Number(
|
|
179
|
+
cog: Number(i),
|
|
180
|
+
hdg: Number(r),
|
|
181
181
|
//@ts-ignore
|
|
182
182
|
draught: isNaN(c) ? null : Number(c),
|
|
183
183
|
type: a,
|
|
184
184
|
important: a !== "A",
|
|
185
|
-
meteo:
|
|
186
|
-
vendor:
|
|
187
|
-
deleted:
|
|
185
|
+
meteo: l ? JSON.parse(l) : void 0,
|
|
186
|
+
vendor: u,
|
|
187
|
+
deleted: f === "true"
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
|
-
static inspectStoppages(
|
|
191
|
-
const
|
|
192
|
-
|
|
190
|
+
static inspectStoppages(e, t = 1, n = !0) {
|
|
191
|
+
const s = e.at(0).positionTime < e.at(-1).positionTime;
|
|
192
|
+
s || e.sort((c, a) => c.positionTime - a.positionTime);
|
|
193
193
|
const o = [];
|
|
194
|
-
let
|
|
195
|
-
for (let c = 0; c <
|
|
196
|
-
const a =
|
|
194
|
+
let i, r;
|
|
195
|
+
for (let c = 0; c < e.length - 1; c++) {
|
|
196
|
+
const a = e[c];
|
|
197
197
|
if (!(n && ["N", "B", "E", "NOON", "BOSP", "EOSP"].includes(a.type))) {
|
|
198
|
-
for (let
|
|
199
|
-
const
|
|
198
|
+
for (let l = c + 1; l < e.length; l++) {
|
|
199
|
+
const u = e[l - 1], f = e[l];
|
|
200
200
|
if (n && ["N", "B", "E", "NOON", "BOSP", "EOSP"].includes(a.type))
|
|
201
201
|
continue;
|
|
202
|
-
const h =
|
|
203
|
-
if (v.calculateDistance(
|
|
204
|
-
|
|
202
|
+
const h = f.positionTime - u.positionTime;
|
|
203
|
+
if (v.calculateDistance(f, u, !0, 4) / (h / 3600) < t)
|
|
204
|
+
i || (i = a), l === e.length - 1 && (r = f, c = l);
|
|
205
205
|
else {
|
|
206
|
-
|
|
206
|
+
i && (r = e[l - 1], c = l);
|
|
207
207
|
break;
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
if ((
|
|
211
|
-
const
|
|
210
|
+
if ((r == null ? void 0 : r.positionTime) > (i == null ? void 0 : i.positionTime) && i) {
|
|
211
|
+
const l = {
|
|
212
212
|
start: {
|
|
213
|
+
lat: i.lat,
|
|
214
|
+
lng: i.lng,
|
|
215
|
+
sog: i.sog,
|
|
216
|
+
positionTime: i.positionTime,
|
|
217
|
+
utc: b.unix(i.positionTime).utc().format()
|
|
218
|
+
},
|
|
219
|
+
end: {
|
|
213
220
|
lat: r.lat,
|
|
214
221
|
lng: r.lng,
|
|
215
222
|
sog: r.sog,
|
|
216
223
|
positionTime: r.positionTime,
|
|
217
224
|
utc: b.unix(r.positionTime).utc().format()
|
|
218
225
|
},
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
sog: s.sog,
|
|
223
|
-
positionTime: s.positionTime,
|
|
224
|
-
utc: b.unix(s.positionTime).utc().format()
|
|
225
|
-
},
|
|
226
|
-
duration: s.positionTime - r.positionTime
|
|
227
|
-
}, l = t.filter((h) => h.positionTime >= u.start.positionTime && h.positionTime <= u.end.positionTime), g = v.divideAccordingToLng(l);
|
|
228
|
-
u.distance = v.calculateRouteDistance(g), u.hours = Math.round(u.duration / 3600 * 10) / 10, u.avgSog = Math.round(u.distance / u.hours * 10) / 10, o.push(u);
|
|
226
|
+
duration: r.positionTime - i.positionTime
|
|
227
|
+
}, u = e.filter((h) => h.positionTime >= l.start.positionTime && h.positionTime <= l.end.positionTime), f = v.divideAccordingToLng(u);
|
|
228
|
+
l.distance = v.calculateRouteDistance(f), l.hours = Math.round(l.duration / 3600 * 10) / 10, l.avgSog = Math.round(l.distance / l.hours * 10) / 10, o.push(l);
|
|
229
229
|
}
|
|
230
|
-
|
|
230
|
+
i = void 0, r = void 0;
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
|
-
return
|
|
233
|
+
return s || e.sort((c, a) => a.positionTime - c.positionTime), o;
|
|
234
234
|
}
|
|
235
235
|
/**
|
|
236
236
|
* 计算轨迹摘要
|
|
@@ -238,25 +238,70 @@ class J {
|
|
|
238
238
|
* @param stm
|
|
239
239
|
* @param etm
|
|
240
240
|
*/
|
|
241
|
-
static inspectSummary(
|
|
242
|
-
const
|
|
243
|
-
let
|
|
244
|
-
if (
|
|
245
|
-
for (let
|
|
246
|
-
const
|
|
247
|
-
|
|
241
|
+
static inspectSummary(e, t, n) {
|
|
242
|
+
const s = b(t), o = b(n), i = e.filter((l) => l.positionTime >= s.unix() && l.positionTime <= o.unix());
|
|
243
|
+
let r = 0, c = 0;
|
|
244
|
+
if (i.length > 1)
|
|
245
|
+
for (let l = 0; l < i.length - 1; l++) {
|
|
246
|
+
const u = i[l], f = i[l + 1];
|
|
247
|
+
r += v.calculateDistance(u, f, !0, 4), c += Math.abs(f.positionTime - u.positionTime);
|
|
248
248
|
}
|
|
249
|
-
|
|
250
|
-
const a = c ? Math.round(
|
|
251
|
-
return { distance:
|
|
249
|
+
r = Math.round(r * 100) / 100, c = Math.round(c / 3600 * 100) / 100;
|
|
250
|
+
const a = c ? Math.round(r / c * 100) / 100 : 0;
|
|
251
|
+
return { distance: r, interval: c, avgSpd: a };
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
|
-
let
|
|
254
|
+
let X;
|
|
255
255
|
try {
|
|
256
|
-
|
|
256
|
+
X = G.getLogger("meteo");
|
|
257
257
|
} catch {
|
|
258
258
|
} finally {
|
|
259
259
|
}
|
|
260
|
+
const Q = (() => {
|
|
261
|
+
const C = new Uint32Array(256);
|
|
262
|
+
for (let e = 0; e < 256; e++) {
|
|
263
|
+
let t = e;
|
|
264
|
+
for (let n = 0; n < 8; n++)
|
|
265
|
+
t = t & 1 ? 3988292384 ^ t >>> 1 : t >>> 1;
|
|
266
|
+
C[e] = t;
|
|
267
|
+
}
|
|
268
|
+
return C;
|
|
269
|
+
})();
|
|
270
|
+
function Y(C) {
|
|
271
|
+
let e = 4294967295;
|
|
272
|
+
for (let t = 0; t < C.length; t++)
|
|
273
|
+
e = Q[(e ^ C[t]) & 255] ^ e >>> 8;
|
|
274
|
+
return (e ^ 4294967295) >>> 0;
|
|
275
|
+
}
|
|
276
|
+
function _(C) {
|
|
277
|
+
const e = new TextEncoder(), t = [], n = [];
|
|
278
|
+
let s = 0;
|
|
279
|
+
for (const h of C) {
|
|
280
|
+
const g = e.encode(h.name), m = Y(h.data), p = new Uint8Array(30 + g.length), S = new DataView(p.buffer);
|
|
281
|
+
S.setUint32(0, 67324752, !0), S.setUint16(4, 20, !0), S.setUint16(8, 0, !0), S.setUint32(14, m, !0), S.setUint32(18, h.data.length, !0), S.setUint32(22, h.data.length, !0), S.setUint16(26, g.length, !0), p.set(g, 30), t.push({ nameBytes: g, data: h.data, crc: m, offset: s }), s += p.length + h.data.length, n.push(p, h.data);
|
|
282
|
+
}
|
|
283
|
+
const o = s, i = [];
|
|
284
|
+
let r = 0;
|
|
285
|
+
for (const h of t) {
|
|
286
|
+
const g = new Uint8Array(46 + h.nameBytes.length), m = new DataView(g.buffer);
|
|
287
|
+
m.setUint32(0, 33639248, !0), m.setUint16(4, 20, !0), m.setUint16(6, 20, !0), m.setUint16(10, 0, !0), m.setUint32(16, h.crc, !0), m.setUint32(20, h.data.length, !0), m.setUint32(24, h.data.length, !0), m.setUint16(28, h.nameBytes.length, !0), m.setUint32(42, h.offset, !0), g.set(h.nameBytes, 46), r += g.length, i.push(g);
|
|
288
|
+
}
|
|
289
|
+
const c = new Uint8Array(22), a = new DataView(c.buffer);
|
|
290
|
+
a.setUint32(0, 101010256, !0), a.setUint16(8, t.length, !0), a.setUint16(10, t.length, !0), a.setUint32(12, r, !0), a.setUint32(16, o, !0);
|
|
291
|
+
const l = n.reduce((h, g) => h + g.length, 0) + r + 22, u = new Uint8Array(l);
|
|
292
|
+
let f = 0;
|
|
293
|
+
for (const h of n)
|
|
294
|
+
u.set(h, f), f += h.length;
|
|
295
|
+
for (const h of i)
|
|
296
|
+
u.set(h, f), f += h.length;
|
|
297
|
+
return u.set(c, f), u;
|
|
298
|
+
}
|
|
299
|
+
function z(C) {
|
|
300
|
+
let e = "", t = C;
|
|
301
|
+
for (; t >= 0; )
|
|
302
|
+
e = String.fromCharCode(65 + t % 26) + e, t = Math.floor(t / 26) - 1;
|
|
303
|
+
return e;
|
|
304
|
+
}
|
|
260
305
|
class v {
|
|
261
306
|
/**
|
|
262
307
|
* 计算方位角
|
|
@@ -266,13 +311,13 @@ class v {
|
|
|
266
311
|
* @param precision
|
|
267
312
|
* @returns {number} 单位度
|
|
268
313
|
*/
|
|
269
|
-
static calculateBearing(
|
|
270
|
-
const o =
|
|
271
|
-
[
|
|
272
|
-
[
|
|
314
|
+
static calculateBearing(e, t, n = !0, s = 4) {
|
|
315
|
+
const o = T.points([
|
|
316
|
+
[e.lng, e.lat],
|
|
317
|
+
[t.lng, t.lat]
|
|
273
318
|
]);
|
|
274
|
-
let
|
|
275
|
-
return n ?
|
|
319
|
+
let i;
|
|
320
|
+
return n ? i = T.rhumbBearing(o.features[0], o.features[1]) : i = T.bearing(o.features[0], o.features[1]), i < 0 && (i += 360), d.roundPrecision(i, s);
|
|
276
321
|
}
|
|
277
322
|
/**
|
|
278
323
|
* 计算两点间距离
|
|
@@ -283,14 +328,14 @@ class v {
|
|
|
283
328
|
* @param units 单位,默认 nm(海里)
|
|
284
329
|
* @returns {number}
|
|
285
330
|
*/
|
|
286
|
-
static calculateDistance(
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
[
|
|
290
|
-
[
|
|
331
|
+
static calculateDistance(e, t, n = !0, s = 4, o = "nauticalmiles") {
|
|
332
|
+
e = { ...e }, t = { ...t }, e.lng = d.convertToStdLng(e.lng, s), t.lng = d.convertToStdLng(t.lng, s);
|
|
333
|
+
const i = T.points([
|
|
334
|
+
[e.lng, e.lat],
|
|
335
|
+
[t.lng, t.lat]
|
|
291
336
|
]);
|
|
292
|
-
let
|
|
293
|
-
return n ?
|
|
337
|
+
let r;
|
|
338
|
+
return n ? r = T.rhumbDistance(i.features[0], i.features[1], { units: o }) : r = T.distance(i.features[0], i.features[1], { units: o }), d.roundPrecision(r, s);
|
|
294
339
|
}
|
|
295
340
|
/**
|
|
296
341
|
* 计算航线距离
|
|
@@ -298,16 +343,16 @@ class v {
|
|
|
298
343
|
* @param precision
|
|
299
344
|
* @param units
|
|
300
345
|
*/
|
|
301
|
-
static calculateRouteDistance(
|
|
302
|
-
let
|
|
303
|
-
for (const
|
|
304
|
-
for (let
|
|
305
|
-
const c = { lng: r
|
|
306
|
-
|
|
307
|
-
const a = { lng: r
|
|
308
|
-
|
|
346
|
+
static calculateRouteDistance(e, t = 4, n = "nauticalmiles") {
|
|
347
|
+
let s = 0, o;
|
|
348
|
+
for (const i of e)
|
|
349
|
+
for (let r = 0; r < i.length - 1; r++) {
|
|
350
|
+
const c = { lng: i[r][0], lat: i[r][1] };
|
|
351
|
+
r === 0 && o && (s += this.calculateDistance(o, c, !0, t, n));
|
|
352
|
+
const a = { lng: i[r + 1][0], lat: i[r + 1][1] };
|
|
353
|
+
s += this.calculateDistance(c, a, !0, t, n), o = a;
|
|
309
354
|
}
|
|
310
|
-
return d.roundPrecision(
|
|
355
|
+
return d.roundPrecision(s, t);
|
|
311
356
|
}
|
|
312
357
|
/**
|
|
313
358
|
* 计算坐标(基于方位角和距离)
|
|
@@ -317,11 +362,11 @@ class v {
|
|
|
317
362
|
* @param units 单位,默认 nm(海里)
|
|
318
363
|
* @param rhumb
|
|
319
364
|
*/
|
|
320
|
-
static calculateCoordinate(
|
|
321
|
-
const
|
|
322
|
-
let
|
|
323
|
-
o ?
|
|
324
|
-
const c =
|
|
365
|
+
static calculateCoordinate(e, t, n, s = "nauticalmiles", o = !0) {
|
|
366
|
+
const i = T.point([e.lng, e.lat]);
|
|
367
|
+
let r;
|
|
368
|
+
o ? r = T.rhumbDestination(i, n, t, { units: s }) : r = T.destination(i, n, t, { units: s });
|
|
369
|
+
const c = r.geometry.coordinates;
|
|
325
370
|
return { lng: d.convertToStdLng(c[0], 8), lat: d.roundPrecision(c[1], 8) };
|
|
326
371
|
}
|
|
327
372
|
/**
|
|
@@ -333,13 +378,13 @@ class v {
|
|
|
333
378
|
* @param includeTail true 包含终点 to
|
|
334
379
|
* @param units 单位,默认 nm(海里)
|
|
335
380
|
*/
|
|
336
|
-
static interpolateCoordinates(
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
let
|
|
340
|
-
for (;
|
|
341
|
-
|
|
342
|
-
return o &&
|
|
381
|
+
static interpolateCoordinates(e, t, n, s = !0, o = !0, i = "nauticalmiles") {
|
|
382
|
+
const r = [], c = this.calculateBearing(e, t, !1), a = this.calculateDistance(e, t, !1, 8, i);
|
|
383
|
+
s && r.push({ lng: e.lng, lat: e.lat });
|
|
384
|
+
let l = 0;
|
|
385
|
+
for (; l < a; )
|
|
386
|
+
l += n, l < a && r.push(this.calculateCoordinate(e, c, l, i, !1));
|
|
387
|
+
return o && r.push({ lng: t.lng, lat: t.lat }), r;
|
|
343
388
|
}
|
|
344
389
|
/**
|
|
345
390
|
* 分组坐标(如相邻两个坐标经度差超180度,需以180为界将坐标分为两组)
|
|
@@ -353,80 +398,80 @@ class v {
|
|
|
353
398
|
* [[-170,40],[-160,30]]
|
|
354
399
|
* ]
|
|
355
400
|
*/
|
|
356
|
-
static divideAccordingToLng(
|
|
357
|
-
if ((
|
|
401
|
+
static divideAccordingToLng(e, t = !1, n = !0) {
|
|
402
|
+
if ((e == null ? void 0 : e.length) < 2)
|
|
358
403
|
return [];
|
|
359
|
-
|
|
360
|
-
let
|
|
404
|
+
e = n ? this.deduplicateCoordinates(e) : e;
|
|
405
|
+
let s = [];
|
|
361
406
|
const o = [];
|
|
362
|
-
let
|
|
363
|
-
for (let c = 0; c <
|
|
364
|
-
|
|
365
|
-
const a =
|
|
407
|
+
let i, r;
|
|
408
|
+
for (let c = 0; c < e.length - 1; c++) {
|
|
409
|
+
i = d.convertToStdLng(e[c].lng, 8), r = d.convertToStdLng(e[c + 1].lng, 8), e[c].lat = d.roundPrecision(e[c].lat, 8), e[c + 1].lat = d.roundPrecision(e[c + 1].lat, 8), s.push([i, e[c].lat]);
|
|
410
|
+
const a = i - r;
|
|
366
411
|
if (Math.abs(a) > 180) {
|
|
367
|
-
const
|
|
368
|
-
[
|
|
369
|
-
[
|
|
412
|
+
const l = d.convertToMonotonicLng2([
|
|
413
|
+
[i, e[c].lat],
|
|
414
|
+
[r, e[c + 1].lat]
|
|
370
415
|
]);
|
|
371
|
-
let
|
|
372
|
-
|
|
416
|
+
let u, f;
|
|
417
|
+
t ? (u = T.lineString(l), f = T.lineString([
|
|
373
418
|
[a > 0 ? 180 : -180, 89],
|
|
374
419
|
[a > 0 ? 180 : -180, -89]
|
|
375
|
-
])) : (
|
|
376
|
-
const h =
|
|
377
|
-
let
|
|
420
|
+
])) : (u = T.greatCircle(l[0], l[1]), f = T.greatCircle([a > 0 ? 180 : -180, 89], [a > 0 ? 180 : -180, -89]));
|
|
421
|
+
const h = T.lineIntersect(u, f);
|
|
422
|
+
let g;
|
|
378
423
|
if (h.features.length) {
|
|
379
|
-
const
|
|
380
|
-
|
|
424
|
+
const m = T.getCoord(h.features[0]);
|
|
425
|
+
g = d.roundPrecision(m[1], 8);
|
|
381
426
|
} else
|
|
382
|
-
|
|
383
|
-
a > 0 ? (
|
|
427
|
+
g = e[c].lat;
|
|
428
|
+
a > 0 ? (s.push([180 - 1e-6, g]), o.push([...s]), s = [], s.push([-(180 - 1e-6), g])) : (s.push([-(180 - 1e-6), g]), o.push([...s]), s = [], s.push([180 - 1e-6, g]));
|
|
384
429
|
}
|
|
385
|
-
c ===
|
|
430
|
+
c === e.length - 2 && s.push([r, e[c + 1].lat]);
|
|
386
431
|
}
|
|
387
|
-
return o.push(
|
|
432
|
+
return o.push(s), o;
|
|
388
433
|
}
|
|
389
434
|
/**
|
|
390
435
|
* 去除重复坐标
|
|
391
436
|
* @param route
|
|
392
437
|
*/
|
|
393
|
-
static deduplicateRoute(
|
|
394
|
-
const
|
|
395
|
-
for (const n of
|
|
396
|
-
const
|
|
397
|
-
|
|
438
|
+
static deduplicateRoute(e) {
|
|
439
|
+
const t = [];
|
|
440
|
+
for (const n of e) {
|
|
441
|
+
const s = n.reduce((o, i) => (o.findIndex((r) => r[0] === i[0] && r[1] === i[1]) === -1 && o.push(i), o), []);
|
|
442
|
+
t.push(s);
|
|
398
443
|
}
|
|
399
|
-
return
|
|
444
|
+
return t;
|
|
400
445
|
}
|
|
401
446
|
/**
|
|
402
447
|
* 去除重新坐标
|
|
403
448
|
* @param coordinates
|
|
404
449
|
*/
|
|
405
|
-
static deduplicateCoordinates(
|
|
406
|
-
return
|
|
450
|
+
static deduplicateCoordinates(e) {
|
|
451
|
+
return e.reduce((t, n) => (t.findIndex((s) => s.lat === n.lat && s.lng === n.lng) === -1 && t.push(n), t), []);
|
|
407
452
|
}
|
|
408
453
|
/**
|
|
409
454
|
* 移出坐标
|
|
410
455
|
* @param coordinate {lng, lat}
|
|
411
456
|
* @param route 航线[[[lng, lat],[lng, lat]]]
|
|
412
457
|
*/
|
|
413
|
-
static removeCoordinateFromRoute(
|
|
414
|
-
|
|
415
|
-
for (const n of
|
|
416
|
-
for (let
|
|
417
|
-
d.roundPrecision(n[
|
|
418
|
-
return
|
|
458
|
+
static removeCoordinateFromRoute(e, t) {
|
|
459
|
+
e.lng = d.convertToStdLng(e.lng, 8);
|
|
460
|
+
for (const n of t)
|
|
461
|
+
for (let s = n.length - 1; s >= 0; s--)
|
|
462
|
+
d.roundPrecision(n[s][0], 8) === e.lng && d.roundPrecision(n[s][1], 8) === d.roundPrecision(e.lat, 8) && n.splice(s, 1);
|
|
463
|
+
return t;
|
|
419
464
|
}
|
|
420
465
|
/**
|
|
421
466
|
* 移出坐标
|
|
422
467
|
* @param coordinate {lng, lat}
|
|
423
468
|
* @param waypoints [{lat, lng}, {lat, lng}]
|
|
424
469
|
*/
|
|
425
|
-
static removeCoordinateFromWaypoints(
|
|
426
|
-
|
|
427
|
-
for (let n =
|
|
428
|
-
d.roundPrecision(
|
|
429
|
-
return
|
|
470
|
+
static removeCoordinateFromWaypoints(e, t) {
|
|
471
|
+
e.lng = d.convertToStdLng(e.lng, 8);
|
|
472
|
+
for (let n = t.length - 1; n >= 0; n--)
|
|
473
|
+
d.roundPrecision(t[n].lng, 8) === e.lng && d.roundPrecision(t[n].lat, 8) === d.roundPrecision(e.lat, 8) && t.splice(n, 1);
|
|
474
|
+
return t;
|
|
430
475
|
}
|
|
431
476
|
/**
|
|
432
477
|
* 合并坐标进航线(基于坐标到线段最短距离边合并)
|
|
@@ -439,44 +484,44 @@ class v {
|
|
|
439
484
|
* @return
|
|
440
485
|
* [[[120, 30], [120, 35], [125,40], [130, 37]], [[-150, 40], [-130, 30]]]
|
|
441
486
|
*/
|
|
442
|
-
static mergeCoordinateToRoute(
|
|
443
|
-
|
|
444
|
-
let n = Number.MAX_VALUE,
|
|
445
|
-
return
|
|
446
|
-
for (let
|
|
447
|
-
const
|
|
448
|
-
n > h && (n = h, o =
|
|
487
|
+
static mergeCoordinateToRoute(e, t) {
|
|
488
|
+
e.lng = d.convertToStdLng(e.lng, 8);
|
|
489
|
+
let n = Number.MAX_VALUE, s = 0, o = 0, i, r;
|
|
490
|
+
return t.forEach((c, a) => {
|
|
491
|
+
for (let l = 0; l < c.length - 1; l++) {
|
|
492
|
+
const u = { lng: c[l][0], lat: c[l][1] }, f = { lng: c[l + 1][0], lat: c[l + 1][1] }, h = this.calculatePointToLineDistance(e, u, f);
|
|
493
|
+
n > h && (n = h, o = l, s = a, i = this.calculateDistance(u, e), r = this.calculateDistance(f, e));
|
|
449
494
|
}
|
|
450
|
-
}),
|
|
495
|
+
}), i !== 0 && r !== 0 ? t[s].splice(o + 1, 0, [e.lng, e.lat]) : i === 0 ? t[s].splice(o, 1, [e.lng, e.lat]) : r === 0 && t[s].splice(o + 1, 1, [e.lng, e.lat]), t;
|
|
451
496
|
}
|
|
452
497
|
/**
|
|
453
498
|
* 向Route尾加1个坐标
|
|
454
499
|
* @param coordinate
|
|
455
500
|
* @param route
|
|
456
501
|
*/
|
|
457
|
-
static appendCoordinateToRoute(
|
|
458
|
-
|
|
459
|
-
const n = v.convertRouteToCoordinates(
|
|
460
|
-
return n.push(
|
|
502
|
+
static appendCoordinateToRoute(e, t) {
|
|
503
|
+
e.lng = d.convertToStdLng(e.lng, 8);
|
|
504
|
+
const n = v.convertRouteToCoordinates(t);
|
|
505
|
+
return n.push(e), v.divideAccordingToLng(n);
|
|
461
506
|
}
|
|
462
507
|
/**
|
|
463
508
|
* 向route头加1个坐标
|
|
464
509
|
* @param coordinate
|
|
465
510
|
* @param route
|
|
466
511
|
*/
|
|
467
|
-
static unshiftCoordinateToRoute(
|
|
468
|
-
const n = v.convertRouteToCoordinates(
|
|
469
|
-
return n.unshift(
|
|
512
|
+
static unshiftCoordinateToRoute(e, t) {
|
|
513
|
+
const n = v.convertRouteToCoordinates(t);
|
|
514
|
+
return n.unshift(e), v.divideAccordingToLng(n);
|
|
470
515
|
}
|
|
471
516
|
/**
|
|
472
517
|
* 合并多个waypoints进航线
|
|
473
518
|
* @param waypoints [{lat, lng}, {lat, lng}]
|
|
474
519
|
* @param route 航线 [[[lng, lat],[lng, lat]]]
|
|
475
520
|
*/
|
|
476
|
-
static mergeWaypointsToRoute(
|
|
477
|
-
for (const n of
|
|
478
|
-
|
|
479
|
-
return
|
|
521
|
+
static mergeWaypointsToRoute(e, t) {
|
|
522
|
+
for (const n of e)
|
|
523
|
+
t = this.mergeCoordinateToRoute(n, t);
|
|
524
|
+
return t;
|
|
480
525
|
}
|
|
481
526
|
/**
|
|
482
527
|
* 计算区间航线
|
|
@@ -485,23 +530,23 @@ class v {
|
|
|
485
530
|
* @param route [[[lng, lat]]]
|
|
486
531
|
* @return [[[lng, lat]]]
|
|
487
532
|
*/
|
|
488
|
-
static calculateRangeRoute(
|
|
489
|
-
n = this.mergeWaypointsToRoute([
|
|
490
|
-
const
|
|
533
|
+
static calculateRangeRoute(e, t, n) {
|
|
534
|
+
n = this.mergeWaypointsToRoute([e, t], n);
|
|
535
|
+
const s = [];
|
|
491
536
|
let o = 0;
|
|
492
|
-
return n.forEach((
|
|
537
|
+
return n.forEach((i) => {
|
|
493
538
|
if (o === 2)
|
|
494
539
|
return;
|
|
495
|
-
const
|
|
496
|
-
for (const c of
|
|
497
|
-
if (d.roundPrecision(
|
|
498
|
-
|
|
540
|
+
const r = [];
|
|
541
|
+
for (const c of i) {
|
|
542
|
+
if (d.roundPrecision(t.lng, 8) === d.roundPrecision(c[0], 8) && d.roundPrecision(t.lat, 8) === d.roundPrecision(c[1], 8)) {
|
|
543
|
+
r.push(c), o === 0 && r.push([e.lng, e.lat]), o = 2;
|
|
499
544
|
break;
|
|
500
545
|
}
|
|
501
|
-
o === 1 ?
|
|
546
|
+
o === 1 ? r.push(c) : d.roundPrecision(e.lng, 8) === d.roundPrecision(c[0], 8) && d.roundPrecision(e.lat, 8) === d.roundPrecision(c[1], 8) && (o = 1, r.push(c));
|
|
502
547
|
}
|
|
503
|
-
|
|
504
|
-
}),
|
|
548
|
+
r.length && s.push(r);
|
|
549
|
+
}), s;
|
|
505
550
|
}
|
|
506
551
|
/**
|
|
507
552
|
* 计算from到to之间的航线
|
|
@@ -511,27 +556,27 @@ class v {
|
|
|
511
556
|
* @param waypoints
|
|
512
557
|
* @return [{lng, lat}]
|
|
513
558
|
*/
|
|
514
|
-
static calculateRangeWaypoints(
|
|
515
|
-
const o = this.convertRouteToCoordinates(n, 0),
|
|
516
|
-
(
|
|
517
|
-
), c =
|
|
518
|
-
(
|
|
559
|
+
static calculateRangeWaypoints(e, t, n, s = []) {
|
|
560
|
+
const o = this.convertRouteToCoordinates(n, 0), i = this.mergeCoordinatesToWaypoints([e, t], o.length ? o : s), r = i.findIndex(
|
|
561
|
+
(l) => d.roundPrecision(e.lng, 8) === d.roundPrecision(l.lng, 8) && d.roundPrecision(e.lat, 8) === d.roundPrecision(l.lat, 8)
|
|
562
|
+
), c = i.findIndex(
|
|
563
|
+
(l) => d.roundPrecision(t.lng, 8) === d.roundPrecision(l.lng, 8) && d.roundPrecision(t.lat, 8) === d.roundPrecision(l.lat, 8)
|
|
519
564
|
);
|
|
520
|
-
return
|
|
565
|
+
return i.filter((l, u) => u >= r && u <= c);
|
|
521
566
|
}
|
|
522
567
|
/**
|
|
523
568
|
* 计算坐标到航路上的最短距离
|
|
524
569
|
* @param from
|
|
525
570
|
* @param route
|
|
526
571
|
*/
|
|
527
|
-
static calculateMinDistanceToRoute(
|
|
528
|
-
let n = Number.MAX_VALUE,
|
|
529
|
-
return
|
|
530
|
-
for (let c = 0; c <
|
|
531
|
-
const a = { lng:
|
|
532
|
-
n >
|
|
572
|
+
static calculateMinDistanceToRoute(e, t) {
|
|
573
|
+
let n = Number.MAX_VALUE, s = 0, o = 0;
|
|
574
|
+
return t.forEach((i, r) => {
|
|
575
|
+
for (let c = 0; c < i.length - 1; c++) {
|
|
576
|
+
const a = { lng: i[c][0], lat: i[c][1] }, l = { lng: i[c + 1][0], lat: i[c + 1][1] }, u = this.calculatePointToLineDistance(e, a, l);
|
|
577
|
+
n > u && (n = u, s = c, o = r);
|
|
533
578
|
}
|
|
534
|
-
}), { minDist: n, segIndex: o, minIndex:
|
|
579
|
+
}), { minDist: n, segIndex: o, minIndex: s };
|
|
535
580
|
}
|
|
536
581
|
/**
|
|
537
582
|
* 计算子航线
|
|
@@ -539,23 +584,23 @@ class v {
|
|
|
539
584
|
* @param route [[[lng, lat]]] 剩余航线
|
|
540
585
|
* @return [[[lng, lat]]]
|
|
541
586
|
*/
|
|
542
|
-
static calculateSubRoute(
|
|
543
|
-
const n = v.convertRouteToCoordinates(
|
|
544
|
-
v.mergeCoordinateToWaypoints(
|
|
545
|
-
const { segIndex:
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
let
|
|
549
|
-
for (let c =
|
|
550
|
-
if (
|
|
587
|
+
static calculateSubRoute(e, t) {
|
|
588
|
+
const n = v.convertRouteToCoordinates(t);
|
|
589
|
+
v.mergeCoordinateToWaypoints(e, n, !0), t = v.divideAccordingToLng(n);
|
|
590
|
+
const { segIndex: s, minIndex: o } = this.calculateMinDistanceToRoute({ ...e }, t);
|
|
591
|
+
e.lng = d.convertToStdLng(e.lng);
|
|
592
|
+
const i = [];
|
|
593
|
+
let r = !0;
|
|
594
|
+
for (let c = s; c < t.length; c++)
|
|
595
|
+
if (r) {
|
|
551
596
|
const a = [];
|
|
552
|
-
a.push([
|
|
553
|
-
for (let
|
|
554
|
-
|
|
555
|
-
|
|
597
|
+
a.push([e.lng, e.lat]);
|
|
598
|
+
for (let l = o + 1; l < t[c].length; l++)
|
|
599
|
+
e.lng === t[c][l][0] && e.lat === t[c][l][1] || a.push(t[c][l]);
|
|
600
|
+
i.push(a), r = !1;
|
|
556
601
|
} else
|
|
557
|
-
|
|
558
|
-
return
|
|
602
|
+
i.push([...t[c]]);
|
|
603
|
+
return i;
|
|
559
604
|
}
|
|
560
605
|
/**
|
|
561
606
|
* 计算子途经点
|
|
@@ -563,21 +608,21 @@ class v {
|
|
|
563
608
|
* @param waypoints [{lng, lat}]
|
|
564
609
|
* @return [{lng, lat}]
|
|
565
610
|
*/
|
|
566
|
-
static calculateSubWaypoints(
|
|
567
|
-
let n = Number.MAX_VALUE,
|
|
568
|
-
for (let
|
|
569
|
-
const
|
|
570
|
-
if (this.calculateDistance(
|
|
571
|
-
return
|
|
572
|
-
if (this.calculateDistance(
|
|
573
|
-
return
|
|
574
|
-
const a = this.calculatePointToLineDistance(
|
|
575
|
-
n > a && (n = a,
|
|
611
|
+
static calculateSubWaypoints(e, t) {
|
|
612
|
+
let n = Number.MAX_VALUE, s = 0;
|
|
613
|
+
for (let i = 0; i < t.length - 1; i++) {
|
|
614
|
+
const r = t[i], c = t[i + 1];
|
|
615
|
+
if (this.calculateDistance(e, r) === 0)
|
|
616
|
+
return t;
|
|
617
|
+
if (this.calculateDistance(e, c) === 0)
|
|
618
|
+
return t.filter((l, u) => u > 0);
|
|
619
|
+
const a = this.calculatePointToLineDistance(e, r, c);
|
|
620
|
+
n > a && (n = a, s = i);
|
|
576
621
|
}
|
|
577
|
-
|
|
578
|
-
const o = [
|
|
579
|
-
for (let
|
|
580
|
-
o.push(
|
|
622
|
+
e.lng = d.convertToStdLng(e.lng);
|
|
623
|
+
const o = [e];
|
|
624
|
+
for (let i = s + 1; i < t.length; i++)
|
|
625
|
+
o.push(t[i]);
|
|
581
626
|
return o;
|
|
582
627
|
}
|
|
583
628
|
/**
|
|
@@ -587,38 +632,38 @@ class v {
|
|
|
587
632
|
* @param to { lng, lat }
|
|
588
633
|
* @param options
|
|
589
634
|
*/
|
|
590
|
-
static calculatePointToLineDistance(
|
|
591
|
-
|
|
592
|
-
const o = d.convertToMonotonicLng([
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
[
|
|
635
|
+
static calculatePointToLineDistance(e, t, n, s = { units: "nauticalmiles", method: "geodesic" }) {
|
|
636
|
+
e.lng = d.convertToStdLng(e.lng, 8), t = { ...t }, n = { ...n }, t.lng = d.convertToStdLng(t.lng, 8), n.lng = d.convertToStdLng(n.lng, 8);
|
|
637
|
+
const o = d.convertToMonotonicLng([t, n]);
|
|
638
|
+
t = o[0], n = o[1];
|
|
639
|
+
const i = T.lineString([
|
|
640
|
+
[t.lng, t.lat],
|
|
596
641
|
[n.lng, n.lat]
|
|
597
|
-
]),
|
|
598
|
-
return d.roundPrecision(Math.min(
|
|
642
|
+
]), r = T.pointToLineDistance(T.point([e.lng, e.lat]), i, s), c = T.pointToLineDistance(T.point([e.lng > 0 ? e.lng - 360 : e.lng + 360, e.lat]), i, s);
|
|
643
|
+
return d.roundPrecision(Math.min(r, c), 6);
|
|
599
644
|
}
|
|
600
645
|
/**
|
|
601
646
|
* 计算途经点的COG, Distance等属性
|
|
602
647
|
* @param waypoints
|
|
603
648
|
* @param route
|
|
604
649
|
*/
|
|
605
|
-
static calculateWaypointsPropInRoute(
|
|
606
|
-
|
|
607
|
-
for (let n = 0; n <
|
|
608
|
-
const
|
|
609
|
-
n === 0 && (
|
|
650
|
+
static calculateWaypointsPropInRoute(e, t) {
|
|
651
|
+
t = this.mergeWaypointsToRoute(e, t);
|
|
652
|
+
for (let n = 0; n < e.length - 1; n++) {
|
|
653
|
+
const s = e[n], o = e[n + 1], i = this.calculateRangeRoute(s, o, t);
|
|
654
|
+
n === 0 && (s.distanceFromPrevious = 0, s.distanceFromStart = 0), o.distanceFromPrevious = this.calculateRouteDistance(i), o.distanceFromStart = d.roundPrecision((s.distanceFromStart || 0) + o.distanceFromPrevious);
|
|
610
655
|
}
|
|
611
|
-
return
|
|
656
|
+
return e;
|
|
612
657
|
}
|
|
613
658
|
/**
|
|
614
659
|
* @param coordinates [{lng, lat}]
|
|
615
660
|
* @param waypoints [{lng, lat}]
|
|
616
661
|
* @param replace true replace the same waypoint with coordinate
|
|
617
662
|
*/
|
|
618
|
-
static mergeCoordinatesToWaypoints(
|
|
619
|
-
for (const
|
|
620
|
-
this.mergeCoordinateToWaypoints(
|
|
621
|
-
return
|
|
663
|
+
static mergeCoordinatesToWaypoints(e, t, n = !0) {
|
|
664
|
+
for (const s of e)
|
|
665
|
+
this.mergeCoordinateToWaypoints(s, t, n);
|
|
666
|
+
return t;
|
|
622
667
|
}
|
|
623
668
|
/**
|
|
624
669
|
* 合并坐标进航路途经点
|
|
@@ -632,24 +677,24 @@ class v {
|
|
|
632
677
|
* @return
|
|
633
678
|
* [{ lng: 160, lat: 30}, { lng: 170, lat: 40}, {lng: 179, lat: 50}, {lng: -170, lat: 40}, {lng: -160, lat: 30}]
|
|
634
679
|
*/
|
|
635
|
-
static mergeCoordinateToWaypoints(
|
|
636
|
-
|
|
637
|
-
let
|
|
638
|
-
if (
|
|
639
|
-
|
|
680
|
+
static mergeCoordinateToWaypoints(e, t, n = !0) {
|
|
681
|
+
e.lng = d.convertToStdLng(e.lng, 8);
|
|
682
|
+
let s = Number.MAX_VALUE, o = 0, i = 0, r = 0;
|
|
683
|
+
if (t.length < 2)
|
|
684
|
+
t.push(e);
|
|
640
685
|
else {
|
|
641
|
-
for (let c = 0; c <
|
|
642
|
-
const a = { lng:
|
|
643
|
-
|
|
686
|
+
for (let c = 0; c < t.length - 1; c++) {
|
|
687
|
+
const a = { lng: t[c].lng, lat: t[c].lat }, l = { lng: t[c + 1].lng, lat: t[c + 1].lat }, u = this.calculatePointToLineDistance(e, a, l);
|
|
688
|
+
s >= u && (s = u, o = c, i = this.calculateDistance(a, e, !1, 6), r = this.calculateDistance(l, e, !1, 6));
|
|
644
689
|
}
|
|
645
|
-
|
|
690
|
+
i !== 0 && r !== 0 ? i <= s && o === 0 ? t.unshift(e) : r <= s && o === t.length - 2 ? t.push(e) : t.splice(o + 1, 0, e) : i === 0 ? n ? t.splice(o, 1, e) : t.splice(o + 1, 0, e) : r === 0 && (n ? t.splice(o + 1, 1, e) : t.splice(o + 1, 0, e));
|
|
646
691
|
}
|
|
647
|
-
return
|
|
692
|
+
return t.map((c, a) => {
|
|
648
693
|
c.lng = d.convertToStdLng(c.lng);
|
|
649
|
-
const
|
|
650
|
-
if (
|
|
651
|
-
const
|
|
652
|
-
c.sog = d.roundPrecision(
|
|
694
|
+
const l = t[a + 1];
|
|
695
|
+
if (l && ((c.bearing === null || c.bearing === void 0) && ((c.positionTime || 0) > (l.positionTime || 0) ? c.bearing = this.calculateBearing(l, c, !0) : c.bearing = this.calculateBearing(c, l, !0)), c.cog = c.cog || c.bearing, !c.sog && c.positionTime && l.positionTime)) {
|
|
696
|
+
const u = this.calculateDistance(c, l, !0), f = Math.abs(l.positionTime - c.positionTime) / 3600;
|
|
697
|
+
c.sog = d.roundPrecision(u / f, 2);
|
|
653
698
|
}
|
|
654
699
|
return c;
|
|
655
700
|
});
|
|
@@ -661,25 +706,25 @@ class v {
|
|
|
661
706
|
* @param rhumb
|
|
662
707
|
* @param deduplicate
|
|
663
708
|
*/
|
|
664
|
-
static generateRouteAccordingToWaypoints(
|
|
665
|
-
const
|
|
666
|
-
for (let o = 1; o <
|
|
667
|
-
const
|
|
668
|
-
if (o === 1 &&
|
|
669
|
-
const c = this.interpolateCoordinates(
|
|
670
|
-
|
|
709
|
+
static generateRouteAccordingToWaypoints(e, t = !0, n = !0) {
|
|
710
|
+
const s = [];
|
|
711
|
+
for (let o = 1; o < e.length; o++) {
|
|
712
|
+
const i = e[o - 1], r = e[o];
|
|
713
|
+
if (o === 1 && s.push(i), r.gcToPrevious) {
|
|
714
|
+
const c = this.interpolateCoordinates(i, r, 200, !1, !0, "nauticalmiles");
|
|
715
|
+
s.push(...c);
|
|
671
716
|
} else
|
|
672
|
-
|
|
717
|
+
s.push(r);
|
|
673
718
|
}
|
|
674
|
-
return this.divideAccordingToLng(
|
|
719
|
+
return this.divideAccordingToLng(s, t, n);
|
|
675
720
|
}
|
|
676
721
|
/**
|
|
677
722
|
* 最近点(从route中找出距离目标点最近的点)
|
|
678
723
|
* @param coordinate 目标点 {lng, lat}
|
|
679
724
|
* @param route [[[lng, lat]]]
|
|
680
725
|
*/
|
|
681
|
-
static nearestCoordinateInRoute(
|
|
682
|
-
const n =
|
|
726
|
+
static nearestCoordinateInRoute(e, t) {
|
|
727
|
+
const n = T.point([e.lng, e.lat]), o = this.convertRouteToCoordinates(t).map((a) => [a.lng, a.lat]), i = T.lineString(o), r = T.nearestPointOnLine(i, n), c = T.getCoord(r);
|
|
683
728
|
return { lng: d.roundPrecision(c[0], 8), lat: d.roundPrecision(c[1], 8) };
|
|
684
729
|
}
|
|
685
730
|
/**
|
|
@@ -687,21 +732,21 @@ class v {
|
|
|
687
732
|
* @param from
|
|
688
733
|
* @param waypoints
|
|
689
734
|
*/
|
|
690
|
-
static calculatePrevWaypoint(
|
|
735
|
+
static calculatePrevWaypoint(e, t) {
|
|
691
736
|
let n = 0;
|
|
692
|
-
this.mergeCoordinateToWaypoints(
|
|
693
|
-
for (let
|
|
694
|
-
const o =
|
|
695
|
-
if (this.calculateDistance(
|
|
696
|
-
n =
|
|
737
|
+
this.mergeCoordinateToWaypoints(e, t);
|
|
738
|
+
for (let s = 0; s < t.length - 1; s++) {
|
|
739
|
+
const o = t[s], i = t[s + 1];
|
|
740
|
+
if (this.calculateDistance(e, o) === 0) {
|
|
741
|
+
n = s;
|
|
697
742
|
break;
|
|
698
743
|
}
|
|
699
|
-
if (this.calculateDistance(
|
|
700
|
-
n =
|
|
744
|
+
if (this.calculateDistance(e, i) === 0) {
|
|
745
|
+
n = s + 1;
|
|
701
746
|
break;
|
|
702
747
|
}
|
|
703
748
|
}
|
|
704
|
-
return
|
|
749
|
+
return t[n === 0 ? 0 : n - 1];
|
|
705
750
|
}
|
|
706
751
|
/**
|
|
707
752
|
* 计算下一个距离单位的坐标及其子航线
|
|
@@ -711,43 +756,43 @@ class v {
|
|
|
711
756
|
* @param units
|
|
712
757
|
* @return { coordinate: {lng, lat}, route: [[[lng, lat]]]}
|
|
713
758
|
*/
|
|
714
|
-
static calculateNextCoordinateAlongRoute(
|
|
715
|
-
var
|
|
716
|
-
const o =
|
|
717
|
-
let
|
|
718
|
-
if (
|
|
759
|
+
static calculateNextCoordinateAlongRoute(e, t, n, s = "nauticalmiles") {
|
|
760
|
+
var f;
|
|
761
|
+
const o = e.speed || 12, i = [];
|
|
762
|
+
let r = [], c = !1, a = 0, l = 0, u;
|
|
763
|
+
if (t && n.length ? (i.push(e), n.forEach((h, g) => {
|
|
719
764
|
if (c)
|
|
720
|
-
|
|
765
|
+
r.push(h);
|
|
721
766
|
else {
|
|
722
|
-
const
|
|
723
|
-
let
|
|
724
|
-
for (let
|
|
725
|
-
if (
|
|
726
|
-
|
|
767
|
+
const m = [];
|
|
768
|
+
let p;
|
|
769
|
+
for (let S = 0; S < h.length; S++)
|
|
770
|
+
if (u)
|
|
771
|
+
m.push(h[S]);
|
|
727
772
|
else {
|
|
728
|
-
|
|
729
|
-
const
|
|
730
|
-
if (a +=
|
|
731
|
-
|
|
773
|
+
p = { lng: h[S][0], lat: h[S][1] };
|
|
774
|
+
const x = this.calculateDistance(e, p, !0, 8, s);
|
|
775
|
+
if (a += x, a < t)
|
|
776
|
+
l += x, x && i.push(p), e = p;
|
|
732
777
|
else {
|
|
733
|
-
if (
|
|
734
|
-
|
|
778
|
+
if (l = t, a === t)
|
|
779
|
+
u = p, m.push([u.lng, u.lat]);
|
|
735
780
|
else {
|
|
736
|
-
const M = a -
|
|
737
|
-
|
|
781
|
+
const M = a - t, y = this.calculateBearing(p, e);
|
|
782
|
+
u = this.calculateCoordinate(p, y, M, s), m.push([u.lng, u.lat]), m.push([p.lng, p.lat]);
|
|
738
783
|
}
|
|
739
784
|
c = !0;
|
|
740
785
|
}
|
|
741
786
|
}
|
|
742
|
-
|
|
787
|
+
m.length && r.push(m), g === n.length - 1 && !u && (u = p);
|
|
743
788
|
}
|
|
744
|
-
})) : (
|
|
745
|
-
if (
|
|
746
|
-
const h = { lng:
|
|
747
|
-
|
|
789
|
+
})) : (r = n, u = { ...e }), u)
|
|
790
|
+
if (i.push(u), u.distanceFromPrevious = Math.round(l * 1e4) / 1e4, u.hourFromPrevious = Math.round(l / o * 1e4) / 1e4, ((f = r[0]) == null ? void 0 : f.length) > 1) {
|
|
791
|
+
const h = { lng: r[0][1][0], lat: r[0][1][1] };
|
|
792
|
+
u.bearing = this.calculateBearing(u, h);
|
|
748
793
|
} else
|
|
749
|
-
|
|
750
|
-
return { coordinate:
|
|
794
|
+
u.bearing = 0;
|
|
795
|
+
return { coordinate: u, nextRoute: r, prevRoute: i };
|
|
751
796
|
}
|
|
752
797
|
/**
|
|
753
798
|
* 返回最近点及其是否为垂足(最近点不是起点或终点)
|
|
@@ -755,35 +800,35 @@ class v {
|
|
|
755
800
|
* @param from {lng, lat}
|
|
756
801
|
* @param to {lng, lat}
|
|
757
802
|
*/
|
|
758
|
-
static nearestCoordinateInLine(
|
|
759
|
-
const
|
|
760
|
-
[
|
|
761
|
-
[
|
|
762
|
-
]), a =
|
|
763
|
-
return { lng:
|
|
803
|
+
static nearestCoordinateInLine(e, t, n) {
|
|
804
|
+
const s = d.convertToStdLng(e.lng, 6), o = T.point([s, e.lat]), i = d.convertToStdLng(t.lng, 6), r = d.convertToStdLng(n.lng, 6), c = T.lineString([
|
|
805
|
+
[i, t.lat],
|
|
806
|
+
[r, n.lat]
|
|
807
|
+
]), a = T.nearestPointOnLine(c, o), l = T.getCoord(a), u = d.roundPrecision(l[0], 6), f = d.roundPrecision(l[1], 6);
|
|
808
|
+
return { lng: u, lat: f, inline: !(u === i && f === t.lat) && !(u === r && f === n.lat) };
|
|
764
809
|
}
|
|
765
810
|
/**
|
|
766
811
|
* 将route转coordinate
|
|
767
812
|
* @param route
|
|
768
813
|
* @param distance 临近点过虑
|
|
769
814
|
*/
|
|
770
|
-
static convertRouteToCoordinates(
|
|
815
|
+
static convertRouteToCoordinates(e, t = 0) {
|
|
771
816
|
const n = [];
|
|
772
|
-
let
|
|
773
|
-
return
|
|
774
|
-
|
|
775
|
-
const c = { lng: d.roundPrecision(
|
|
817
|
+
let s, o;
|
|
818
|
+
return e.forEach((i) => {
|
|
819
|
+
i.forEach((r) => {
|
|
820
|
+
const c = { lng: d.roundPrecision(r[0], 8), lat: d.roundPrecision(r[1], 8) };
|
|
776
821
|
if (!o)
|
|
777
822
|
n.push(c), o = c;
|
|
778
823
|
else if (o.bearing === void 0 || o.bearing === null)
|
|
779
824
|
o.bearing = this.calculateBearing(o, c, !0);
|
|
780
825
|
else {
|
|
781
|
-
const a = this.calculateDistance(
|
|
782
|
-
a && a >=
|
|
826
|
+
const a = this.calculateDistance(s, c, !0);
|
|
827
|
+
a && a >= t && (s.bearing = this.calculateBearing(s, c, !0), n.push(s), o = s);
|
|
783
828
|
}
|
|
784
|
-
|
|
829
|
+
s = c;
|
|
785
830
|
});
|
|
786
|
-
}),
|
|
831
|
+
}), s && n.push(s), n;
|
|
787
832
|
}
|
|
788
833
|
/**
|
|
789
834
|
* 抽稀(基于转向点)
|
|
@@ -791,76 +836,76 @@ class v {
|
|
|
791
836
|
* @param waypoints [{ lng, lat, gcToPrevious }]
|
|
792
837
|
* @param distance
|
|
793
838
|
*/
|
|
794
|
-
static simplifyRouteToCoordinates(
|
|
795
|
-
let
|
|
796
|
-
return
|
|
839
|
+
static simplifyRouteToCoordinates(e, t, n = 1) {
|
|
840
|
+
let s = this.convertRouteToCoordinates(e, n);
|
|
841
|
+
return s = this.simplifyGCCoordinates(s, t), s;
|
|
797
842
|
}
|
|
798
843
|
/**
|
|
799
844
|
* 基于大圆标识抽稀
|
|
800
845
|
* @param coordinates
|
|
801
846
|
* @param waypoints
|
|
802
847
|
*/
|
|
803
|
-
static simplifyGCCoordinates(
|
|
804
|
-
|
|
805
|
-
this.mergeCoordinateToWaypoints(
|
|
848
|
+
static simplifyGCCoordinates(e, t) {
|
|
849
|
+
t.forEach((s) => {
|
|
850
|
+
this.mergeCoordinateToWaypoints(s, e, !0);
|
|
806
851
|
});
|
|
807
|
-
for (let
|
|
808
|
-
const o =
|
|
809
|
-
if (
|
|
810
|
-
const
|
|
811
|
-
for (let a = c - 1; a >
|
|
812
|
-
|
|
852
|
+
for (let s = 1; s < t.length; s++) {
|
|
853
|
+
const o = t[s - 1], i = t[s];
|
|
854
|
+
if (i.gcToPrevious) {
|
|
855
|
+
const r = e.findIndex((a) => a.lng === o.lng && a.lat === o.lat), c = e.findIndex((a) => a.lng === i.lng && a.lat === i.lat);
|
|
856
|
+
for (let a = c - 1; a > r; a--)
|
|
857
|
+
e.splice(a, 1);
|
|
813
858
|
}
|
|
814
859
|
}
|
|
815
860
|
let n = 0;
|
|
816
|
-
for (let
|
|
817
|
-
const o =
|
|
818
|
-
|
|
861
|
+
for (let s = 1; s < e.length; s++) {
|
|
862
|
+
const o = e[s - 1], i = e[s];
|
|
863
|
+
i.gcToPrevious ? (o.bearing = this.calculateBearing(o, i, !1), i.distanceFromPrevious = this.calculateDistance(o, i, !1)) : (o.bearing = this.calculateBearing(o, i, !0), i.distanceFromPrevious = this.calculateDistance(o, i, !0)), n = d.roundPrecision(n + i.distanceFromPrevious), i.distanceFromStart = n;
|
|
819
864
|
}
|
|
820
|
-
return
|
|
865
|
+
return e.map((s) => (s.lng = d.convertToStdLng(s.lng), s));
|
|
821
866
|
}
|
|
822
867
|
/**
|
|
823
868
|
* 计算轨迹中心点
|
|
824
869
|
* @param route
|
|
825
870
|
*/
|
|
826
|
-
static calculateCenter(
|
|
827
|
-
const
|
|
828
|
-
for (const
|
|
829
|
-
for (const c of
|
|
830
|
-
|
|
831
|
-
const n =
|
|
832
|
-
for (const
|
|
833
|
-
n.features.push(
|
|
834
|
-
const
|
|
835
|
-
return { lng: d.convertToStdLng(
|
|
871
|
+
static calculateCenter(e) {
|
|
872
|
+
const t = [];
|
|
873
|
+
for (const r of e)
|
|
874
|
+
for (const c of r)
|
|
875
|
+
t.push(c);
|
|
876
|
+
const n = T.featureCollection([]), s = d.convertToMonotonicLng2(t);
|
|
877
|
+
for (const r of s)
|
|
878
|
+
n.features.push(T.point(r));
|
|
879
|
+
const i = T.center(n).geometry.coordinates;
|
|
880
|
+
return { lng: d.convertToStdLng(i[0], 8), lat: d.roundPrecision(i[1], 8) };
|
|
836
881
|
}
|
|
837
882
|
/**
|
|
838
883
|
* 计算中心点
|
|
839
884
|
* @param coords
|
|
840
885
|
*/
|
|
841
|
-
static calculateCenter2(
|
|
842
|
-
const
|
|
843
|
-
return this.calculateCenter(
|
|
886
|
+
static calculateCenter2(e) {
|
|
887
|
+
const t = this.generateRouteAccordingToWaypoints(e);
|
|
888
|
+
return this.calculateCenter(t);
|
|
844
889
|
}
|
|
845
890
|
/**
|
|
846
891
|
* 计算BBox
|
|
847
892
|
* @param route
|
|
848
893
|
*/
|
|
849
|
-
static calculateBBox(
|
|
850
|
-
const
|
|
851
|
-
for (const o of
|
|
852
|
-
for (const
|
|
853
|
-
|
|
854
|
-
const n = d.convertToMonotonicLng2(
|
|
855
|
-
return
|
|
894
|
+
static calculateBBox(e) {
|
|
895
|
+
const t = [];
|
|
896
|
+
for (const o of e)
|
|
897
|
+
for (const i of o)
|
|
898
|
+
t.push(i);
|
|
899
|
+
const n = d.convertToMonotonicLng2(t), s = T.lineString(n);
|
|
900
|
+
return T.bbox(s);
|
|
856
901
|
}
|
|
857
902
|
/**
|
|
858
903
|
* 计算BBox
|
|
859
904
|
* @param coords
|
|
860
905
|
*/
|
|
861
|
-
static calculateBBox2(
|
|
862
|
-
const
|
|
863
|
-
return this.calculateBBox(
|
|
906
|
+
static calculateBBox2(e) {
|
|
907
|
+
const t = this.generateRouteAccordingToWaypoints(e);
|
|
908
|
+
return this.calculateBBox(t);
|
|
864
909
|
}
|
|
865
910
|
/**
|
|
866
911
|
* 抽稀 三点抽稀, 对比A,B,C; 基于三点距离及夹角抽稀
|
|
@@ -868,21 +913,21 @@ class v {
|
|
|
868
913
|
* @param distance 三点最小距离, 小于此距离时,B点将被忽略(三点太近)
|
|
869
914
|
* @param angle 三点夹角, 大于此角度时,B点将被忽略(近似走RL)
|
|
870
915
|
*/
|
|
871
|
-
static simplifyCoordinates(
|
|
872
|
-
const
|
|
873
|
-
for (let o = 1; o <
|
|
874
|
-
const
|
|
875
|
-
let a = !1,
|
|
876
|
-
if ((
|
|
877
|
-
const
|
|
878
|
-
Math.round(Math.acos(
|
|
916
|
+
static simplifyCoordinates(e, t = 1, n = 180) {
|
|
917
|
+
const s = [];
|
|
918
|
+
for (let o = 1; o < e.length; o++) {
|
|
919
|
+
const i = e[o - 1], r = e[o], c = e[o + 1];
|
|
920
|
+
let a = !1, l = !1;
|
|
921
|
+
if ((i.velocity || i.suspend || i.important || i.pilot || o === 1) && (a = !0, s.push(i)), r.gcToPrevious && (a || (a = !0, s.push(i)), l = !0, s.push(r), o++), c) {
|
|
922
|
+
const u = v.calculateDistance(i, r, !0), f = v.calculateDistance(r, c, !0), h = v.calculateDistance(i, c, !0), g = (Math.pow(u, 2) + Math.pow(f, 2) - Math.pow(h, 2)) / (2 * u * f);
|
|
923
|
+
Math.round(Math.acos(g) * 180 / Math.PI) < n && h > t && !l && (s.push(r), o++);
|
|
879
924
|
}
|
|
880
|
-
if (o >=
|
|
881
|
-
const
|
|
882
|
-
|
|
925
|
+
if (o >= e.length - 1) {
|
|
926
|
+
const u = e.at(-1);
|
|
927
|
+
u && s.push(u);
|
|
883
928
|
}
|
|
884
929
|
}
|
|
885
|
-
return
|
|
930
|
+
return s;
|
|
886
931
|
}
|
|
887
932
|
/**
|
|
888
933
|
* 在时间轨迹上寻找ts最近的点
|
|
@@ -890,78 +935,78 @@ class v {
|
|
|
890
935
|
* @param step 时间步长, 前后n小时
|
|
891
936
|
* @param waypoints 带时间的轨迹, 单位秒
|
|
892
937
|
*/
|
|
893
|
-
static nearestTSPointInWaypoints(
|
|
894
|
-
const
|
|
895
|
-
(
|
|
938
|
+
static nearestTSPointInWaypoints(e, t, n) {
|
|
939
|
+
const s = b.unix(e), o = n.filter(
|
|
940
|
+
(i) => s.clone().subtract(t, "hour").unix() <= (i.positionTime || 0) && s.clone().add(t, "h").unix() >= (i.positionTime || 0)
|
|
896
941
|
);
|
|
897
|
-
return o.sort((
|
|
942
|
+
return o.sort((i, r) => (i.positionTime || 0) - (r.positionTime || 0)), o.at(-1);
|
|
898
943
|
}
|
|
899
944
|
/**
|
|
900
945
|
* 推测船位
|
|
901
946
|
* @param ts 目标时间的船位,单位 s
|
|
902
947
|
* @param positions 带时间(positionTime)的轨迹
|
|
903
948
|
*/
|
|
904
|
-
static deadReckoning(
|
|
905
|
-
var o,
|
|
906
|
-
|
|
907
|
-
const n = b.unix(
|
|
908
|
-
let
|
|
909
|
-
if (!
|
|
910
|
-
const a = (
|
|
911
|
-
if (a &&
|
|
912
|
-
const
|
|
913
|
-
|
|
949
|
+
static deadReckoning(e, t) {
|
|
950
|
+
var o, i, r, c;
|
|
951
|
+
e > 1e12 && (e = Math.round(e / 1e3));
|
|
952
|
+
const n = b.unix(e);
|
|
953
|
+
let s = t.find((a) => a.positionTime === n.unix());
|
|
954
|
+
if (!s) {
|
|
955
|
+
const a = (i = (o = t.filter((u) => (u == null ? void 0 : u.positionTime) < n.unix())) == null ? void 0 : o.sort((u, f) => (u.positionTime || 0) - (f.positionTime || 0))) == null ? void 0 : i.at(-1), l = (c = (r = t.filter((u) => (u == null ? void 0 : u.positionTime) > n.unix())) == null ? void 0 : r.sort((u, f) => (u.positionTime || 0) - (f.positionTime || 0))) == null ? void 0 : c.at(0);
|
|
956
|
+
if (a && l) {
|
|
957
|
+
const u = v.calculateBearing(a, l, !0), f = v.calculateDistance(a, l), h = (n.unix() - a.positionTime) / (l.positionTime - a.positionTime);
|
|
958
|
+
s = v.calculateCoordinate(a, u, f * h), s.positionTime = n.unix(), s.utc = n.utc().format(), s.cog = u, s.sog = Math.round(f / ((l.positionTime - a.positionTime) / 3600) * 100) / 100;
|
|
914
959
|
} else
|
|
915
|
-
|
|
960
|
+
s = a || l, s && (s.utc = b.unix(s == null ? void 0 : s.positionTime).utc().format());
|
|
916
961
|
}
|
|
917
|
-
return
|
|
962
|
+
return s;
|
|
918
963
|
}
|
|
919
964
|
/**
|
|
920
965
|
* 推测船位时间
|
|
921
966
|
* @param coordinate
|
|
922
967
|
* @param positions
|
|
923
968
|
*/
|
|
924
|
-
static deadReckoningTime(
|
|
925
|
-
|
|
926
|
-
let n = Number.MAX_SAFE_INTEGER,
|
|
927
|
-
for (let a = 0; a <
|
|
928
|
-
const
|
|
929
|
-
|
|
969
|
+
static deadReckoningTime(e, t) {
|
|
970
|
+
t = JSON.parse(JSON.stringify(t)), t.sort((a, l) => (a.positionTime || 0) - (l.positionTime || 0));
|
|
971
|
+
let n = Number.MAX_SAFE_INTEGER, s = Number.MAX_SAFE_INTEGER;
|
|
972
|
+
for (let a = 0; a < t.length - 1; a++) {
|
|
973
|
+
const l = t[a], u = t[a + 1], f = v.calculatePointToLineDistance(e, l, u);
|
|
974
|
+
f < n && (n = f, s = a);
|
|
930
975
|
}
|
|
931
|
-
const o =
|
|
932
|
-
if (
|
|
933
|
-
|
|
976
|
+
const o = t[s], i = t[s + 1], r = v.calculateDistance(o, e), c = v.calculateDistance(i, e);
|
|
977
|
+
if (r === 0)
|
|
978
|
+
e = o;
|
|
934
979
|
else if (c === 0)
|
|
935
|
-
|
|
980
|
+
e = i;
|
|
936
981
|
else {
|
|
937
|
-
const a = o.positionTime || 0,
|
|
938
|
-
|
|
982
|
+
const a = o.positionTime || 0, l = i.positionTime || 0, u = v.calculateDistance(o, i);
|
|
983
|
+
e.positionTime = Math.round(a + (l - a) * (r / u));
|
|
939
984
|
}
|
|
940
|
-
return
|
|
985
|
+
return e.utc = e.positionTime ? b.unix(e.positionTime).utc().format() : void 0, e.positionTime ? e : void 0;
|
|
941
986
|
}
|
|
942
987
|
/**
|
|
943
988
|
* 翻转轨迹
|
|
944
989
|
* @param route
|
|
945
990
|
*/
|
|
946
|
-
static reverseRoute(
|
|
947
|
-
const
|
|
948
|
-
for (const n of
|
|
949
|
-
|
|
950
|
-
return
|
|
991
|
+
static reverseRoute(e) {
|
|
992
|
+
const t = [];
|
|
993
|
+
for (const n of e)
|
|
994
|
+
t.push(n.reverse());
|
|
995
|
+
return t;
|
|
951
996
|
}
|
|
952
997
|
/**
|
|
953
998
|
* 翻转坐标
|
|
954
999
|
* @param coordinates
|
|
955
1000
|
*/
|
|
956
|
-
static reverseCoordinates(
|
|
957
|
-
return
|
|
1001
|
+
static reverseCoordinates(e) {
|
|
1002
|
+
return e.reverse();
|
|
958
1003
|
}
|
|
959
1004
|
/**
|
|
960
1005
|
* XML转义
|
|
961
1006
|
* @param s
|
|
962
1007
|
*/
|
|
963
|
-
static xmlEscape(
|
|
964
|
-
return
|
|
1008
|
+
static xmlEscape(e) {
|
|
1009
|
+
return e == null ? "" : String(e).trim().replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
965
1010
|
}
|
|
966
1011
|
/**
|
|
967
1012
|
* 路径转RTZ 1.2
|
|
@@ -971,9 +1016,9 @@ class v {
|
|
|
971
1016
|
* @see https://cirm.org/api/documents/download/64?documents/October2022/bWjrTyyfNGseK9ch3TKf.xsd
|
|
972
1017
|
*
|
|
973
1018
|
*/
|
|
974
|
-
static waypoints2RTZ(
|
|
975
|
-
const
|
|
976
|
-
return
|
|
1019
|
+
static waypoints2RTZ(e, t) {
|
|
1020
|
+
const s = [];
|
|
1021
|
+
return s.push('<?xml version="1.0" encoding="UTF-8"?>'), s.push('<route xmlns="http://www.cirm.org/RTZ/1/2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2">'), s.push(` <routeInfo routeName="${v.xmlEscape(e)}"></routeInfo>`), s.push(...v.toRTZWaypoints(t, 6)), s.push("</route>"), s.join(`
|
|
977
1022
|
`);
|
|
978
1023
|
}
|
|
979
1024
|
/**
|
|
@@ -982,34 +1027,34 @@ class v {
|
|
|
982
1027
|
* @param waypoints 途径点
|
|
983
1028
|
* @see https://cirm.org/rtz-xml-schemas
|
|
984
1029
|
*/
|
|
985
|
-
static waypoints2RTZ10(
|
|
1030
|
+
static waypoints2RTZ10(e, t, n = !0) {
|
|
986
1031
|
const o = [];
|
|
987
|
-
return o.push('<?xml version="1.0" encoding="UTF-8"?>'), o.push('<route xmlns="http://www.cirm.org/RTZ/1/0" version="1.0">'), o.push(` <routeInfo routeName="${v.xmlEscape(
|
|
1032
|
+
return o.push('<?xml version="1.0" encoding="UTF-8"?>'), o.push('<route xmlns="http://www.cirm.org/RTZ/1/0" version="1.0">'), o.push(` <routeInfo routeName="${v.xmlEscape(e)}"></routeInfo>`), o.push(...v.toRTZWaypoints(t, 6, n)), o.push("</route>"), o.join(`
|
|
988
1033
|
`);
|
|
989
1034
|
}
|
|
990
|
-
static toRTZWaypoints(
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
for (let o = 0; o <
|
|
994
|
-
const
|
|
995
|
-
if (
|
|
996
|
-
const
|
|
997
|
-
|
|
1035
|
+
static toRTZWaypoints(e, t = 6, n = !0) {
|
|
1036
|
+
const s = [];
|
|
1037
|
+
s.push(" <waypoints>");
|
|
1038
|
+
for (let o = 0; o < e.length; o++) {
|
|
1039
|
+
const i = e[o], r = o + 1, c = (i.lat ?? "").toFixed ? i.lat.toFixed(t).padEnd(t + 2, "0") : i.lat, a = (i.lng ?? "").toFixed ? i.lng.toFixed(t).padEnd(t + 2, "0") : i.lng, l = [`id="${r}"`, 'revision="0"'];
|
|
1040
|
+
if (i.name && l.push(`name="${v.xmlEscape(i.name)}"`), s.push(` <waypoint ${l.join(" ")}>`), s.push(` <position lat="${v.xmlEscape(c)}" lon="${v.xmlEscape(a)}" />`), o > 0) {
|
|
1041
|
+
const u = i.gcToPrevious ? "Orthodrome" : "Loxodrome";
|
|
1042
|
+
s.push(` <leg geometryType="${u}" />`);
|
|
998
1043
|
}
|
|
999
1044
|
if (n) {
|
|
1000
|
-
const
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
1003
|
-
else if (
|
|
1045
|
+
const u = [];
|
|
1046
|
+
if (i.description && u.push(` <description>${v.xmlEscape(i.description)}</description>`), i.utc)
|
|
1047
|
+
u.push(` <time>${v.xmlEscape(i.utc)}</time>`);
|
|
1048
|
+
else if (i.positionTime)
|
|
1004
1049
|
try {
|
|
1005
|
-
|
|
1050
|
+
u.push(` <time>${v.xmlEscape(b.unix(i.positionTime).utc().format())}</time>`);
|
|
1006
1051
|
} catch {
|
|
1007
1052
|
}
|
|
1008
|
-
|
|
1053
|
+
i.cog !== void 0 && u.push(` <cog>${v.xmlEscape(i.cog)}</cog>`), i.sog !== void 0 && u.push(` <sog>${v.xmlEscape(i.sog)}</sog>`), u.length && (s.push(" <extensions>"), s.push(...u), s.push(" </extensions>"));
|
|
1009
1054
|
}
|
|
1010
|
-
|
|
1055
|
+
s.push(" </waypoint>");
|
|
1011
1056
|
}
|
|
1012
|
-
return
|
|
1057
|
+
return s.push(" </waypoints>"), s;
|
|
1013
1058
|
}
|
|
1014
1059
|
/**
|
|
1015
1060
|
* 路径转通用CSV (纯十进制度数、无厂商头、RFC 4180标准)
|
|
@@ -1018,60 +1063,94 @@ class v {
|
|
|
1018
1063
|
* @param waypoints 途径点
|
|
1019
1064
|
* @param options.precision 经纬度小数位数,默认6
|
|
1020
1065
|
*/
|
|
1021
|
-
static waypoints2CSV(
|
|
1022
|
-
|
|
1023
|
-
const
|
|
1024
|
-
o &&
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
for (let
|
|
1028
|
-
const
|
|
1029
|
-
|
|
1066
|
+
static waypoints2CSV(e, t, n) {
|
|
1067
|
+
X.debug("keep name for waypoints2CSV for legacy compatibility only", e);
|
|
1068
|
+
const s = (n == null ? void 0 : n.precision) ?? 6, o = t.some((p) => p.name), i = t.some((p) => p.description), r = t.some((p) => p.port != null), c = t.some((p) => p.stbd != null), a = t.some((p) => p.arrRad != null), l = t.some((p) => p.speed != null), u = t.some((p, S) => S > 0 && p.gcToPrevious != null), f = t.some((p) => p.bearing != null), h = t.some((p) => p.distanceFromPrevious != null), g = ["WPT No.", "Latitude", "Longitude"];
|
|
1069
|
+
o && g.push("Name"), i && g.push("Description"), u && g.push("Leg"), f && g.push("Bearing[deg]"), h && g.push("Distance[NM]"), l && g.push("Speed[kn]"), r && g.push("PORT XTD[NM]"), c && g.push("STBD XTD[NM]"), a && g.push("Arr.Rad[NM]");
|
|
1070
|
+
const m = [];
|
|
1071
|
+
m.push(g.map((p) => v.csvEscapeField(p)).join(","));
|
|
1072
|
+
for (let p = 0; p < t.length; p++) {
|
|
1073
|
+
const S = t[p], x = [];
|
|
1074
|
+
x.push((p + 1).toString()), x.push(S.lat.toFixed(s)), x.push(S.lng.toFixed(s)), o && x.push(S.name ?? ""), i && x.push(S.description ?? ""), u && x.push(p === 0 ? "" : S.gcToPrevious ? "GC" : "RL"), f && x.push(S.bearing != null ? String(S.bearing) : ""), h && x.push(S.distanceFromPrevious != null ? String(S.distanceFromPrevious) : ""), l && x.push(S.speed != null ? String(S.speed) : ""), r && x.push(S.port != null ? String(S.port) : ""), c && x.push(S.stbd != null ? String(S.stbd) : ""), a && x.push(S.arrRad != null ? String(S.arrRad) : ""), m.push(x.map((M) => v.csvEscapeField(M)).join(","));
|
|
1030
1075
|
}
|
|
1031
|
-
return
|
|
1076
|
+
return m.join(`
|
|
1032
1077
|
`);
|
|
1033
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* 路径导出为 XLSX (Office Open XML 格式,无需第三方依赖)
|
|
1081
|
+
* 兼容 Excel 2007+、LibreOffice、Google Sheets、WPS、macOS Numbers 等所有现代表格软件
|
|
1082
|
+
* const buf = LaneHelper.waypoints2XLSX('route-name', waypoints, { precision: 6 })
|
|
1083
|
+
* 浏览器下载
|
|
1084
|
+
* const blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
|
1085
|
+
* @param name 工作表名称
|
|
1086
|
+
* @param waypoints 途径点
|
|
1087
|
+
* @param options.precision 经纬度小数位数,默认6
|
|
1088
|
+
* @returns Uint8Array 二进制数据,可直接用于 new Blob([result], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
|
|
1089
|
+
*/
|
|
1090
|
+
static waypoints2XLSX(e, t, n) {
|
|
1091
|
+
const s = (n == null ? void 0 : n.precision) ?? 6, o = e || "Route", i = t.some((P) => P.name), r = t.some((P) => P.description), c = t.some((P) => P.port != null), a = t.some((P) => P.stbd != null), l = t.some((P) => P.arrRad != null), u = t.some((P) => P.speed != null), f = t.some((P, I) => I > 0 && P.gcToPrevious != null), h = t.some((P) => P.bearing != null), g = t.some((P) => P.distanceFromPrevious != null), m = ["WPT No.", "Latitude", "Longitude"];
|
|
1092
|
+
i && m.push("Name"), r && m.push("Description"), f && m.push("Leg"), h && m.push("Bearing[deg]"), g && m.push("Distance[NM]"), u && m.push("Speed[kn]"), c && m.push("PORT XTD[NM]"), a && m.push("STBD XTD[NM]"), l && m.push("Arr.Rad[NM]");
|
|
1093
|
+
const p = (P) => P == null ? "" : String(P).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1094
|
+
let x = `<row r="1">${m.map((P, I) => `<c r="${z(I)}1" s="1" t="inlineStr"><is><t>${p(P)}</t></is></c>`).join("")}</row>`;
|
|
1095
|
+
for (let P = 0; P < t.length; P++) {
|
|
1096
|
+
const I = t[P], E = P + 2, B = [], Z = (W, j) => B.push(`<c r="${z(W)}${E}"><v>${j}</v></c>`), A = (W, j) => B.push(`<c r="${z(W)}${E}" t="inlineStr"><is><t>${p(j)}</t></is></c>`), k = (W, j) => {
|
|
1097
|
+
j != null ? Z(W, j) : A(W, "");
|
|
1098
|
+
};
|
|
1099
|
+
Z(0, P + 1), A(1, d.lat2pretty(I.lat, s).pretty), A(2, d.lng2pretty(I.lng, s).pretty);
|
|
1100
|
+
let D = 3;
|
|
1101
|
+
i && (A(D, I.name ?? ""), D++), r && (A(D, I.description ?? ""), D++), f && (A(D, P === 0 ? "" : I.gcToPrevious ? "GC" : "RL"), D++), h && (k(D, I.bearing), D++), g && (k(D, I.distanceFromPrevious), D++), u && (k(D, I.speed), D++), c && (k(D, I.port), D++), a && (k(D, I.stbd), D++), l && (k(D, I.arrRad), D++), x += `<row r="${E}">${B.join("")}</row>`;
|
|
1102
|
+
}
|
|
1103
|
+
const M = "0." + "0".repeat(s), y = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>' + x + "</sheetData></worksheet>", N = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>', w = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>', q = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="' + p(o) + '" sheetId="1" r:id="rId1"/></sheets></workbook>', U = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>', L = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><numFmts count="1"><numFmt numFmtId="164" formatCode="' + M + '"/></numFmts><fonts count="2"><font><sz val="11"/><name val="Arial"/></font><font><b/><sz val="11"/><name val="Arial"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="none"/></fill></fills><borders count="2"><border><left/><right/><top/><bottom/><diagonal/></border><border><bottom style="thin"><color auto="1"/></bottom></border></borders><cellStyleXfs count="1"><xf/></cellStyleXfs><cellXfs count="3"><xf/><xf fontId="1" fillId="1" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/><xf numFmtId="164" fontId="0" applyNumberFormat="1"/></cellXfs></styleSheet>', O = new TextEncoder();
|
|
1104
|
+
return _([
|
|
1105
|
+
{ name: "[Content_Types].xml", data: O.encode(N) },
|
|
1106
|
+
{ name: "_rels/.rels", data: O.encode(w) },
|
|
1107
|
+
{ name: "xl/workbook.xml", data: O.encode(q) },
|
|
1108
|
+
{ name: "xl/_rels/workbook.xml.rels", data: O.encode(U) },
|
|
1109
|
+
{ name: "xl/worksheets/sheet1.xml", data: O.encode(y) },
|
|
1110
|
+
{ name: "xl/styles.xml", data: O.encode(L) }
|
|
1111
|
+
]);
|
|
1112
|
+
}
|
|
1034
1113
|
/**
|
|
1035
1114
|
* RFC 4180 CSV字段转义:含逗号、双引号、换行时用双引号包裹,内部双引号翻倍
|
|
1036
1115
|
*/
|
|
1037
|
-
static csvEscapeField(
|
|
1038
|
-
if (
|
|
1116
|
+
static csvEscapeField(e) {
|
|
1117
|
+
if (e == null)
|
|
1039
1118
|
return "";
|
|
1040
|
-
const
|
|
1041
|
-
return
|
|
1042
|
-
`) ||
|
|
1119
|
+
const t = String(e);
|
|
1120
|
+
return t.includes(",") || t.includes('"') || t.includes(`
|
|
1121
|
+
`) || t.includes("\r") ? `"${t.replace(/"/g, '""')}"` : t;
|
|
1043
1122
|
}
|
|
1044
1123
|
/**
|
|
1045
1124
|
* 十进制度数转NMEA度分格式 (DDMM.MM / DDDMM.MM)
|
|
1046
1125
|
* @param value 十进制度数
|
|
1047
1126
|
* @param isLat true为纬度,false为经度
|
|
1048
1127
|
*/
|
|
1049
|
-
static decimalToNmeaDm(
|
|
1050
|
-
const n = Math.abs(
|
|
1051
|
-
return { dm:
|
|
1128
|
+
static decimalToNmeaDm(e, t) {
|
|
1129
|
+
const n = Math.abs(e), s = Math.floor(n), o = (n - s) * 60, i = t ? `${s.toString().padStart(2, "0")}${o.toFixed(2).padStart(5, "0")}` : `${s.toString().padStart(3, "0")}${o.toFixed(2).padStart(5, "0")}`, r = t ? e >= 0 ? "N" : "S" : e >= 0 ? "E" : "W";
|
|
1130
|
+
return { dm: i, dir: r };
|
|
1052
1131
|
}
|
|
1053
1132
|
/**
|
|
1054
1133
|
* 计算NMEA 0183校验和 ($与*之间所有字符的异或,两字符十六进制大写)
|
|
1055
1134
|
* @param sentence $与*之间的字符串
|
|
1056
1135
|
*/
|
|
1057
|
-
static nmeaChecksum(
|
|
1058
|
-
let
|
|
1059
|
-
for (let n = 0; n <
|
|
1060
|
-
|
|
1061
|
-
return
|
|
1136
|
+
static nmeaChecksum(e) {
|
|
1137
|
+
let t = 0;
|
|
1138
|
+
for (let n = 0; n < e.length; n++)
|
|
1139
|
+
t ^= e.charCodeAt(n);
|
|
1140
|
+
return t.toString(16).toUpperCase().padStart(2, "0");
|
|
1062
1141
|
}
|
|
1063
1142
|
/**
|
|
1064
1143
|
* 路径转NMEA 0183 WPL航点语句
|
|
1065
1144
|
* @param waypoints 途径点
|
|
1066
1145
|
* @returns 多条$GPWPL语句,以\r\n分隔
|
|
1067
1146
|
*/
|
|
1068
|
-
static waypoints2NMEA(
|
|
1069
|
-
const
|
|
1070
|
-
for (let n = 0; n <
|
|
1071
|
-
const
|
|
1072
|
-
|
|
1147
|
+
static waypoints2NMEA(e) {
|
|
1148
|
+
const t = [];
|
|
1149
|
+
for (let n = 0; n < e.length; n++) {
|
|
1150
|
+
const s = e[n], o = v.decimalToNmeaDm(s.lat, !0), i = v.decimalToNmeaDm(s.lng, !1), r = s.name ? String(s.name).slice(0, 6) : `WPT${(n + 1).toString().padStart(3, "0")}`, c = `GPWPL,${o.dm},${o.dir},${i.dm},${i.dir},${r}`, a = v.nmeaChecksum(c);
|
|
1151
|
+
t.push(`$${c}*${a}`);
|
|
1073
1152
|
}
|
|
1074
|
-
return
|
|
1153
|
+
return t.join(`\r
|
|
1075
1154
|
`);
|
|
1076
1155
|
}
|
|
1077
1156
|
/**
|
|
@@ -1086,21 +1165,21 @@ class v {
|
|
|
1086
1165
|
* avgSpeed: number // 平均航速,单位节
|
|
1087
1166
|
* }
|
|
1088
1167
|
*/
|
|
1089
|
-
static coordinatesSummary(
|
|
1090
|
-
if (
|
|
1091
|
-
const n =
|
|
1092
|
-
(h,
|
|
1168
|
+
static coordinatesSummary(e, t = 3) {
|
|
1169
|
+
if (e.length > 1) {
|
|
1170
|
+
const n = e[0], s = e[e.length - 1], o = (n == null ? void 0 : n.positionTime) < (s == null ? void 0 : s.positionTime) ? b.unix(n == null ? void 0 : n.positionTime) : b.unix(s == null ? void 0 : s.positionTime), i = (n == null ? void 0 : n.positionTime) > (s == null ? void 0 : s.positionTime) ? b.unix(n == null ? void 0 : n.positionTime) : b.unix(s == null ? void 0 : s.positionTime), r = Math.round(i.diff(o, "hours", !0) * 100) / 100, c = this.generateRouteAccordingToWaypoints(e, !0, !0), a = this.calculateRouteDistance(c), u = K.inspectStoppages(e, t).reduce(
|
|
1171
|
+
(h, g) => (h.duration += g.duration, h.distance += g.distance, h),
|
|
1093
1172
|
{ hours: 0, distance: 0, spd: 0, duration: 0 }
|
|
1094
1173
|
);
|
|
1095
|
-
|
|
1096
|
-
const
|
|
1174
|
+
u.hours = Math.round(u.duration / 3600 * 100) / 100, u.distance = Math.round(u.distance * 100) / 100, u.spd = u.hours ? Math.round(u.distance / u.hours * 100) / 100 : 0;
|
|
1175
|
+
const f = r ? Math.round((a - u.distance) / (r - u.hours) * 100) / 100 : 0;
|
|
1097
1176
|
return {
|
|
1098
1177
|
begin: o.utc().format(),
|
|
1099
|
-
end:
|
|
1100
|
-
distance: Math.round((a -
|
|
1101
|
-
hours: Math.round((
|
|
1102
|
-
avgSpeed:
|
|
1103
|
-
stoppage:
|
|
1178
|
+
end: i.utc().format(),
|
|
1179
|
+
distance: Math.round((a - u.distance) * 100) / 100,
|
|
1180
|
+
hours: Math.round((r - u.hours) * 100) / 100,
|
|
1181
|
+
avgSpeed: f,
|
|
1182
|
+
stoppage: u
|
|
1104
1183
|
};
|
|
1105
1184
|
}
|
|
1106
1185
|
return {
|
|
@@ -1120,34 +1199,34 @@ class v {
|
|
|
1120
1199
|
* hour: Coordinate
|
|
1121
1200
|
* }
|
|
1122
1201
|
*/
|
|
1123
|
-
static pickUTCSampleFromSpeed(
|
|
1124
|
-
var
|
|
1125
|
-
if (!((
|
|
1202
|
+
static pickUTCSampleFromSpeed(e, t) {
|
|
1203
|
+
var l, u, f;
|
|
1204
|
+
if (!((u = (l = t == null ? void 0 : t.sample) == null ? void 0 : l.hours) != null && u.length))
|
|
1126
1205
|
return { routes: [], hour: void 0 };
|
|
1127
|
-
const n =
|
|
1128
|
-
let
|
|
1129
|
-
if (!
|
|
1130
|
-
const h =
|
|
1131
|
-
|
|
1132
|
-
const { cFactor:
|
|
1133
|
-
|
|
1134
|
-
...
|
|
1135
|
-
cFactor:
|
|
1136
|
-
cog:
|
|
1206
|
+
const n = t.sample.hours.at(0), s = b.utc(e), o = b.utc(t.eta), i = s.isAfter(o) ? o : s;
|
|
1207
|
+
let r = t.sample.all.find((h) => h.eta === i.format());
|
|
1208
|
+
if (!r) {
|
|
1209
|
+
const h = t.sample.all.filter((N) => b.utc(N.eta).isBefore(i)).at(-1), g = this.calculateSubRoute(h, t.route);
|
|
1210
|
+
r = (f = this.calculateNextCoordinateAlongRoute(h, h.speed * i.diff(b(h.etd), "hours", !0), g)) == null ? void 0 : f.coordinate;
|
|
1211
|
+
const { cFactor: m, cog: p, wxFactor: S, meteo: x } = h, M = Math.round(r.distanceFromPrevious * 1e4) / 1e4, y = Math.round((M + h.distanceFromStart) * 1e4) / 1e4;
|
|
1212
|
+
r = {
|
|
1213
|
+
...r,
|
|
1214
|
+
cFactor: m,
|
|
1215
|
+
cog: p,
|
|
1137
1216
|
speed: h.speed,
|
|
1138
|
-
wxFactor:
|
|
1139
|
-
distanceFromStart:
|
|
1217
|
+
wxFactor: S,
|
|
1218
|
+
distanceFromStart: y,
|
|
1140
1219
|
distanceFromPrevious: M,
|
|
1141
|
-
meteo:
|
|
1142
|
-
eta:
|
|
1143
|
-
etd:
|
|
1220
|
+
meteo: x,
|
|
1221
|
+
eta: i.format(),
|
|
1222
|
+
etd: i.format()
|
|
1144
1223
|
};
|
|
1145
1224
|
}
|
|
1146
|
-
|
|
1147
|
-
const c = this.calculateRangeWaypoints(n,
|
|
1225
|
+
r.distanceToGo = Math.round((t.distance - r.distanceFromStart) * 100) / 100, r.timeToGo = Math.round(o.diff(r.etd, "hours", !0) * 100) / 100;
|
|
1226
|
+
const c = this.calculateRangeWaypoints(n, r, t.route);
|
|
1148
1227
|
return {
|
|
1149
1228
|
routes: this.generateRouteAccordingToWaypoints(c),
|
|
1150
|
-
hour:
|
|
1229
|
+
hour: r
|
|
1151
1230
|
};
|
|
1152
1231
|
}
|
|
1153
1232
|
/**
|
|
@@ -1160,15 +1239,15 @@ class v {
|
|
|
1160
1239
|
* hour: Coordinate
|
|
1161
1240
|
* }
|
|
1162
1241
|
*/
|
|
1163
|
-
static pickUTCSampleFromRoute(
|
|
1242
|
+
static pickUTCSampleFromRoute(e, t, n) {
|
|
1164
1243
|
var h;
|
|
1165
|
-
const
|
|
1166
|
-
a.speed =
|
|
1167
|
-
const
|
|
1168
|
-
a.eta = Math.abs(
|
|
1169
|
-
const
|
|
1244
|
+
const s = this.calculateSubRoute(t, n), o = this.calculateRouteDistance(s), i = o / t.speed, r = b.utc(e), c = b(t.etd), a = (h = this.calculateNextCoordinateAlongRoute(t, t.speed * r.diff(b(t.etd), "hours", !0), s)) == null ? void 0 : h.coordinate;
|
|
1245
|
+
a.speed = t.speed;
|
|
1246
|
+
const l = c.clone().add(a.hourFromPrevious, "hour");
|
|
1247
|
+
a.eta = Math.abs(l.diff(r, "second")) < 2 ? r.format() : l.format(), a.etd = a.eta, a.distanceFromStart = Math.round(a.distanceFromPrevious * 100) / 100, a.distanceToGo = Math.round((o - a.distanceFromStart) * 100) / 100, a.timeToGo = Math.round(c.clone().add(i, "hour").diff(b(a.etd), "hour") * 100) / 100;
|
|
1248
|
+
const u = this.calculateRangeWaypoints(t, a, n);
|
|
1170
1249
|
return {
|
|
1171
|
-
routes: this.generateRouteAccordingToWaypoints(
|
|
1250
|
+
routes: this.generateRouteAccordingToWaypoints(u),
|
|
1172
1251
|
hour: a
|
|
1173
1252
|
};
|
|
1174
1253
|
}
|
|
@@ -1177,167 +1256,167 @@ class v {
|
|
|
1177
1256
|
* @param bearing 航首向
|
|
1178
1257
|
* @param degree 要素角度,如 swell.degree
|
|
1179
1258
|
*/
|
|
1180
|
-
static includedAngle(
|
|
1181
|
-
|
|
1182
|
-
let n = Math.abs(
|
|
1259
|
+
static includedAngle(e, t) {
|
|
1260
|
+
X == null || X.debug("calculate bearing via: %j", { bearing: e, degree: t });
|
|
1261
|
+
let n = Math.abs(e % 360 - (t % 360 || 0));
|
|
1183
1262
|
return n = n > 180 ? 360 - n : n, n;
|
|
1184
1263
|
}
|
|
1185
1264
|
}
|
|
1186
|
-
let
|
|
1265
|
+
let $;
|
|
1187
1266
|
try {
|
|
1188
|
-
|
|
1267
|
+
$ = G.getLogger("vessel");
|
|
1189
1268
|
} catch {
|
|
1190
1269
|
} finally {
|
|
1191
1270
|
}
|
|
1192
|
-
class
|
|
1271
|
+
class re {
|
|
1193
1272
|
/**
|
|
1194
1273
|
* 将原始数据转换为geojson
|
|
1195
1274
|
* @param raw
|
|
1196
1275
|
*/
|
|
1197
|
-
static convert2Geojson(
|
|
1198
|
-
var n,
|
|
1199
|
-
const
|
|
1200
|
-
for (const
|
|
1201
|
-
const
|
|
1202
|
-
if (
|
|
1203
|
-
|
|
1204
|
-
for (const c of
|
|
1276
|
+
static convert2Geojson(e) {
|
|
1277
|
+
var n, s, o;
|
|
1278
|
+
const t = T.featureCollection([]);
|
|
1279
|
+
for (const i of e) {
|
|
1280
|
+
const r = (n = i.history) == null ? void 0 : n[0];
|
|
1281
|
+
if (i.forecasts) {
|
|
1282
|
+
r && r.wind && (r.wind.kts = r.kts);
|
|
1283
|
+
for (const c of i.forecasts) {
|
|
1205
1284
|
let a;
|
|
1206
|
-
const
|
|
1207
|
-
for (const
|
|
1208
|
-
const
|
|
1209
|
-
a = a ||
|
|
1210
|
-
const
|
|
1285
|
+
const l = [], u = [], f = b(c.date).utc(), h = `${i.name}-${c.model}`;
|
|
1286
|
+
for (const m in c == null ? void 0 : c.hours) {
|
|
1287
|
+
const p = c.hours[m];
|
|
1288
|
+
a = a || p;
|
|
1289
|
+
const S = f.clone().add(Number(m), "hour"), x = T.point([p.lng, p.lat], {
|
|
1211
1290
|
model: c.model,
|
|
1212
|
-
name:
|
|
1213
|
-
nameCn:
|
|
1214
|
-
date:
|
|
1215
|
-
hour: Number(
|
|
1216
|
-
format:
|
|
1217
|
-
pressure:
|
|
1218
|
-
gusts:
|
|
1219
|
-
wind:
|
|
1220
|
-
movement:
|
|
1291
|
+
name: i.name,
|
|
1292
|
+
nameCn: i.nameCn,
|
|
1293
|
+
date: S.format(),
|
|
1294
|
+
hour: Number(m),
|
|
1295
|
+
format: S.format("MMM-DD/HHmm[Z]"),
|
|
1296
|
+
pressure: p.pressure > 1e4 ? d.roundPrecision(p.pressure / 100, 0) : d.roundPrecision(p.pressure, 0),
|
|
1297
|
+
gusts: p.gusts,
|
|
1298
|
+
wind: p.wind || {},
|
|
1299
|
+
movement: p.movement,
|
|
1221
1300
|
category: h,
|
|
1222
1301
|
type: "forecast"
|
|
1223
1302
|
});
|
|
1224
|
-
|
|
1303
|
+
u.push(x), l.push(x.geometry.coordinates);
|
|
1225
1304
|
}
|
|
1226
|
-
const
|
|
1305
|
+
const g = {
|
|
1227
1306
|
kts: void 0,
|
|
1228
1307
|
deg: void 0
|
|
1229
1308
|
};
|
|
1230
|
-
if (
|
|
1231
|
-
const
|
|
1309
|
+
if (r) {
|
|
1310
|
+
const m = b(r.updated).utc();
|
|
1232
1311
|
if (a) {
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1312
|
+
const S = v.calculateDistance(r, a), x = b(a.utc || a.updated).diff(m, "h", !0);
|
|
1313
|
+
g.kts = Math.round(S / x * 100) / 100, g.deg = v.calculateBearing(r, a, !0, 0);
|
|
1235
1314
|
}
|
|
1236
|
-
const
|
|
1315
|
+
const p = T.point([r.lng, r.lat], {
|
|
1237
1316
|
model: c.model,
|
|
1238
|
-
name:
|
|
1239
|
-
nameCn:
|
|
1240
|
-
date:
|
|
1317
|
+
name: i.name,
|
|
1318
|
+
nameCn: i.nameCn,
|
|
1319
|
+
date: m.format(),
|
|
1241
1320
|
hour: 0,
|
|
1242
|
-
format:
|
|
1243
|
-
pressure:
|
|
1244
|
-
wind:
|
|
1245
|
-
movement:
|
|
1321
|
+
format: m.format("MMM-DD/HHmm[Z]"),
|
|
1322
|
+
pressure: r.pressure > 1e4 ? d.roundPrecision((r == null ? void 0 : r.pressure) / 100, 0) : d.roundPrecision(r.pressure, 0),
|
|
1323
|
+
wind: r.wind,
|
|
1324
|
+
movement: g,
|
|
1246
1325
|
category: h,
|
|
1247
1326
|
type: "forecast",
|
|
1248
1327
|
important: !0
|
|
1249
1328
|
// 第一个预报点为重要点
|
|
1250
1329
|
});
|
|
1251
|
-
|
|
1330
|
+
u.unshift(p), l.unshift(p.geometry.coordinates);
|
|
1252
1331
|
}
|
|
1253
|
-
if (
|
|
1254
|
-
const
|
|
1255
|
-
date: (
|
|
1256
|
-
id:
|
|
1332
|
+
if (t.features.push(...u), (l == null ? void 0 : l.length) > 1) {
|
|
1333
|
+
const m = T.lineString(d.convertToMonotonicLng2(l), {
|
|
1334
|
+
date: (r == null ? void 0 : r.updated) || (f == null ? void 0 : f.format()),
|
|
1335
|
+
id: i.id || i.name,
|
|
1257
1336
|
model: c.model,
|
|
1258
|
-
name:
|
|
1337
|
+
name: i.name,
|
|
1259
1338
|
category: h,
|
|
1260
1339
|
type: "forecast",
|
|
1261
|
-
movement:
|
|
1340
|
+
movement: g
|
|
1262
1341
|
});
|
|
1263
|
-
|
|
1342
|
+
t.features.push(m);
|
|
1264
1343
|
}
|
|
1265
1344
|
}
|
|
1266
1345
|
}
|
|
1267
|
-
if (
|
|
1268
|
-
const c = [], a = b(
|
|
1269
|
-
for (const
|
|
1270
|
-
const h = b(
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1273
|
-
name:
|
|
1274
|
-
nameCn:
|
|
1346
|
+
if (t.features.sort((c, a) => c.properties.type === "forecast" && a.properties.type === "forecast" && c.geometry.type === "Point" && a.geometry.type === "Point" ? b(c.properties.date).valueOf() - b(a.properties.date).valueOf() : 0), (s = i.history) != null && s.length) {
|
|
1347
|
+
const c = [], a = b(r == null ? void 0 : r.updated).utc(), l = b((o = i.history) == null ? void 0 : o.at(-1).updated).utc(), u = a.diff(l, "h") % 24 > 2 ? 24 : 12;
|
|
1348
|
+
for (const f of i.history) {
|
|
1349
|
+
const h = b(f.updated).utc(), g = h.isSameOrBefore(a) || h.isSame(l);
|
|
1350
|
+
g && a.add(-u, "h");
|
|
1351
|
+
const m = T.point([f.lng, f.lat], {
|
|
1352
|
+
name: i.name,
|
|
1353
|
+
nameCn: i.nameCn,
|
|
1275
1354
|
date: h.format(),
|
|
1276
1355
|
format: h.format("MMM-DD/HHmm[Z]"),
|
|
1277
|
-
pressure:
|
|
1278
|
-
kts:
|
|
1279
|
-
level:
|
|
1356
|
+
pressure: f.pressure > 1e4 ? d.roundPrecision(f.pressure / 100, 0) : d.roundPrecision(f.pressure, 0),
|
|
1357
|
+
kts: f.kts,
|
|
1358
|
+
level: f.type,
|
|
1280
1359
|
type: "history",
|
|
1281
|
-
category: `${
|
|
1282
|
-
wind:
|
|
1283
|
-
movement:
|
|
1284
|
-
important:
|
|
1360
|
+
category: `${i.name}-history`,
|
|
1361
|
+
wind: f.wind,
|
|
1362
|
+
movement: f.movement,
|
|
1363
|
+
important: g
|
|
1285
1364
|
});
|
|
1286
|
-
|
|
1365
|
+
t.features.push(m), c.push(m.geometry.coordinates);
|
|
1287
1366
|
}
|
|
1288
1367
|
if (c.length === 1 && c.push(c[0]), c.length > 1) {
|
|
1289
|
-
const
|
|
1290
|
-
name:
|
|
1368
|
+
const f = T.lineString(d.convertToMonotonicLng2(c), {
|
|
1369
|
+
name: i.name,
|
|
1291
1370
|
type: "history",
|
|
1292
|
-
updated:
|
|
1293
|
-
pressure: (
|
|
1294
|
-
kts:
|
|
1295
|
-
level:
|
|
1371
|
+
updated: r == null ? void 0 : r.updated,
|
|
1372
|
+
pressure: (r == null ? void 0 : r.pressure) > 1e4 ? d.roundPrecision((r == null ? void 0 : r.pressure) / 100, 0) : d.roundPrecision(r == null ? void 0 : r.pressure, 0),
|
|
1373
|
+
kts: r == null ? void 0 : r.kts,
|
|
1374
|
+
level: r == null ? void 0 : r.type
|
|
1296
1375
|
});
|
|
1297
|
-
|
|
1376
|
+
t.features.push(f);
|
|
1298
1377
|
}
|
|
1299
1378
|
}
|
|
1300
1379
|
}
|
|
1301
|
-
return
|
|
1380
|
+
return t;
|
|
1302
1381
|
}
|
|
1303
1382
|
/**
|
|
1304
1383
|
* 插值台风预报轨迹
|
|
1305
1384
|
* @param tropicals
|
|
1306
1385
|
* @param step
|
|
1307
1386
|
*/
|
|
1308
|
-
static interpolate(
|
|
1309
|
-
var o,
|
|
1310
|
-
const n = (o =
|
|
1387
|
+
static interpolate(e, t = 3) {
|
|
1388
|
+
var o, i, r, c;
|
|
1389
|
+
const n = (o = e == null ? void 0 : e.data) == null ? void 0 : o.features.filter((a) => a.geometry.type === "LineString" && a.properties.type === "forecast"), s = [];
|
|
1311
1390
|
for (const a of n) {
|
|
1312
|
-
const
|
|
1313
|
-
let
|
|
1314
|
-
const
|
|
1315
|
-
(M) => M.geometry.type === "Point" && M.properties.type === "forecast" && M.properties.category === `${
|
|
1391
|
+
const l = a.properties.name, u = a.properties.model, f = a.properties.showCircle, h = a.properties.disabled, g = b(a.properties.date).utc();
|
|
1392
|
+
let m = t * 60;
|
|
1393
|
+
const p = (i = e == null ? void 0 : e.data) == null ? void 0 : i.features.filter(
|
|
1394
|
+
(M) => M.geometry.type === "Point" && M.properties.type === "forecast" && M.properties.category === `${l}-${u}`
|
|
1316
1395
|
);
|
|
1317
|
-
let
|
|
1318
|
-
for (;
|
|
1319
|
-
if (
|
|
1320
|
-
const M =
|
|
1321
|
-
name:
|
|
1322
|
-
model:
|
|
1396
|
+
let S, x = g.clone().add(m, "minute").set({ minute: 0, second: 0, millisecond: 0 });
|
|
1397
|
+
for (; S = this.pickIndex(p, x), S <= p.length - 1; ) {
|
|
1398
|
+
if (S > 0) {
|
|
1399
|
+
const M = p[S], y = S === 0 ? void 0 : p[S - 1], N = (m / 60 - ((r = y == null ? void 0 : y.properties) == null ? void 0 : r.hour)) / (M.properties.hour - ((c = y == null ? void 0 : y.properties) == null ? void 0 : c.hour)), w = this.computeNumber(y == null ? void 0 : y.geometry.coordinates[0], M.geometry.coordinates[0], N), q = this.computeNumber(y == null ? void 0 : y.geometry.coordinates[1], M.geometry.coordinates[1], N), U = T.point([w, q], {
|
|
1400
|
+
name: l,
|
|
1401
|
+
model: u,
|
|
1323
1402
|
category: M == null ? void 0 : M.properties.category,
|
|
1324
|
-
date:
|
|
1325
|
-
format:
|
|
1326
|
-
gusts: this.computeNumber(
|
|
1327
|
-
hour: this.computeNumber(
|
|
1328
|
-
movement: this.computeNumber(
|
|
1329
|
-
pressure: this.computeNumber(
|
|
1330
|
-
wind: this.computeNumber(
|
|
1403
|
+
date: x.format(),
|
|
1404
|
+
format: x.format("MMM-DD/HHmm[Z]"),
|
|
1405
|
+
gusts: this.computeNumber(y == null ? void 0 : y.properties.gusts, M.properties.gusts, N),
|
|
1406
|
+
hour: this.computeNumber(y == null ? void 0 : y.properties.hour, M.properties.hour, N),
|
|
1407
|
+
movement: this.computeNumber(y == null ? void 0 : y.properties.movement, M.properties.movement, N),
|
|
1408
|
+
pressure: this.computeNumber(y == null ? void 0 : y.properties.pressure, M.properties.pressure, N),
|
|
1409
|
+
wind: this.computeNumber(y == null ? void 0 : y.properties.wind, M.properties.wind, N),
|
|
1331
1410
|
type: "forecast",
|
|
1332
1411
|
disabled: h,
|
|
1333
|
-
showCircle:
|
|
1412
|
+
showCircle: f
|
|
1334
1413
|
});
|
|
1335
|
-
|
|
1414
|
+
s.push(U);
|
|
1336
1415
|
}
|
|
1337
|
-
|
|
1416
|
+
m += t * 60, x = g.clone().add(m, "minute").set({ minute: 0, second: 0, millisecond: 0 });
|
|
1338
1417
|
}
|
|
1339
1418
|
}
|
|
1340
|
-
return
|
|
1419
|
+
return s;
|
|
1341
1420
|
}
|
|
1342
1421
|
/**
|
|
1343
1422
|
* 计算穿航核验点
|
|
@@ -1345,9 +1424,9 @@ class H {
|
|
|
1345
1424
|
* @param tropical 台风数据 { history: any[], forecasts: any[] }
|
|
1346
1425
|
* @param options
|
|
1347
1426
|
*/
|
|
1348
|
-
static accelPassageAt(
|
|
1349
|
-
const { t1: n, t2:
|
|
1350
|
-
return { t1: n, t2:
|
|
1427
|
+
static accelPassageAt(e, t) {
|
|
1428
|
+
const { t1: n, t2: s, hr: o, hours: i } = this.tropicalCenterTwin(e, 24, t);
|
|
1429
|
+
return { t1: n, t2: s, hr: o, hours: i };
|
|
1351
1430
|
}
|
|
1352
1431
|
/**
|
|
1353
1432
|
* 计算最佳绕航点
|
|
@@ -1360,32 +1439,32 @@ class H {
|
|
|
1360
1439
|
* @param speed 前进速度
|
|
1361
1440
|
* @param options
|
|
1362
1441
|
*/
|
|
1363
|
-
static diversionPassageAt(
|
|
1364
|
-
const { t1: o, t2:
|
|
1365
|
-
if (o &&
|
|
1366
|
-
if (!
|
|
1367
|
-
const
|
|
1368
|
-
if (
|
|
1369
|
-
return
|
|
1370
|
-
from:
|
|
1442
|
+
static diversionPassageAt(e, t, n, s = {}) {
|
|
1443
|
+
const { t1: o, t2: i, hr: r, hours: c } = this.tropicalCenterTwin(t, 24, s);
|
|
1444
|
+
if (o && i) {
|
|
1445
|
+
if (!s.debug) {
|
|
1446
|
+
const g = v.calculateDistance(e, o), m = v.calculateDistance(e, i);
|
|
1447
|
+
if (g > 2 * n && m > 2 * n)
|
|
1448
|
+
return $ == null || $.info("[%s] the distance between from and t1(%d) and t2(%d) is enough, no need diversion: %j", s.requestId, g, m, {
|
|
1449
|
+
from: e,
|
|
1371
1450
|
t1: o,
|
|
1372
|
-
t2:
|
|
1373
|
-
hr:
|
|
1451
|
+
t2: i,
|
|
1452
|
+
hr: r
|
|
1374
1453
|
}), {};
|
|
1375
1454
|
}
|
|
1376
|
-
const a = v.calculateBearing(
|
|
1377
|
-
let
|
|
1378
|
-
|
|
1379
|
-
const h = v.calculateCoordinate(o,
|
|
1380
|
-
return
|
|
1381
|
-
from:
|
|
1455
|
+
const a = v.calculateBearing(e, o), l = v.calculateBearing(o, i), u = Math.abs(a - l);
|
|
1456
|
+
let f = 0;
|
|
1457
|
+
u < 180 ? f = u + 90 : u >= 180 && (f = u - 90);
|
|
1458
|
+
const h = v.calculateCoordinate(o, f, n);
|
|
1459
|
+
return $ == null || $.info("[%s] the right tangent position: %j", s.requestId, {
|
|
1460
|
+
from: e,
|
|
1382
1461
|
t1: o,
|
|
1383
|
-
t2:
|
|
1462
|
+
t2: i,
|
|
1384
1463
|
radius: n,
|
|
1385
1464
|
bearing1: a,
|
|
1386
|
-
bearing2:
|
|
1465
|
+
bearing2: l,
|
|
1387
1466
|
right: h
|
|
1388
|
-
}), { at: h, t1: o, t2:
|
|
1467
|
+
}), { at: h, t1: o, t2: i, hr: Number(r), hours: c };
|
|
1389
1468
|
}
|
|
1390
1469
|
return {};
|
|
1391
1470
|
}
|
|
@@ -1399,23 +1478,23 @@ class H {
|
|
|
1399
1478
|
* @param radius 与台风中心的距离
|
|
1400
1479
|
* @param options
|
|
1401
1480
|
*/
|
|
1402
|
-
static driftPassageAt(
|
|
1403
|
-
const { t1: o, t2:
|
|
1404
|
-
if (o &&
|
|
1405
|
-
if (!
|
|
1406
|
-
const h = v.calculateDistance(
|
|
1407
|
-
if (h > 2 * n &&
|
|
1408
|
-
return
|
|
1409
|
-
from:
|
|
1481
|
+
static driftPassageAt(e, t, n, s = {}) {
|
|
1482
|
+
const { t1: o, t2: i, hr: r, hours: c } = this.tropicalCenterTwin(t, 24, s);
|
|
1483
|
+
if (o && i) {
|
|
1484
|
+
if (!s.debug) {
|
|
1485
|
+
const h = v.calculateDistance(e, o), g = v.calculateDistance(e, i);
|
|
1486
|
+
if (h > 2 * n && g > 2 * n)
|
|
1487
|
+
return $ == null || $.info("[%s] the distance between from and t1(%d) and t2(%d) is enough, no need drifting: %j", s.requestId, h, g, {
|
|
1488
|
+
from: e,
|
|
1410
1489
|
t1: o,
|
|
1411
|
-
t2:
|
|
1412
|
-
hr:
|
|
1490
|
+
t2: i,
|
|
1491
|
+
hr: r
|
|
1413
1492
|
}), {};
|
|
1414
1493
|
}
|
|
1415
|
-
const a = v.calculateBearing(
|
|
1416
|
-
return { at: v.calculateCoordinate(o, a -
|
|
1494
|
+
const a = v.calculateBearing(e, o), l = v.calculateBearing(o, i), u = v.calculateDistance(e, o);
|
|
1495
|
+
return { at: v.calculateCoordinate(o, a - l + 180, n < u ? n : u), t1: o, t2: i, hr: Number(r), hours: c };
|
|
1417
1496
|
} else
|
|
1418
|
-
return
|
|
1497
|
+
return $ == null || $.info("[%s] no need drift: %j", s.requestId, { from: e, t1: o, t2: i, hr: r }), {};
|
|
1419
1498
|
}
|
|
1420
1499
|
/**
|
|
1421
1500
|
* 获取台风中心点对
|
|
@@ -1425,75 +1504,75 @@ class H {
|
|
|
1425
1504
|
* @returns { t1: 当前台风中心点, t2: 未来hr小时台风中心点, hr: 未来台风中心点与当前台风中心点之间的时间差, hours: 未来24小时内所有台风中心点 }
|
|
1426
1505
|
* @private
|
|
1427
1506
|
*/
|
|
1428
|
-
static tropicalCenterTwin(
|
|
1429
|
-
var
|
|
1430
|
-
let
|
|
1431
|
-
(
|
|
1432
|
-
|
|
1507
|
+
static tropicalCenterTwin(e, t = 24, n = {}) {
|
|
1508
|
+
var l, u, f, h, g;
|
|
1509
|
+
let s = {};
|
|
1510
|
+
(l = e.forecasts) == null || l.forEach((m) => {
|
|
1511
|
+
s = { ...m.hours, ...s };
|
|
1433
1512
|
});
|
|
1434
|
-
const o = ((
|
|
1435
|
-
|
|
1436
|
-
let
|
|
1437
|
-
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
const c = Object.keys(
|
|
1441
|
-
for (const
|
|
1442
|
-
a[
|
|
1443
|
-
return { t1: o, t2:
|
|
1444
|
-
}
|
|
1445
|
-
static pickIndex(
|
|
1513
|
+
const o = ((u = e == null ? void 0 : e.history) == null ? void 0 : u[0]) || (s == null ? void 0 : s[(f = Object.keys(s)) == null ? void 0 : f[0]]);
|
|
1514
|
+
$ == null || $.info("[%s] the first tropical center: %j", n.requestId, o);
|
|
1515
|
+
let i = (h = Object.keys(s || {}).filter((m) => Number(m) <= (t < 0 ? 24 : t))) == null ? void 0 : h.at(-1);
|
|
1516
|
+
i || (i = (g = Object.keys(s || {}).filter((m) => Number(m) <= (t < 0 ? 24 : 2 * t))) == null ? void 0 : g.at(-1));
|
|
1517
|
+
const r = s == null ? void 0 : s[i || -1];
|
|
1518
|
+
$ == null || $.info("[%s] the second tropical center: %j in %d hrs", n.requestId, r, i);
|
|
1519
|
+
const c = Object.keys(s || {}).filter((m) => Number(m) <= Number(i)), a = { 0: o };
|
|
1520
|
+
for (const m of c)
|
|
1521
|
+
a[m] = s[m];
|
|
1522
|
+
return { t1: o, t2: r, hr: Number(i), hours: a };
|
|
1523
|
+
}
|
|
1524
|
+
static pickIndex(e, t) {
|
|
1446
1525
|
let n = 0;
|
|
1447
|
-
for (const
|
|
1448
|
-
if (b(
|
|
1526
|
+
for (const s of e) {
|
|
1527
|
+
if (b(s.properties.date).isAfter(t))
|
|
1449
1528
|
return n === 0 ? -1 : n;
|
|
1450
1529
|
n++;
|
|
1451
1530
|
}
|
|
1452
1531
|
return n;
|
|
1453
1532
|
}
|
|
1454
|
-
static computeNumber(
|
|
1455
|
-
if (
|
|
1456
|
-
if (
|
|
1457
|
-
if (isNaN(
|
|
1458
|
-
const
|
|
1459
|
-
for (const o in
|
|
1460
|
-
|
|
1461
|
-
return
|
|
1533
|
+
static computeNumber(e, t, n) {
|
|
1534
|
+
if (e)
|
|
1535
|
+
if (t) {
|
|
1536
|
+
if (isNaN(e) && isNaN(t) && typeof e != "string" && typeof t != "string") {
|
|
1537
|
+
const s = {};
|
|
1538
|
+
for (const o in e)
|
|
1539
|
+
s[o] = this.computeNumber(e[o], t[o], n);
|
|
1540
|
+
return s;
|
|
1462
1541
|
}
|
|
1463
|
-
return Math.round((
|
|
1542
|
+
return Math.round((e + (t - e) * n) * 100) / 100;
|
|
1464
1543
|
} else
|
|
1465
|
-
return
|
|
1544
|
+
return e;
|
|
1466
1545
|
else
|
|
1467
|
-
return
|
|
1546
|
+
return t;
|
|
1468
1547
|
}
|
|
1469
1548
|
}
|
|
1470
1549
|
typeof globalThis < "u" && typeof globalThis.Buffer > "u" && (globalThis.Buffer = {
|
|
1471
|
-
isBuffer(
|
|
1472
|
-
return
|
|
1550
|
+
isBuffer(C) {
|
|
1551
|
+
return C instanceof Uint8Array;
|
|
1473
1552
|
},
|
|
1474
|
-
from(
|
|
1475
|
-
if (typeof
|
|
1476
|
-
return new TextEncoder().encode(
|
|
1477
|
-
if (
|
|
1478
|
-
return new Uint8Array(
|
|
1479
|
-
if (Array.isArray(
|
|
1480
|
-
return new Uint8Array(
|
|
1553
|
+
from(C, e) {
|
|
1554
|
+
if (typeof C == "string")
|
|
1555
|
+
return new TextEncoder().encode(C);
|
|
1556
|
+
if (C instanceof ArrayBuffer)
|
|
1557
|
+
return new Uint8Array(C);
|
|
1558
|
+
if (Array.isArray(C))
|
|
1559
|
+
return new Uint8Array(C);
|
|
1481
1560
|
throw new TypeError("Buffer.from: unsupported source type");
|
|
1482
1561
|
}
|
|
1483
1562
|
});
|
|
1484
|
-
let
|
|
1563
|
+
let R;
|
|
1485
1564
|
try {
|
|
1486
|
-
|
|
1565
|
+
R = G.getLogger("meteo");
|
|
1487
1566
|
} catch {
|
|
1488
1567
|
} finally {
|
|
1489
1568
|
}
|
|
1490
|
-
function
|
|
1569
|
+
function H() {
|
|
1491
1570
|
if (typeof DOMParser < "u")
|
|
1492
1571
|
return new DOMParser();
|
|
1493
|
-
const { JSDOM:
|
|
1494
|
-
return new
|
|
1572
|
+
const { JSDOM: C } = require("jsdom"), { DOMParser: e } = new C("").window;
|
|
1573
|
+
return new e();
|
|
1495
1574
|
}
|
|
1496
|
-
class
|
|
1575
|
+
class F {
|
|
1497
1576
|
/**
|
|
1498
1577
|
* Buffer to ArrayBuffer
|
|
1499
1578
|
* 将 Node.js Buffer 转换为独立的 ArrayBuffer(byteOffset=0)。
|
|
@@ -1501,8 +1580,8 @@ class R {
|
|
|
1501
1580
|
* @param buf
|
|
1502
1581
|
* @returns
|
|
1503
1582
|
*/
|
|
1504
|
-
static toArrayBuffer(
|
|
1505
|
-
return
|
|
1583
|
+
static toArrayBuffer(e) {
|
|
1584
|
+
return e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength);
|
|
1506
1585
|
}
|
|
1507
1586
|
/**
|
|
1508
1587
|
* 基于turf画圆,返回GeoJSON Feature<Polygon>
|
|
@@ -1511,11 +1590,11 @@ class R {
|
|
|
1511
1590
|
* @param radius 半径
|
|
1512
1591
|
* @param options 步数、单位等选项
|
|
1513
1592
|
*/
|
|
1514
|
-
static drawCircle(
|
|
1515
|
-
const o = (
|
|
1516
|
-
|
|
1517
|
-
const
|
|
1518
|
-
return
|
|
1593
|
+
static drawCircle(e, t, n, s = {}) {
|
|
1594
|
+
const o = (s == null ? void 0 : s.steps) ?? 64, i = (s == null ? void 0 : s.units) ?? "nauticalmiles", r = (s == null ? void 0 : s.properties) ?? {}, c = T.point([e, t]), a = T.destination(c, n, 90, { units: i }), l = b.utc();
|
|
1595
|
+
r.id = (s == null ? void 0 : s.id) || l.valueOf().toString();
|
|
1596
|
+
const u = "circle";
|
|
1597
|
+
return r.name = (s == null ? void 0 : s.name) || `${u}_${l.format()}`, r.radius = n, r.center = [e, t], r.end = a.geometry.coordinates.map((f) => d.roundPrecision(f, 9)), r.units = i, r.steps = o, r.shape = "circle", R.info("[%s] draw circle with %j", s == null ? void 0 : s.requestId, r), T.circle(c, n, { steps: o, units: i, properties: r });
|
|
1519
1598
|
}
|
|
1520
1599
|
/**
|
|
1521
1600
|
* 基于turf画矩形,返回GeoJSON Feature<Polygon>
|
|
@@ -1523,13 +1602,13 @@ class R {
|
|
|
1523
1602
|
* @param end 对角线终点 [lng, lat]
|
|
1524
1603
|
* @param options 选项
|
|
1525
1604
|
*/
|
|
1526
|
-
static drawRect(
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1531
|
-
const [
|
|
1532
|
-
return
|
|
1605
|
+
static drawRect(e, t, n = {}) {
|
|
1606
|
+
const s = (n == null ? void 0 : n.properties) ?? {}, o = b.utc();
|
|
1607
|
+
s.id = (n == null ? void 0 : n.id) || o.valueOf().toString();
|
|
1608
|
+
const i = "rect";
|
|
1609
|
+
s.name = (n == null ? void 0 : n.name) || `${i}_${o.format()}`, s.start = e.map((l) => d.roundPrecision(l, 9)), s.end = t.map((l) => d.roundPrecision(l, 9)), s.shape = "rect", R.info("[%s] draw rect with %j", n == null ? void 0 : n.requestId, s);
|
|
1610
|
+
const [r, c] = d.convertToMonotonicLng2([e, t]), a = [Math.min(r[0], c[0]), Math.min(r[1], c[1]), Math.max(r[0], c[0]), Math.max(r[1], c[1])];
|
|
1611
|
+
return T.bboxPolygon(a, { properties: s });
|
|
1533
1612
|
}
|
|
1534
1613
|
/**
|
|
1535
1614
|
* 基于turf画线,返回GeoJSON Feature<LineString>
|
|
@@ -1538,13 +1617,13 @@ class R {
|
|
|
1538
1617
|
* @param options
|
|
1539
1618
|
* @returns
|
|
1540
1619
|
*/
|
|
1541
|
-
static drawLine(
|
|
1542
|
-
const n = (
|
|
1543
|
-
n.id = (
|
|
1620
|
+
static drawLine(e, t = {}) {
|
|
1621
|
+
const n = (t == null ? void 0 : t.properties) ?? {}, s = b.utc();
|
|
1622
|
+
n.id = (t == null ? void 0 : t.id) || s.valueOf().toString();
|
|
1544
1623
|
const o = "line";
|
|
1545
|
-
n.name = (
|
|
1546
|
-
const
|
|
1547
|
-
return
|
|
1624
|
+
n.name = (t == null ? void 0 : t.name) || `${o}_${s.format()}`, n.shape = "line", R.info("[%s] draw line with %j", t == null ? void 0 : t.requestId, n);
|
|
1625
|
+
const i = d.convertToMonotonicLng2(e);
|
|
1626
|
+
return T.lineString(i, n);
|
|
1548
1627
|
}
|
|
1549
1628
|
/**
|
|
1550
1629
|
* 基于turf画多边形,返回GeoJSON Feature<Polygon>
|
|
@@ -1552,13 +1631,13 @@ class R {
|
|
|
1552
1631
|
* @param options
|
|
1553
1632
|
* @returns
|
|
1554
1633
|
*/
|
|
1555
|
-
static drawPolygon(
|
|
1556
|
-
const n = (
|
|
1557
|
-
n.id = (
|
|
1634
|
+
static drawPolygon(e, t = {}) {
|
|
1635
|
+
const n = (t == null ? void 0 : t.properties) ?? {}, s = b.utc();
|
|
1636
|
+
n.id = (t == null ? void 0 : t.id) || s.valueOf().toString();
|
|
1558
1637
|
const o = "polygon";
|
|
1559
|
-
n.name = (
|
|
1560
|
-
const
|
|
1561
|
-
return
|
|
1638
|
+
n.name = (t == null ? void 0 : t.name) || `${o}_${s.format()}`, n.coordinates = e.map((r) => r.map((c) => d.roundPrecision(c, 9))), n.shape = "polygon", R.info("[%s] draw polygon with %j", t == null ? void 0 : t.requestId, n);
|
|
1639
|
+
const i = d.convertToMonotonicLng2(e);
|
|
1640
|
+
return T.polygon([i], n);
|
|
1562
1641
|
}
|
|
1563
1642
|
/**
|
|
1564
1643
|
* 基于turf画点,返回GeoJSON Feature<Point>
|
|
@@ -1567,11 +1646,11 @@ class R {
|
|
|
1567
1646
|
* @param options
|
|
1568
1647
|
* @returns
|
|
1569
1648
|
*/
|
|
1570
|
-
static drawPoint(
|
|
1571
|
-
const
|
|
1572
|
-
|
|
1573
|
-
const
|
|
1574
|
-
return
|
|
1649
|
+
static drawPoint(e, t, n = {}) {
|
|
1650
|
+
const s = (n == null ? void 0 : n.properties) ?? {}, o = b.utc();
|
|
1651
|
+
s.id = (n == null ? void 0 : n.id) || o.valueOf().toString();
|
|
1652
|
+
const i = "point";
|
|
1653
|
+
return s.name = (n == null ? void 0 : n.name) || `${i}_${o.format()}`, s.shape = "point", R.info("[%s] draw point with %j", n == null ? void 0 : n.requestId, s), T.point([t, e], s);
|
|
1575
1654
|
}
|
|
1576
1655
|
/**
|
|
1577
1656
|
* 解析ORM GeoJSON Feature (圆), 返回FeatureCollection
|
|
@@ -1580,29 +1659,29 @@ class R {
|
|
|
1580
1659
|
* @returns
|
|
1581
1660
|
* 如果feature是圆,则返回圆的中心、端点、半径线 和 边线四个Feature
|
|
1582
1661
|
*/
|
|
1583
|
-
static parseCircle(
|
|
1584
|
-
var
|
|
1585
|
-
|
|
1586
|
-
const n =
|
|
1662
|
+
static parseCircle(e, t = {}) {
|
|
1663
|
+
var s, o;
|
|
1664
|
+
R.info("[%s] parse circle with %j", t == null ? void 0 : t.requestId, e.properties);
|
|
1665
|
+
const n = e.properties || {};
|
|
1587
1666
|
if (n.shape == "circle") {
|
|
1588
|
-
const
|
|
1589
|
-
n.id = (n == null ? void 0 : n.id) ||
|
|
1590
|
-
const
|
|
1667
|
+
const i = b.utc();
|
|
1668
|
+
n.id = (n == null ? void 0 : n.id) || i.valueOf().toString();
|
|
1669
|
+
const r = n == null ? void 0 : n.id, c = ((s = n == null ? void 0 : n.center) == null ? void 0 : s.length) == 2 ? T.point(n.center) : T.centroid(e);
|
|
1591
1670
|
let a = "center";
|
|
1592
|
-
c.properties = { id: `${a}_${
|
|
1593
|
-
const
|
|
1594
|
-
let
|
|
1595
|
-
if (!
|
|
1596
|
-
const
|
|
1597
|
-
|
|
1671
|
+
c.properties = { id: `${a}_${r}`, parentId: r, name: a };
|
|
1672
|
+
const l = (n == null ? void 0 : n.units) || "nauticalmiles";
|
|
1673
|
+
let u = n == null ? void 0 : n.radius;
|
|
1674
|
+
if (!u) {
|
|
1675
|
+
const g = T.polygonToLine(e);
|
|
1676
|
+
u = T.pointToLineDistance(c, g, { units: l });
|
|
1598
1677
|
}
|
|
1599
1678
|
a = "end";
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
const h =
|
|
1603
|
-
return a = "edge", h.properties = { id: `${a}_${
|
|
1679
|
+
const f = ((o = n == null ? void 0 : n.end) == null ? void 0 : o.length) == 2 ? T.point(n.end) : T.destination(c, u, 90, { units: l });
|
|
1680
|
+
f.properties = { id: `${a}_${r}`, parentId: r, name: a, radius: u, units: l };
|
|
1681
|
+
const h = T.lineString([c.geometry.coordinates, f.geometry.coordinates]);
|
|
1682
|
+
return a = "edge", h.properties = { id: `${a}_${r}`, parentId: r, name: a, radius: u, units: l }, T.featureCollection([c, f, h, e]);
|
|
1604
1683
|
} else
|
|
1605
|
-
return
|
|
1684
|
+
return R.warn("[%s] not a orm-std circle, just return the original feature", t == null ? void 0 : t.requestId), T.featureCollection([e]);
|
|
1606
1685
|
}
|
|
1607
1686
|
/**
|
|
1608
1687
|
* 解析ORM GeoJSON Feature (矩形), 返回FeatureCollection
|
|
@@ -1611,19 +1690,19 @@ class R {
|
|
|
1611
1690
|
* @returns
|
|
1612
1691
|
* 如果feature是矩形,则返回矩形的四个顶点和矩形Feature
|
|
1613
1692
|
*/
|
|
1614
|
-
static parseRect(
|
|
1615
|
-
|
|
1616
|
-
const n =
|
|
1693
|
+
static parseRect(e, t = {}) {
|
|
1694
|
+
R.info("[%s] parse rect with %j", t == null ? void 0 : t.requestId, e.properties);
|
|
1695
|
+
const n = e.properties || {};
|
|
1617
1696
|
if (n.shape == "rect") {
|
|
1618
|
-
const
|
|
1619
|
-
n.id = (n == null ? void 0 : n.id) ||
|
|
1620
|
-
const o = n == null ? void 0 : n.id,
|
|
1621
|
-
const
|
|
1622
|
-
return
|
|
1697
|
+
const s = b.utc();
|
|
1698
|
+
n.id = (n == null ? void 0 : n.id) || s.valueOf().toString();
|
|
1699
|
+
const o = n == null ? void 0 : n.id, i = e.geometry.coordinates[0], c = ["sw", "nw", "ne", "se"].map((a, l) => {
|
|
1700
|
+
const u = T.point(i[l]);
|
|
1701
|
+
return u.properties = { id: `${a}_${o}`, parentId: o, name: a }, u;
|
|
1623
1702
|
});
|
|
1624
|
-
return
|
|
1703
|
+
return T.featureCollection([...c, e]);
|
|
1625
1704
|
} else
|
|
1626
|
-
return
|
|
1705
|
+
return R.warn("[%s] not a orm-std rect, just return the original feature", t == null ? void 0 : t.requestId), T.featureCollection([e]);
|
|
1627
1706
|
}
|
|
1628
1707
|
/**
|
|
1629
1708
|
* 解析ORM GeoJSON Feature (线), 返回FeatureCollection
|
|
@@ -1632,19 +1711,19 @@ class R {
|
|
|
1632
1711
|
* @returns
|
|
1633
1712
|
* 如果feature是线,则返回所有坐标点(首为start、尾为end、中间为point_N)以及线Feature
|
|
1634
1713
|
*/
|
|
1635
|
-
static parseLine(
|
|
1636
|
-
|
|
1637
|
-
const n =
|
|
1714
|
+
static parseLine(e, t = {}) {
|
|
1715
|
+
R.info("[%s] parse line with %j", t == null ? void 0 : t.requestId, e.properties);
|
|
1716
|
+
const n = e.properties || {};
|
|
1638
1717
|
if (n.shape == "line") {
|
|
1639
|
-
const
|
|
1640
|
-
n.id = (n == null ? void 0 : n.id) ||
|
|
1641
|
-
const o = n == null ? void 0 : n.id,
|
|
1642
|
-
const
|
|
1643
|
-
return
|
|
1718
|
+
const s = b.utc();
|
|
1719
|
+
n.id = (n == null ? void 0 : n.id) || s.valueOf().toString();
|
|
1720
|
+
const o = n == null ? void 0 : n.id, r = e.geometry.coordinates.map((c, a) => {
|
|
1721
|
+
const l = `point_${a}`, u = T.point(c);
|
|
1722
|
+
return u.properties = { id: `${l}_${o}`, parentId: o, name: l, index: a }, u;
|
|
1644
1723
|
});
|
|
1645
|
-
return
|
|
1724
|
+
return T.featureCollection([...r, e]);
|
|
1646
1725
|
} else
|
|
1647
|
-
return
|
|
1726
|
+
return R.warn("[%s] not a orm-std line, just return the original feature", t == null ? void 0 : t.requestId), T.featureCollection([e]);
|
|
1648
1727
|
}
|
|
1649
1728
|
/**
|
|
1650
1729
|
* 解析ORM GeoJSON Feature (多边形), 返回FeatureCollection
|
|
@@ -1653,19 +1732,19 @@ class R {
|
|
|
1653
1732
|
* @returns
|
|
1654
1733
|
* 如果feature是多边形,则返回多边形的顶点和多边形Feature
|
|
1655
1734
|
*/
|
|
1656
|
-
static parsePolygon(
|
|
1657
|
-
|
|
1658
|
-
const n =
|
|
1735
|
+
static parsePolygon(e, t = {}) {
|
|
1736
|
+
R.info("[%s] parse polygon with %j", t == null ? void 0 : t.requestId, e.properties);
|
|
1737
|
+
const n = e.properties || {};
|
|
1659
1738
|
if (n.shape == "polygon") {
|
|
1660
|
-
const
|
|
1661
|
-
n.id = (n == null ? void 0 : n.id) ||
|
|
1662
|
-
const o = n == null ? void 0 : n.id,
|
|
1663
|
-
const
|
|
1664
|
-
return
|
|
1739
|
+
const s = b.utc();
|
|
1740
|
+
n.id = (n == null ? void 0 : n.id) || s.valueOf().toString();
|
|
1741
|
+
const o = n == null ? void 0 : n.id, r = e.geometry.coordinates[0].map((c, a) => {
|
|
1742
|
+
const l = T.point(c), u = `corner_${a}`;
|
|
1743
|
+
return l.properties = { id: `${u}_${o}`, parentId: o, name: u, index: a }, l;
|
|
1665
1744
|
});
|
|
1666
|
-
return
|
|
1745
|
+
return T.featureCollection([...r, e]);
|
|
1667
1746
|
} else
|
|
1668
|
-
return
|
|
1747
|
+
return R.warn("[%s] not a orm-std polygon, just return the original feature", t == null ? void 0 : t.requestId), T.featureCollection([e]);
|
|
1669
1748
|
}
|
|
1670
1749
|
// ---------------------------------------------------------------------------
|
|
1671
1750
|
// ZIP / KMZ 内部工具
|
|
@@ -1674,11 +1753,11 @@ class R {
|
|
|
1674
1753
|
* 解压 DEFLATE raw 数据(ZIP compressionMethod=8)。
|
|
1675
1754
|
* Node.js 环境使用内置 zlib.inflateRawSync;浏览器环境抛出错误提示使用异步版本。
|
|
1676
1755
|
*/
|
|
1677
|
-
static _inflateRawSync(
|
|
1678
|
-
var
|
|
1679
|
-
if (typeof process < "u" && ((
|
|
1756
|
+
static _inflateRawSync(e) {
|
|
1757
|
+
var t;
|
|
1758
|
+
if (typeof process < "u" && ((t = process.versions) != null && t.node)) {
|
|
1680
1759
|
const n = require("zlib");
|
|
1681
|
-
return new Uint8Array(n.inflateRawSync(Buffer.from(
|
|
1760
|
+
return new Uint8Array(n.inflateRawSync(Buffer.from(e)));
|
|
1682
1761
|
}
|
|
1683
1762
|
throw new Error("Sync inflate is not supported in browser; use the async convertKMZ2GeoJSONAsync / convertZIP2GeoJSONAsync instead");
|
|
1684
1763
|
}
|
|
@@ -1686,53 +1765,53 @@ class R {
|
|
|
1686
1765
|
* 从 ZIP ArrayBuffer 中异步解析所有文件条目(浏览器 DecompressionStream)。
|
|
1687
1766
|
* 使用 Central Directory 解析,避免 Data Descriptor 导致的大小不准问题。
|
|
1688
1767
|
*/
|
|
1689
|
-
static async _parseZipEntries(
|
|
1690
|
-
var
|
|
1691
|
-
const
|
|
1692
|
-
let
|
|
1768
|
+
static async _parseZipEntries(e) {
|
|
1769
|
+
var f;
|
|
1770
|
+
const t = new Uint8Array(e), n = t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength), s = new DataView(n), o = new Uint8Array(n), i = /* @__PURE__ */ new Map();
|
|
1771
|
+
let r = -1;
|
|
1693
1772
|
for (let h = n.byteLength - 22; h >= 0; h--)
|
|
1694
|
-
if (
|
|
1695
|
-
|
|
1773
|
+
if (s.getUint32(h, !0) === 101010256) {
|
|
1774
|
+
r = h;
|
|
1696
1775
|
break;
|
|
1697
1776
|
}
|
|
1698
|
-
if (
|
|
1777
|
+
if (r < 0)
|
|
1699
1778
|
throw new Error("Invalid ZIP file: EOCD not found");
|
|
1700
|
-
|
|
1701
|
-
const c =
|
|
1702
|
-
let
|
|
1703
|
-
const
|
|
1704
|
-
for (;
|
|
1705
|
-
if (
|
|
1706
|
-
throw new Error(`Invalid ZIP file: Central Directory signature mismatch at offset ${
|
|
1707
|
-
const
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1710
|
-
else if (
|
|
1779
|
+
s.getUint16(r + 8, !0);
|
|
1780
|
+
const c = s.getUint32(r + 12, !0), a = s.getUint32(r + 16, !0);
|
|
1781
|
+
let l = a;
|
|
1782
|
+
const u = a + c;
|
|
1783
|
+
for (; l < u; ) {
|
|
1784
|
+
if (s.getUint32(l, !0) !== 33639248)
|
|
1785
|
+
throw new Error(`Invalid ZIP file: Central Directory signature mismatch at offset ${l}`);
|
|
1786
|
+
const g = s.getUint16(l + 10, !0), m = s.getUint32(l + 20, !0), p = s.getUint16(l + 28, !0), S = s.getUint16(l + 30, !0), x = s.getUint16(l + 32, !0), M = s.getUint32(l + 42, !0), y = o.slice(l + 46, l + 46 + p), N = new TextDecoder().decode(y), w = s.getUint16(M + 26, !0), q = s.getUint16(M + 28, !0), U = M + 30 + w + q, L = o.slice(U, U + m);
|
|
1787
|
+
if (g === 0)
|
|
1788
|
+
i.set(N, L);
|
|
1789
|
+
else if (g === 8) {
|
|
1711
1790
|
if (typeof DecompressionStream < "u") {
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1714
|
-
const
|
|
1715
|
-
let
|
|
1791
|
+
const O = new DecompressionStream("deflate-raw"), P = O.writable.getWriter(), I = O.readable.getReader();
|
|
1792
|
+
P.write(L), P.close();
|
|
1793
|
+
const E = [];
|
|
1794
|
+
let B = 0;
|
|
1716
1795
|
for (; ; ) {
|
|
1717
|
-
const { done:
|
|
1718
|
-
if (
|
|
1796
|
+
const { done: k, value: D } = await I.read();
|
|
1797
|
+
if (k)
|
|
1719
1798
|
break;
|
|
1720
|
-
|
|
1799
|
+
E.push(D), B += D.length;
|
|
1721
1800
|
}
|
|
1722
|
-
const
|
|
1723
|
-
let
|
|
1724
|
-
for (const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
} else if (typeof process < "u" && ((
|
|
1801
|
+
const Z = new Uint8Array(B);
|
|
1802
|
+
let A = 0;
|
|
1803
|
+
for (const k of E)
|
|
1804
|
+
Z.set(k, A), A += k.length;
|
|
1805
|
+
i.set(N, Z);
|
|
1806
|
+
} else if (typeof process < "u" && ((f = process.versions) != null && f.node))
|
|
1728
1807
|
try {
|
|
1729
|
-
|
|
1808
|
+
i.set(N, F._inflateRawSync(L));
|
|
1730
1809
|
} catch {
|
|
1731
1810
|
}
|
|
1732
1811
|
}
|
|
1733
|
-
|
|
1812
|
+
l += 46 + p + S + x;
|
|
1734
1813
|
}
|
|
1735
|
-
return
|
|
1814
|
+
return i;
|
|
1736
1815
|
}
|
|
1737
1816
|
// ---------------------------------------------------------------------------
|
|
1738
1817
|
// KMZ / ZIP → GeoJSON
|
|
@@ -1743,48 +1822,48 @@ class R {
|
|
|
1743
1822
|
* @param options
|
|
1744
1823
|
* @returns
|
|
1745
1824
|
*/
|
|
1746
|
-
static async convertKML2GeoJSON(
|
|
1747
|
-
var
|
|
1748
|
-
|
|
1749
|
-
const
|
|
1825
|
+
static async convertKML2GeoJSON(e, t = {}) {
|
|
1826
|
+
var r, c;
|
|
1827
|
+
R.info("[%s] convert kml to geojson", t == null ? void 0 : t.requestId);
|
|
1828
|
+
const s = H().parseFromString(e, "application/xml"), o = Array.from(s.querySelectorAll("Placemark")), i = [];
|
|
1750
1829
|
for (const a of o) {
|
|
1751
|
-
const
|
|
1752
|
-
for (const
|
|
1753
|
-
const N =
|
|
1754
|
-
N &&
|
|
1830
|
+
const l = {}, u = Array.from(a.querySelectorAll("ExtendedData > Data"));
|
|
1831
|
+
for (const y of u) {
|
|
1832
|
+
const N = y.getAttribute("name"), w = (c = (r = y.querySelector("value")) == null ? void 0 : r.textContent) == null ? void 0 : c.trim();
|
|
1833
|
+
N && w !== void 0 && w !== null && (l[N] = w);
|
|
1755
1834
|
}
|
|
1756
|
-
const
|
|
1757
|
-
const
|
|
1758
|
-
return [
|
|
1759
|
-
}),
|
|
1760
|
-
if (
|
|
1761
|
-
const
|
|
1762
|
-
if (
|
|
1763
|
-
const N =
|
|
1764
|
-
|
|
1835
|
+
const f = b.utc(), h = Object.keys(l).find((y) => y.toLowerCase().indexOf("name") !== -1), g = h ? l[h] : void 0, m = `${f.valueOf().toString()}_${Math.random().toString(36).slice(2, 8)}`, p = (y) => y.trim().split(/[\s\n]+/).filter((N) => N.length > 0).map((N) => {
|
|
1836
|
+
const w = N.split(",").map(Number);
|
|
1837
|
+
return [w[0], w[1]];
|
|
1838
|
+
}), S = a.querySelector("LineString > coordinates");
|
|
1839
|
+
if (S) {
|
|
1840
|
+
const y = p(S.textContent || "");
|
|
1841
|
+
if (y.length >= 2) {
|
|
1842
|
+
const N = T.lineString(y, { ...l, shape: "line", id: m, name: g });
|
|
1843
|
+
i.push(N);
|
|
1765
1844
|
}
|
|
1766
1845
|
continue;
|
|
1767
1846
|
}
|
|
1768
|
-
const
|
|
1769
|
-
if (
|
|
1770
|
-
const
|
|
1771
|
-
if (
|
|
1772
|
-
const N =
|
|
1773
|
-
|
|
1847
|
+
const x = a.querySelector("Polygon outerBoundaryIs LinearRing > coordinates");
|
|
1848
|
+
if (x) {
|
|
1849
|
+
const y = p(x.textContent || "");
|
|
1850
|
+
if (y.length >= 4) {
|
|
1851
|
+
const N = y[0][0] === y[y.length - 1][0] && y[0][1] === y[y.length - 1][1] ? y : [...y, y[0]], w = T.polygon([N], { ...l, shape: "polygon", id: m, name: g });
|
|
1852
|
+
i.push(w);
|
|
1774
1853
|
}
|
|
1775
1854
|
continue;
|
|
1776
1855
|
}
|
|
1777
1856
|
const M = a.querySelector("Point > coordinates");
|
|
1778
1857
|
if (M) {
|
|
1779
|
-
const
|
|
1780
|
-
if (
|
|
1781
|
-
const N =
|
|
1782
|
-
|
|
1858
|
+
const y = p(M.textContent || "");
|
|
1859
|
+
if (y.length >= 1) {
|
|
1860
|
+
const N = T.point(y[0], { ...l, shape: "point", id: m, name: g });
|
|
1861
|
+
i.push(N);
|
|
1783
1862
|
}
|
|
1784
1863
|
continue;
|
|
1785
1864
|
}
|
|
1786
1865
|
}
|
|
1787
|
-
return
|
|
1866
|
+
return T.featureCollection(i);
|
|
1788
1867
|
}
|
|
1789
1868
|
/**
|
|
1790
1869
|
* 将 KMZ(ZIP 内含 doc.kml 或首个 .kml 文件)异步转换为 GeoJSON。
|
|
@@ -1795,14 +1874,14 @@ class R {
|
|
|
1795
1874
|
* @param buffer KMZ 文件的 ArrayBuffer
|
|
1796
1875
|
* @param options
|
|
1797
1876
|
*/
|
|
1798
|
-
static async convertKMZ2GeoJSON(
|
|
1799
|
-
var
|
|
1800
|
-
|
|
1801
|
-
const n = await
|
|
1802
|
-
if (!
|
|
1803
|
-
return
|
|
1804
|
-
const o = new TextDecoder().decode(
|
|
1805
|
-
return
|
|
1877
|
+
static async convertKMZ2GeoJSON(e, t = {}) {
|
|
1878
|
+
var i;
|
|
1879
|
+
R.info("[%s] convert kmz to geojson (async)", t == null ? void 0 : t.requestId), typeof Buffer < "u" && Buffer.isBuffer(e) && (e = F.toArrayBuffer(e));
|
|
1880
|
+
const n = await F._parseZipEntries(e), s = n.get("doc.kml") ?? ((i = [...n.entries()].find(([r]) => r.toLowerCase().endsWith(".kml"))) == null ? void 0 : i[1]);
|
|
1881
|
+
if (!s)
|
|
1882
|
+
return R.warn("[%s] no .kml file found in kmz", t == null ? void 0 : t.requestId), T.featureCollection([]);
|
|
1883
|
+
const o = new TextDecoder().decode(s);
|
|
1884
|
+
return F.convertKML2GeoJSON(o, t);
|
|
1806
1885
|
}
|
|
1807
1886
|
/**
|
|
1808
1887
|
* 将 ZIP 文件中所有 .kml 文件合并转换为一个 GeoJSON FeatureCollection(异步)。
|
|
@@ -1813,16 +1892,16 @@ class R {
|
|
|
1813
1892
|
* @param buffer ZIP 文件的 ArrayBuffer
|
|
1814
1893
|
* @param options
|
|
1815
1894
|
*/
|
|
1816
|
-
static async convertZIP2GeoJSON(
|
|
1817
|
-
|
|
1818
|
-
const n = await
|
|
1819
|
-
for (const [o,
|
|
1895
|
+
static async convertZIP2GeoJSON(e, t = {}) {
|
|
1896
|
+
R.info("[%s] convert zip to geojson (async)", t == null ? void 0 : t.requestId), typeof Buffer < "u" && Buffer.isBuffer(e) && (e = F.toArrayBuffer(e));
|
|
1897
|
+
const n = await F._parseZipEntries(e), s = [];
|
|
1898
|
+
for (const [o, i] of n) {
|
|
1820
1899
|
if (!o.toLowerCase().endsWith(".kml"))
|
|
1821
1900
|
continue;
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1901
|
+
const r = new TextDecoder().decode(i), c = await F.convertKML2GeoJSON(r, t);
|
|
1902
|
+
s.push(...c.features);
|
|
1824
1903
|
}
|
|
1825
|
-
return
|
|
1904
|
+
return s.length === 0 ? (R.warn("[%s] no .kml files found in zip", t == null ? void 0 : t.requestId), this.convertSHP2GeoJSON(e, t)) : T.featureCollection(s);
|
|
1826
1905
|
}
|
|
1827
1906
|
/**
|
|
1828
1907
|
* 将 Shapefile ZIP(含 .shp/.shx/.dbf/.prj)转换为 GeoJSON FeatureCollection。
|
|
@@ -1832,13 +1911,13 @@ class R {
|
|
|
1832
1911
|
* @param buffer ZIP 文件的 ArrayBuffer
|
|
1833
1912
|
* @param options
|
|
1834
1913
|
*/
|
|
1835
|
-
static async convertSHP2GeoJSON(
|
|
1836
|
-
|
|
1914
|
+
static async convertSHP2GeoJSON(e, t = {}) {
|
|
1915
|
+
R.info("[%s] convert shp zip to geojson", t == null ? void 0 : t.requestId);
|
|
1837
1916
|
try {
|
|
1838
|
-
typeof Buffer < "u" && Buffer.isBuffer(
|
|
1839
|
-
const n = await
|
|
1917
|
+
typeof Buffer < "u" && Buffer.isBuffer(e) && (e = F.toArrayBuffer(e));
|
|
1918
|
+
const n = await J(e);
|
|
1840
1919
|
if (Array.isArray(n)) {
|
|
1841
|
-
const
|
|
1920
|
+
const s = n.flatMap((c) => c.features || []), o = b.utc(), i = (t == null ? void 0 : t.id) || o.valueOf().toString(), r = {
|
|
1842
1921
|
Point: "point",
|
|
1843
1922
|
MultiPoint: "point",
|
|
1844
1923
|
LineString: "line",
|
|
@@ -1846,24 +1925,24 @@ class R {
|
|
|
1846
1925
|
Polygon: "polygon",
|
|
1847
1926
|
MultiPolygon: "polygon"
|
|
1848
1927
|
};
|
|
1849
|
-
return
|
|
1850
|
-
var
|
|
1851
|
-
const
|
|
1852
|
-
c.properties = c.properties || {}, c.properties.shape =
|
|
1853
|
-
const
|
|
1854
|
-
c.properties.name = h || `${
|
|
1855
|
-
}),
|
|
1928
|
+
return s.forEach((c, a) => {
|
|
1929
|
+
var g;
|
|
1930
|
+
const l = ((g = c.geometry) == null ? void 0 : g.type) || "", u = r[l] || l.toLowerCase();
|
|
1931
|
+
c.properties = c.properties || {}, c.properties.shape = u, c.properties.parentId = i, c.properties.id = `${i}_${a}`;
|
|
1932
|
+
const f = Object.keys(c.properties).find((m) => m.toLowerCase().indexOf("name") !== -1), h = f ? c.properties[f] : void 0;
|
|
1933
|
+
c.properties.name = h || `${u}_${o.format()}_${a}`;
|
|
1934
|
+
}), T.featureCollection(s);
|
|
1856
1935
|
}
|
|
1857
1936
|
return n;
|
|
1858
1937
|
} catch (n) {
|
|
1859
|
-
return
|
|
1938
|
+
return R.warn("[%s] failed to convert shp zip: %s", t == null ? void 0 : t.requestId, n), T.featureCollection([]);
|
|
1860
1939
|
}
|
|
1861
1940
|
}
|
|
1862
1941
|
}
|
|
1863
1942
|
export {
|
|
1864
|
-
|
|
1865
|
-
|
|
1943
|
+
K as AisHelper,
|
|
1944
|
+
F as GeoJsonHelper,
|
|
1866
1945
|
v as LaneHelper,
|
|
1867
1946
|
d as LngLatHelper,
|
|
1868
|
-
|
|
1947
|
+
re as TropicalHelper
|
|
1869
1948
|
};
|