hn-map 1.0.10 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -22
- package/dist/index.js +2313 -610
- package/package.json +11 -8
- package/src/base/gaode_entity.ts +61 -0
- package/src/base/mars3d_entity.ts +64 -0
- package/src/base/siji_entity.ts +118 -0
- package/src/graphic/circle.ts +218 -0
- package/src/graphic/divPoint.ts +133 -0
- package/src/graphic/imagePoint.ts +237 -0
- package/src/graphic/label.ts +330 -0
- package/src/graphic/line.ts +345 -0
- package/src/graphic/numPoint.ts +290 -0
- package/src/graphic/point.ts +234 -0
- package/src/graphic/polygon.ts +188 -0
- package/src/graphic/rectangle.ts +202 -0
- package/src/index.ts +213 -0
- package/src/layer/cluster.ts +276 -0
- package/src/layer/geoJson.ts +174 -0
- package/src/layer/heatMap.ts +163 -0
- package/src/layer/layer.ts +464 -0
- package/src/layer/pointCloud.ts +78 -0
- package/src/map.ts +433 -0
- package/src/other/route.ts +457 -0
- package/src/types/globals.d.ts +5 -0
- package/src/util.ts +216 -0
- package/src/base/gaode_entity.js +0 -59
- package/src/base/mars3d_entity.js +0 -50
- package/src/graphic/circle.js +0 -159
- package/src/graphic/divPoint.js +0 -86
- package/src/graphic/imagePoint.js +0 -163
- package/src/graphic/label.js +0 -176
- package/src/graphic/line.js +0 -203
- package/src/graphic/numPoint.js +0 -119
- package/src/graphic/point.js +0 -144
- package/src/graphic/polygon.js +0 -111
- package/src/graphic/rectangle.js +0 -115
- package/src/index.js +0 -105
- package/src/layer/cluster.js +0 -277
- package/src/layer/geoJson.js +0 -174
- package/src/layer/heatMap.js +0 -163
- package/src/layer/layer.js +0 -311
- package/src/layer/pointCloud.js +0 -78
- package/src/map.js +0 -303
- package/src/other/route.js +0 -217
- package/src/util.js +0 -103
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { deepMerge, wgs84ToGcj02Format } from "../util";
|
|
2
|
+
|
|
3
|
+
import siji_entity from "../base/siji_entity";
|
|
4
|
+
export default (hnMap: any) => {
|
|
5
|
+
const defaultOption = {
|
|
6
|
+
id: "",
|
|
7
|
+
position: [],
|
|
8
|
+
speed: 10,
|
|
9
|
+
replaySpeed: 20,
|
|
10
|
+
clockLoop: false,
|
|
11
|
+
image: {
|
|
12
|
+
src: "",
|
|
13
|
+
width: 10,
|
|
14
|
+
height: 10,
|
|
15
|
+
},
|
|
16
|
+
camera: {
|
|
17
|
+
type: "",
|
|
18
|
+
pitch: -30,
|
|
19
|
+
radius: 500,
|
|
20
|
+
},
|
|
21
|
+
polyline: {
|
|
22
|
+
color: "#0000ff",
|
|
23
|
+
width: 2,
|
|
24
|
+
},
|
|
25
|
+
path: {
|
|
26
|
+
color: "#ff0000",
|
|
27
|
+
width: 2,
|
|
28
|
+
},
|
|
29
|
+
label: {
|
|
30
|
+
text: "", // 文本内容
|
|
31
|
+
font_size: 16,
|
|
32
|
+
color: "#ffffff",
|
|
33
|
+
outline: false,
|
|
34
|
+
outlineColor: "#000000",
|
|
35
|
+
outlineWidth: 1,
|
|
36
|
+
outlineOpacity: 1, // 描边透明度
|
|
37
|
+
horizontalOrigin: "left", // 水平对齐方式
|
|
38
|
+
verticalOrigin: "bottom", // 垂直对齐方式
|
|
39
|
+
pixelOffset: [0, 0],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
class mars3d_class {
|
|
44
|
+
type: any = "route";
|
|
45
|
+
id: any = null;
|
|
46
|
+
option: any = JSON.parse(JSON.stringify(defaultOption));
|
|
47
|
+
config: any = null;
|
|
48
|
+
graphic: any = null;
|
|
49
|
+
|
|
50
|
+
constructor(option: any) {
|
|
51
|
+
this.id = option.id;
|
|
52
|
+
deepMerge(this.option, option);
|
|
53
|
+
this.config = this.formatConfig(this.option);
|
|
54
|
+
this.graphic = new mars3d.graphic.FixedRoute(this.config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
formatConfig(option: any) {
|
|
58
|
+
return {
|
|
59
|
+
id: option.id,
|
|
60
|
+
speed: option.speed,
|
|
61
|
+
positions: option.position,
|
|
62
|
+
// leadTime: option.leadTime,
|
|
63
|
+
clockLoop: option.clockLoop, // 是否循环播放
|
|
64
|
+
camera: {
|
|
65
|
+
type: option.camera.type, // 轨迹类型
|
|
66
|
+
pitch: option.camera.pitch,
|
|
67
|
+
radius: option.camera.radius,
|
|
68
|
+
},
|
|
69
|
+
billboard: {
|
|
70
|
+
image: option.image.src,
|
|
71
|
+
width: option.image.width,
|
|
72
|
+
height: option.image.height,
|
|
73
|
+
}, // Marker
|
|
74
|
+
polyline: {
|
|
75
|
+
color: option.polyline.color,
|
|
76
|
+
width: option.polyline.width,
|
|
77
|
+
// showAll: true,
|
|
78
|
+
},
|
|
79
|
+
path: {
|
|
80
|
+
color: option.path.color,
|
|
81
|
+
width: option.path.width,
|
|
82
|
+
}, // 轨迹
|
|
83
|
+
label: {
|
|
84
|
+
text: option.label.text, // 文本内容
|
|
85
|
+
font_size: option.label.textSize,
|
|
86
|
+
color: option.label.color,
|
|
87
|
+
outline: option.label.outline,
|
|
88
|
+
outlineColor: option.label.outLineColor,
|
|
89
|
+
outlineWidth: option.label.outlineWidth,
|
|
90
|
+
outlineOpacity: option.label.outlineOpacity, // 描边透明度
|
|
91
|
+
horizontalOrigin: option.label.horizontalOrigin, // 水平对齐方式
|
|
92
|
+
verticalOrigin: option.label.verticalOrigin, // 垂直对齐方式
|
|
93
|
+
pixelOffset: option.label.pixelOffset,
|
|
94
|
+
},
|
|
95
|
+
// model: {
|
|
96
|
+
// url: option.models.url, // 模型地址
|
|
97
|
+
// scale: option.models.scale, // 模型缩放比例
|
|
98
|
+
// minimumPixelSize: option.models.minimumPixelSize, // 模型最小尺寸
|
|
99
|
+
// clampToGround: option.models.clampToGround, // 模型贴地
|
|
100
|
+
// },
|
|
101
|
+
|
|
102
|
+
attr: option.data,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
set(option: any) {
|
|
107
|
+
deepMerge(this.option, option);
|
|
108
|
+
this.config = this.formatConfig(this.option);
|
|
109
|
+
this.graphic.setOptions(this.config);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
start() {
|
|
113
|
+
this.graphic.start();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pause() {
|
|
117
|
+
this.graphic.pause();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
proceed() {
|
|
121
|
+
this.graphic.proceed();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
stop() {
|
|
125
|
+
this.graphic.stop();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
destroy() {
|
|
129
|
+
this.graphic.destroy();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
flyTo() {
|
|
133
|
+
this.graphic.flyToPoint();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
class gaode_class {
|
|
138
|
+
id: any = null;
|
|
139
|
+
option: any = JSON.parse(JSON.stringify(defaultOption));
|
|
140
|
+
config_graphic: any = null;
|
|
141
|
+
config_path: any = null;
|
|
142
|
+
graphic: any = null;
|
|
143
|
+
pathLayer: any = null;
|
|
144
|
+
path: any = null;
|
|
145
|
+
|
|
146
|
+
constructor(option: any) {
|
|
147
|
+
this.id = option.id;
|
|
148
|
+
deepMerge(this.option, option);
|
|
149
|
+
this.config_graphic = this.formatConfigGraphic(this.option);
|
|
150
|
+
this.config_path = this.formatConfigPath(this.option);
|
|
151
|
+
this.graphic = new AMap.Marker(this.config_graphic);
|
|
152
|
+
this.addPath();
|
|
153
|
+
|
|
154
|
+
this.graphic.on("moving", (e: any) => {
|
|
155
|
+
hnMap.map.map.setCenter(e.target.getPosition(), true);
|
|
156
|
+
});
|
|
157
|
+
hnMap.map.map.setFitView();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
formatConfigGraphic(option: any) {
|
|
161
|
+
const amapPosition = wgs84ToGcj02Format(option.position);
|
|
162
|
+
return {
|
|
163
|
+
position: amapPosition[0],
|
|
164
|
+
speed: option.speed,
|
|
165
|
+
icon: new AMap.Icon({
|
|
166
|
+
image: option.image.src,
|
|
167
|
+
imageSize: new AMap.Size(option.image.width, option.image.height),
|
|
168
|
+
size: new AMap.Size(option.image.width, option.image.height),
|
|
169
|
+
}),
|
|
170
|
+
extData: {
|
|
171
|
+
id: option.id,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
formatConfigPath(option: any) {
|
|
177
|
+
const amapPosition = wgs84ToGcj02Format(option.position);
|
|
178
|
+
return {
|
|
179
|
+
id: option.id + "_path",
|
|
180
|
+
position: amapPosition,
|
|
181
|
+
width: option.polyline.width,
|
|
182
|
+
color: option.polyline.color,
|
|
183
|
+
data: { id: option.id + "_path" },
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
set(option: any) {
|
|
188
|
+
deepMerge(this.option, option);
|
|
189
|
+
this.config_graphic = this.formatConfigGraphic(this.option);
|
|
190
|
+
this.config_path = this.formatConfigPath(this.option);
|
|
191
|
+
this.graphic.setOptions(this.config_graphic);
|
|
192
|
+
this.stop();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
addPath() {
|
|
196
|
+
this.pathLayer = hnMap.map.addLayer(
|
|
197
|
+
new hnMap.Layer({ id: this.id + "_path_layer" })
|
|
198
|
+
);
|
|
199
|
+
if (this.pathLayer) {
|
|
200
|
+
this.path = new hnMap.Line(this.config_path);
|
|
201
|
+
this.pathLayer.addEntity(this.path);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
start() {
|
|
206
|
+
this.config_path.position.reduce((prev: any, next: any) => {
|
|
207
|
+
let t1 = turf.point(prev);
|
|
208
|
+
let t2 = turf.point(next);
|
|
209
|
+
let distance = turf.distance(t1, t2, { units: "meters" });
|
|
210
|
+
this.graphic.moveAlong(this.path.graphic._opts.path, {
|
|
211
|
+
duration: Number(distance / this.config_graphic.speed).toFixed(0),
|
|
212
|
+
autoRotation: true,
|
|
213
|
+
circlable: true,
|
|
214
|
+
});
|
|
215
|
+
return next;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
pause() {
|
|
220
|
+
this.graphic.pauseMove();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
proceed() {
|
|
224
|
+
this.graphic.resumeMove();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
stop() {
|
|
228
|
+
this.graphic.stopMove();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
destroy() {
|
|
232
|
+
this.stop();
|
|
233
|
+
this.graphic.remove();
|
|
234
|
+
this.pathLayer.destroy();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
flyTo() {
|
|
238
|
+
if (this.graphic.getBounds) {
|
|
239
|
+
const bounds = this.graphic.getBounds();
|
|
240
|
+
hnMap.map.map.setBounds(bounds);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class siji_class extends siji_entity {
|
|
246
|
+
type: any = "route";
|
|
247
|
+
id: any = null;
|
|
248
|
+
option: any = JSON.parse(JSON.stringify(defaultOption));
|
|
249
|
+
config_routeline: any = null;
|
|
250
|
+
config_routeplay: any = null;
|
|
251
|
+
imgMarker: any = null;
|
|
252
|
+
chunkData: any = null;
|
|
253
|
+
// timer: any = null;
|
|
254
|
+
|
|
255
|
+
constructor(option: any) {
|
|
256
|
+
super(hnMap);
|
|
257
|
+
this.id = option.id;
|
|
258
|
+
deepMerge(this.option, option);
|
|
259
|
+
this.config_routeline = this.formatConfig(this.option);
|
|
260
|
+
this.config_routeplay = this.formatConfigPlay(this.option);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
formatConfig(option: any) {
|
|
264
|
+
return {
|
|
265
|
+
id: "route-line_" + option.id,
|
|
266
|
+
type: "line",
|
|
267
|
+
source: {
|
|
268
|
+
type: "geojson",
|
|
269
|
+
data: {
|
|
270
|
+
geometry: {
|
|
271
|
+
type: "LineString",
|
|
272
|
+
coordinates: option.position,
|
|
273
|
+
},
|
|
274
|
+
properties: {},
|
|
275
|
+
type: "Feature",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
layout: {
|
|
279
|
+
"line-join": "round",
|
|
280
|
+
"line-cap": "round",
|
|
281
|
+
},
|
|
282
|
+
paint: {
|
|
283
|
+
"line-color": option.polyline.color,
|
|
284
|
+
"line-width": Number(option.polyline.width),
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
formatConfigPlay(option: any) {
|
|
290
|
+
return {
|
|
291
|
+
id: "route-played_" + option.id,
|
|
292
|
+
type: "line",
|
|
293
|
+
source: {
|
|
294
|
+
type: "geojson",
|
|
295
|
+
data: {
|
|
296
|
+
type: "FeatureCollection",
|
|
297
|
+
features: [],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
layout: {
|
|
301
|
+
"line-join": "round",
|
|
302
|
+
"line-cap": "round",
|
|
303
|
+
},
|
|
304
|
+
paint: {
|
|
305
|
+
"line-color": option.path.color,
|
|
306
|
+
"line-width": Number(option.path.width),
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
createCar() {
|
|
312
|
+
if (this.imgMarker) {
|
|
313
|
+
this.imgMarker.remove();
|
|
314
|
+
}
|
|
315
|
+
// 画marker点
|
|
316
|
+
var el = document.createElement("img");
|
|
317
|
+
el.src = this.option.image.src;
|
|
318
|
+
el.style.width = this.option.image.width + "px";
|
|
319
|
+
el.style.height = this.option.image.height + "px";
|
|
320
|
+
this.imgMarker = new SGMap.Marker(el)
|
|
321
|
+
.setLngLat(this.config_routeline.source.data.geometry.coordinates[0])
|
|
322
|
+
.addTo(hnMap.map.map);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
joinLinePoint() {
|
|
326
|
+
const speed = 60;
|
|
327
|
+
// 播放速度,这里选择20倍播放速度
|
|
328
|
+
const replaySpeed = 20;
|
|
329
|
+
// 按照实际车辆跑的速度,每秒跑 60 / 3600 km
|
|
330
|
+
// 车辆位置按每秒刷新20帧算,1/20秒移动的距离为 60 / 3600 / 20 km
|
|
331
|
+
const step = 60 / 3600 / 20;
|
|
332
|
+
// 倍数播放时,每帧移动的步长为 step * replaySpeed
|
|
333
|
+
const scaleSpeed = step * replaySpeed;
|
|
334
|
+
// 线路根据步长scaleSpeed间隔进行插值,插值线总长/步长(distance/scaleSpeed)个点,得到chunkData
|
|
335
|
+
// 注意:线路长度一定,scaleSpeed越小,插值数量越多。线路长度很长的情况下,请调整replaySpeed,避免插值点数过多产生卡顿
|
|
336
|
+
this.chunkData = turf.lineChunk(
|
|
337
|
+
this.config_routeline.source.data,
|
|
338
|
+
scaleSpeed,
|
|
339
|
+
{
|
|
340
|
+
units: "kilometers",
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
return this.chunkData;
|
|
344
|
+
}
|
|
345
|
+
start() {
|
|
346
|
+
const features = this.chunkData.features;
|
|
347
|
+
|
|
348
|
+
let max = features.length;
|
|
349
|
+
let order = 0;
|
|
350
|
+
|
|
351
|
+
let timer = setInterval(() => {
|
|
352
|
+
order++;
|
|
353
|
+
if (order >= max) {
|
|
354
|
+
clearInterval(timer);
|
|
355
|
+
} else {
|
|
356
|
+
const beforePoint = features[order - 1].geometry.coordinates[1];
|
|
357
|
+
const nowPoint = features[order].geometry.coordinates[1];
|
|
358
|
+
// 设置小车的位置
|
|
359
|
+
this.imgMarker.setLngLat(nowPoint);
|
|
360
|
+
// 设置小车的方向
|
|
361
|
+
var bearing = turf.bearing(
|
|
362
|
+
turf.point(beforePoint),
|
|
363
|
+
turf.point(nowPoint)
|
|
364
|
+
);
|
|
365
|
+
bearing && this.imgMarker.setRotation(bearing - 90);
|
|
366
|
+
// 更新已走过的线路图层数据
|
|
367
|
+
this.readerReplayedRouterLayer(order, this.option);
|
|
368
|
+
}
|
|
369
|
+
}, 1000 / 20); // 间隔1000 / 20执行一次,每秒执行20次,可以和上面计算线路的时间间隔配合
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
readerReplayedRouterLayer(order: any, option: any) {
|
|
373
|
+
hnMap.map.map.getSource("route-played_" + option.id).setData({
|
|
374
|
+
type: "FeatureCollection",
|
|
375
|
+
features: this.chunkData.features.slice(0, order + 1),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
set(option: any) {
|
|
380
|
+
deepMerge(this.option, option);
|
|
381
|
+
if (this.imgMarker) {
|
|
382
|
+
this.imgMarker.remove();
|
|
383
|
+
}
|
|
384
|
+
this.createCar();
|
|
385
|
+
|
|
386
|
+
this.config_routeline = this.formatConfig(this.option);
|
|
387
|
+
let mySource_routeline = hnMap.map.map.getSource(
|
|
388
|
+
this.config_routeline.id
|
|
389
|
+
);
|
|
390
|
+
mySource_routeline.setData({
|
|
391
|
+
geometry: {
|
|
392
|
+
type: "LineString",
|
|
393
|
+
coordinates: option.position,
|
|
394
|
+
},
|
|
395
|
+
properties: option.data,
|
|
396
|
+
type: "Feature",
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
for (let key in this.config_routeline) {
|
|
400
|
+
if (this.config_routeline.hasOwnProperty(key)) {
|
|
401
|
+
if (key == "paint") {
|
|
402
|
+
for (let key2 in this.config_routeline[key]) {
|
|
403
|
+
if (this.config_routeline[key].hasOwnProperty(key2)) {
|
|
404
|
+
// 遍历 paint 属性
|
|
405
|
+
hnMap.map.map.setPaintProperty(
|
|
406
|
+
this.config_routeline.id,
|
|
407
|
+
key2,
|
|
408
|
+
this.config_routeline[key][key2]
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
this.config_routeplay = this.formatConfigPlay(this.option);
|
|
416
|
+
|
|
417
|
+
let mySource_routeplay = hnMap.map.map.getSource(
|
|
418
|
+
this.config_routeplay.id
|
|
419
|
+
);
|
|
420
|
+
mySource_routeplay.setData({
|
|
421
|
+
geometry: {
|
|
422
|
+
type: "LineString",
|
|
423
|
+
coordinates: option.position,
|
|
424
|
+
},
|
|
425
|
+
properties: option.data,
|
|
426
|
+
type: "Feature",
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
for (let key in this.config_routeplay) {
|
|
430
|
+
if (this.config_routeplay.hasOwnProperty(key)) {
|
|
431
|
+
if (key == "paint") {
|
|
432
|
+
for (let key2 in this.config_routeplay[key]) {
|
|
433
|
+
if (this.config_routeplay[key].hasOwnProperty(key2)) {
|
|
434
|
+
// 遍历 paint 属性
|
|
435
|
+
hnMap.map.map.setPaintProperty(
|
|
436
|
+
this.config_routeplay.id,
|
|
437
|
+
key2,
|
|
438
|
+
this.config_routeplay[key][key2]
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
this.joinLinePoint();
|
|
446
|
+
this.start();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const fn: any = {
|
|
451
|
+
mars3d: mars3d_class,
|
|
452
|
+
gaode: gaode_class,
|
|
453
|
+
siji: siji_class,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
return fn[hnMap.mapType];
|
|
457
|
+
};
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// import Vue from 'vue'
|
|
2
|
+
//
|
|
3
|
+
// // 渲染vue组件并获取dom结构
|
|
4
|
+
// export function renderComponents(comp, params) {
|
|
5
|
+
// const vNodeDom = document.createElement("div")
|
|
6
|
+
// document.body.appendChild(vNodeDom)
|
|
7
|
+
//
|
|
8
|
+
// const SubVue = Vue.extend(comp)
|
|
9
|
+
// const app = new SubVue({
|
|
10
|
+
// el: vNodeDom,
|
|
11
|
+
// propsData: params
|
|
12
|
+
// })
|
|
13
|
+
// return app.$el
|
|
14
|
+
// }
|
|
15
|
+
|
|
16
|
+
// 对象深度合并
|
|
17
|
+
export function deepMerge(target: any, source: any): any {
|
|
18
|
+
for (let key of Object.keys(source)) {
|
|
19
|
+
if (source[key] instanceof Object && !Array.isArray(source[key])) {
|
|
20
|
+
if (!target[key]) Object.assign(target, {[key]: {}});
|
|
21
|
+
deepMerge(target[key], source[key]);
|
|
22
|
+
} else {
|
|
23
|
+
Object.assign(target, {[key]: source[key]});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return target;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 递归格式化高德坐标系
|
|
30
|
+
export function wgs84ToGcj02Format(position: any): any {
|
|
31
|
+
// 判断是否是数组
|
|
32
|
+
if (position.every((item: any) => Array.isArray(item))) {
|
|
33
|
+
return position.map((item: any) => {
|
|
34
|
+
return wgs84ToGcj02Format(item);
|
|
35
|
+
});
|
|
36
|
+
} else if (position.every((item: any) => typeof item === "object")) {
|
|
37
|
+
return position.map((item: any) => {
|
|
38
|
+
let data = wgs84ToGcj02Format([item.lng, item.lat]);
|
|
39
|
+
return {
|
|
40
|
+
...item,
|
|
41
|
+
lng: data[0],
|
|
42
|
+
lat: data[1],
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
return wgs84ToGcj02(position[0], position[1]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* WGS-84 转 GCJ-02(高德坐标)
|
|
52
|
+
* @param {number} wgsLat - WGS-84纬度
|
|
53
|
+
* @param {number} wgsLng - WGS-84经度
|
|
54
|
+
* @returns {[number, number]} GCJ-02经纬度数组
|
|
55
|
+
*/
|
|
56
|
+
export function wgs84ToGcj02(wgsLng: any, wgsLat: any): any {
|
|
57
|
+
if (!isCorrectPosition(wgsLng, wgsLat)) {
|
|
58
|
+
return [wgsLng, wgsLat];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const a = 6378245.0; // 长半轴
|
|
62
|
+
const ee = 0.00669342162296594323; // 扁率
|
|
63
|
+
|
|
64
|
+
function transformLat(x: any, y: any): any {
|
|
65
|
+
let ret =
|
|
66
|
+
-100.0 +
|
|
67
|
+
2.0 * x +
|
|
68
|
+
3.0 * y +
|
|
69
|
+
0.2 * y * y +
|
|
70
|
+
0.1 * x * y +
|
|
71
|
+
0.2 * Math.sqrt(Math.abs(x));
|
|
72
|
+
ret +=
|
|
73
|
+
((20.0 * Math.sin(6.0 * x * Math.PI) +
|
|
74
|
+
20.0 * Math.sin(2.0 * x * Math.PI)) *
|
|
75
|
+
2.0) /
|
|
76
|
+
3.0;
|
|
77
|
+
ret +=
|
|
78
|
+
((160.0 * Math.sin((y * Math.PI) / 3.0) +
|
|
79
|
+
320 * Math.sin((y * Math.PI) / 30.0)) *
|
|
80
|
+
2.0) /
|
|
81
|
+
3.0;
|
|
82
|
+
return ret;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function transformLng(x: any, y: any): any {
|
|
86
|
+
let ret =
|
|
87
|
+
300.0 +
|
|
88
|
+
x +
|
|
89
|
+
2.0 * y +
|
|
90
|
+
0.1 * x * x +
|
|
91
|
+
0.1 * x * y +
|
|
92
|
+
0.1 * Math.sqrt(Math.abs(x));
|
|
93
|
+
ret +=
|
|
94
|
+
((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(x * Math.PI)) *
|
|
95
|
+
2.0) /
|
|
96
|
+
3.0;
|
|
97
|
+
ret +=
|
|
98
|
+
((150.0 * Math.sin((x * Math.PI) / 3.0) +
|
|
99
|
+
300.0 * Math.sin((x * Math.PI) / 15.0)) *
|
|
100
|
+
2.0) /
|
|
101
|
+
3.0;
|
|
102
|
+
return ret;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0);
|
|
106
|
+
let dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0);
|
|
107
|
+
const radLat = (wgsLat / 180.0) * Math.PI;
|
|
108
|
+
let magic = Math.sin(radLat);
|
|
109
|
+
magic = 1 - ee * magic * magic;
|
|
110
|
+
const sqrtMagic = Math.sqrt(magic);
|
|
111
|
+
dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI);
|
|
112
|
+
dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI);
|
|
113
|
+
const gcjLat = wgsLat + dLat;
|
|
114
|
+
const gcjLng = wgsLng + dLng;
|
|
115
|
+
|
|
116
|
+
return [gcjLng, gcjLat];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 判断是否为正确的坐标
|
|
121
|
+
* @param {number} lat - 纬度
|
|
122
|
+
* @param {number} lng - 经度
|
|
123
|
+
* @returns {boolean}
|
|
124
|
+
*/
|
|
125
|
+
export function isCorrectPosition(lng: any, lat: any): boolean {
|
|
126
|
+
return lng <= 180 && lng >= -180 && lat <= 90 && lat >= -90;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 根据矩形左上角和右上角坐标计算矩形坐标对
|
|
130
|
+
export function createRectangleCoordinates(leftTop: any, rightBottom: any) {
|
|
131
|
+
const [leftTopLng, leftTopLat] = leftTop;
|
|
132
|
+
const [rightBottomLng, rightBottomLat] = rightBottom;
|
|
133
|
+
|
|
134
|
+
// 计算四个角点
|
|
135
|
+
const leftTopPoint = [Number(leftTopLng), Number(leftTopLat)];
|
|
136
|
+
const rightTopPoint = [Number(rightBottomLng), Number(leftTopLat)];
|
|
137
|
+
const rightBottomPoint = [Number(rightBottomLng), Number(rightBottomLat)];
|
|
138
|
+
const leftBottomPoint = [Number(leftTopLng), Number(rightBottomLat)];
|
|
139
|
+
|
|
140
|
+
// 返回五个点的坐标对(首尾相同)
|
|
141
|
+
return [
|
|
142
|
+
leftTopPoint,
|
|
143
|
+
rightTopPoint,
|
|
144
|
+
rightBottomPoint,
|
|
145
|
+
leftBottomPoint,
|
|
146
|
+
leftTopPoint, // 闭合矩形
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getMapRangeHeightByLevel(level: number) {
|
|
151
|
+
// 输入校验:限制在 1~18
|
|
152
|
+
level = Math.max(1, Math.min(18, Math.floor(level)));
|
|
153
|
+
|
|
154
|
+
// 计算指定 level 对应的高度(level >= 2 时使用指数衰减)
|
|
155
|
+
function getHeight(lvl: number) {
|
|
156
|
+
if (lvl === 1) {
|
|
157
|
+
return Infinity; // level=1 表示“最大高度”,逻辑上为无穷大
|
|
158
|
+
} else {
|
|
159
|
+
return 32000000 / Math.pow(2, lvl - 2);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const endHeight = getHeight(level); // 当前 level 的“粗略”高度
|
|
164
|
+
const startHeight = level < 18 ? getHeight(level + 1) : 0; // 下一级更细
|
|
165
|
+
return {startHeight, endHeight};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 根据 level 返回该层级的“中间高度”(几何平均值)
|
|
170
|
+
*
|
|
171
|
+
* @param {number} level - 缩放级别 [1, 18]
|
|
172
|
+
* @returns {number} 中间高度(米)
|
|
173
|
+
*/
|
|
174
|
+
export function getLevelMiddleHeight(level: number): number {
|
|
175
|
+
const {startHeight, endHeight} = getMapRangeHeightByLevel(level)
|
|
176
|
+
// 如果 startHeight 为 0(如 level=18),几何平均会为 0,不合理
|
|
177
|
+
// 所以 level=18 特殊处理:返回 (0 + end)/2 或直接返回 end * 0.7 左右
|
|
178
|
+
if (startHeight === 0) {
|
|
179
|
+
return endHeight * 0.7; // 经验值,贴近“中间感知”
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 几何平均:√(start × end)
|
|
183
|
+
return Math.sqrt(startHeight * endHeight);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 根据地图缩放级别(level),返回对应的视野高度范围
|
|
189
|
+
* 规则:
|
|
190
|
+
* - level 1: 高度 > 32,000,000
|
|
191
|
+
* - level 2 ~ 18: 每级高度减半,level=2 对应 32,000,000
|
|
192
|
+
*
|
|
193
|
+
* @param {number} level - 缩放级别 [1, 18]
|
|
194
|
+
* @returns {[number, number]} [开始高度, 结束高度](单位:米)
|
|
195
|
+
*/
|
|
196
|
+
export function getLevelHeightRange(level: number): [number, number] {
|
|
197
|
+
const {startHeight, endHeight} = getMapRangeHeightByLevel(level)
|
|
198
|
+
return [startHeight, endHeight];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 根据高度反推 level(1~18)
|
|
203
|
+
* @param {number} height - 当前视野高度(米)
|
|
204
|
+
* @returns {number} level
|
|
205
|
+
*/
|
|
206
|
+
export function getHeightToLevel(height:number): number {
|
|
207
|
+
if (height > 32000000) return 1;
|
|
208
|
+
if (height <= 0) return 18;
|
|
209
|
+
|
|
210
|
+
// 解公式:height = 32e6 / 2^(level-2)
|
|
211
|
+
let level = 2 + Math.log(32000000 / height) / Math.log(2);
|
|
212
|
+
level = Math.floor(level);
|
|
213
|
+
|
|
214
|
+
// 限制在 1~18
|
|
215
|
+
return Math.max(1, Math.min(18, level));
|
|
216
|
+
}
|