hn-map 1.1.7 → 1.1.10

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/src/index.ts CHANGED
@@ -77,19 +77,57 @@ export default class HnMap {
77
77
  * 创建HnMap实例的方法
78
78
  * @param id 地图容器的ID
79
79
  * @param option 地图的配置选项
80
- * @param mapType 地图的类型
80
+ * @param mapType 用户期望的地图类型(如 'gaode', 'siji')
81
81
  */
82
82
  static async create(id: string, option: option, mapType: string) {
83
- // 判断是否在子路径下
84
83
  const basePath = window.location.pathname.endsWith("/")
85
- ? window.location.pathname
86
- : window.location.pathname.substring(
87
- 0,
88
- window.location.pathname.lastIndexOf("/") + 1
84
+ ? window.location.pathname
85
+ : window.location.pathname.substring(
86
+ 0,
87
+ window.location.pathname.lastIndexOf("/") + 1
89
88
  );
89
+
90
+ // 预加载 turf(所有类型都需要)
90
91
  await loadResource(basePath + "lib/turf/turf.min.js", "js");
91
92
 
92
- // 1.根据不同的地图类型,加载相应的资源
93
+ // 尝试创建地图,支持 fallback
94
+ let innerMap: any = null;
95
+ let finalMapType = mapType;
96
+
97
+ try {
98
+ innerMap = await HnMap.tryCreateMap(id, option, mapType, basePath);
99
+ } catch (error) {
100
+ console.warn(`[${mapType}] 地图初始化失败,尝试回退到 mars3d`, error);
101
+ finalMapType = "mars3d";
102
+ try {
103
+ innerMap = await HnMap.tryCreateMap(id, option, "mars3d", basePath);
104
+ } catch (fallbackError) {
105
+ console.error("mars3d 回退也失败了!", fallbackError);
106
+ throw new Error("所有地图类型均无法加载,请检查网络或资源配置");
107
+ }
108
+ }
109
+
110
+ // 创建 HnMap 实例
111
+ HnMap.allowConstruction = true;
112
+ try {
113
+ const instance = new HnMap(id, option, finalMapType, innerMap);
114
+ instance.initModules();
115
+ return instance;
116
+ } finally {
117
+ HnMap.allowConstruction = false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * 尝试根据 mapType 加载资源并创建地图实例
123
+ */
124
+ private static async tryCreateMap(
125
+ id: string,
126
+ option: option,
127
+ mapType: string,
128
+ basePath: string
129
+ ): Promise<any> {
130
+ // 1. 加载对应地图的资源
93
131
  switch (mapType) {
94
132
  case "mars3d":
95
133
  await loadResource(basePath + "lib/Cesium/Widgets/widgets.css", "css");
@@ -97,30 +135,34 @@ export default class HnMap {
97
135
  await loadResource(basePath + "lib/mars3d/mars3d.css", "css");
98
136
  await loadResource(basePath + "lib/mars3d/mars3d.js", "js");
99
137
  await loadResource(
100
- basePath + "lib/mars3d/plugins/heatmap/heatmap.js",
101
- "js"
138
+ basePath + "lib/mars3d/plugins/heatmap/heatmap.js",
139
+ "js"
102
140
  );
103
141
  await loadResource(
104
- basePath + "lib/mars3d/plugins/heatmap/mars3d-heatmap.js",
105
- "js"
142
+ basePath + "lib/mars3d/plugins/heatmap/mars3d-heatmap.js",
143
+ "js"
106
144
  );
107
145
  break;
108
146
  case "gaode":
109
147
  await loadResource(
110
- `https://webapi.amap.com/maps?v=2.0&key=${option.gaode_key}&plugin=AMap.HeatMap,AMap.MarkerCluster,AMap.MoveAnimation`,
111
- "js"
148
+ `https://webapi.amap.com/maps?v=2.0&key=${option.gaode_key}&plugin=AMap.HeatMap,AMap.MarkerCluster,AMap.MoveAnimation`,
149
+ "js"
112
150
  );
113
151
  await loadResource(
114
- "https://a.amap.com/jsapi_demos/static/data3d/lib/three.117.js",
115
- "js"
152
+ "https://a.amap.com/jsapi_demos/static/data3d/lib/three.117.js",
153
+ "js"
116
154
  );
117
155
  await loadResource(
118
- "https://a.amap.com/jsapi_demos/static/data3d/lib/GLTFLoader.117.min.js",
119
- "js"
156
+ "https://a.amap.com/jsapi_demos/static/data3d/lib/GLTFLoader.117.min.js",
157
+ "js"
120
158
  );
121
159
  break;
122
160
  case "siji":
123
161
  await loadResource(option.sj_js_url, "js");
162
+ // 注意:SGMap 可能是全局变量,需确保已挂载
163
+ if (typeof SGMap === "undefined") {
164
+ throw new Error("siji 地图 JS 加载成功但 SGMap 未定义");
165
+ }
124
166
  await SGMap.tokenTask.login(option.sj_app_key, option.sj_app_secret);
125
167
  await SGMap.plugin([
126
168
  "SGMap.DrawPolygonHandler",
@@ -129,24 +171,15 @@ export default class HnMap {
129
171
  "SGMap.GeocodingTask",
130
172
  "SGMap.RoadNetLayer",
131
173
  ]);
132
-
133
174
  break;
175
+ default:
176
+ throw new Error(`不支持的地图类型: ${mapType}`);
134
177
  }
135
178
 
136
179
  // 2. 创建地图对象
137
- const MapClass = map({ id, option, mapType }); // 假设 map 是一个工厂
138
- const innerMap = await MapClass.create(id, option); // 真正的地图实例
139
- // 3. 创建 HnMap 实例(此时传入 map)
140
- HnMap.allowConstruction = true;
141
- try {
142
- const instance = new HnMap(id, option, mapType, innerMap);
143
-
144
- // 4. ✅ 此时 this.map 已存在,再初始化所有模块
145
- instance.initModules();
146
- return instance;
147
- } finally {
148
- HnMap.allowConstruction = false;
149
- }
180
+ const MapClass = map({ id, option, mapType });
181
+ const innerMap = await MapClass.create(id, option); // 这里可能也会失败(如容器不存在、key无效等)
182
+ return innerMap;
150
183
  }
151
184
 
152
185
  // ✅ 添加一个初始化方法,用于注册所有图形类
@@ -1,163 +1,313 @@
1
- import {deepMerge, wgs84ToGcj02Format} from '../util'
1
+ import { deepMerge, wgs84ToGcj02Format } from "../util";
2
2
 
3
3
  export default (hnMap: any) => {
4
+ const defaultOption = {
5
+ id: "",
6
+ position: [],
7
+ allowDrillPick: true,
8
+ max: null,
9
+ min: null,
10
+ radius: 25,
11
+ minOpacity: 0.1,
12
+ maxOpacity: 0.8,
13
+ opacity: 1,
14
+ blur: 0.85,
15
+ gradient: {},
16
+ scaleByDistance: false,
17
+ clampToGround: true,
18
+ data: null,
19
+ };
4
20
 
5
- const defaultOption = {
6
- id: "",
7
- position: [],
8
- allowDrillPick: true,
9
- max: null,
10
- min: null,
11
- radius: 25,
12
- minOpacity: 0.1,
13
- maxOpacity: 0.8,
14
- opacity: 1,
15
- blur: 0.85,
16
- gradient: {},
17
- scaleByDistance: false,
18
- clampToGround: true,
19
- data: null
20
- }
21
-
22
- class mars3d_class {
23
- type: any = 'heatMap'
24
- id: any = null
25
- option: any = JSON.parse(JSON.stringify(defaultOption))
26
- config: any = null
27
- layerEntity: any = null
28
-
29
- constructor(option: any) {
30
- this.id = option.id
31
- deepMerge(this.option, option)
32
- this.config = this.formatConfig(this.option)
33
- this.layerEntity = new mars3d.layer.HeatLayer(JSON.parse(JSON.stringify(this.config)))
34
- }
35
-
36
- formatConfig(option: any) {
37
- return {
38
- id: option.id,
39
- positions: option.position,
40
- allowDrillPick: option.allowDrillPick,
41
- max: option.max,
42
- min: option.min,
43
- heatStyle: {
44
- radius: option.radius,
45
- minOpacity: option.minOpacity,
46
- maxOpacity: option.maxOpacity,
47
- blur: option.blur,
48
- gradient: option.gradient,
49
- },
50
- style: {
51
- opacity: option.opacity,
52
- scaleByDistance: option.scaleByDistance,
53
- clampToGround: option.clampToGround,
54
- },
55
- redrawZoom: true,
56
- attr: option.data
57
- }
58
- }
59
-
60
- set(option: any) {
61
- deepMerge(this.option, option)
62
- this.config = this.formatConfig(this.option)
63
- this.layerEntity.setOptions(this.config)
64
- }
65
-
66
- destroy() {
67
- this.layerEntity.remove(true)
68
- hnMap.map.layerList = hnMap.map.layerList.filter((v: any) => v.id !== this.id)
69
- }
70
-
71
- flyTo() {
72
- this.layerEntity.flyTo()
73
- }
74
-
75
- clear() {
76
- this.layerEntity.clear()
77
- }
78
-
79
- setPosition(position: any) {
80
- deepMerge(this.option, {position})
81
- this.layerEntity.setPositions(position)
82
- }
83
-
84
- show() {
85
- this.layerEntity.show = true
86
- }
87
-
88
- hide() {
89
- this.layerEntity.show = false
90
- }
91
-
92
- }
93
-
94
- class gaode_class {
95
-
96
- id: any = null
97
- option: any = JSON.parse(JSON.stringify(defaultOption))
98
- config: any = null
99
- layerEntity: any = null
100
-
101
- constructor(option: any) {
102
- this.id = option.id
103
- deepMerge(this.option, option)
104
- this.config = this.formatConfig(this.option)
105
- this.layerEntity = new AMap.HeatMap(hnMap.map.map, this.config)
106
- this.layerEntity.setDataSet({data: this.config.data, max: this.config.max, min: this.config.min});
107
-
108
- }
109
-
110
- formatConfig(option: any) {
111
- const data = option.position.map((v: any) => {
112
- return {lng: v.lng, lat: v.lat, count: v.value}
113
- })
114
- const amapData = wgs84ToGcj02Format(data)
115
-
116
- return {
117
- id: option.id,
118
- radius: option.radius,
119
- max: option.max,
120
- min: option.min,
121
- opacity: [option.minOpacity, option.maxOpacity],
122
- gradient: option.gradient,
123
- data: amapData,
124
- extData: {
125
- id: option.id,
126
- data: option.data,
127
- },
128
- }
129
- }
130
-
131
- set(option: any) {
132
- deepMerge(this.option, option)
133
- this.config = this.formatConfig(this.option)
134
- this.layerEntity.setOptions(this.config)
135
- }
136
-
137
- destroy() {
138
- this.layerEntity.setMap(null)
139
- hnMap.map.layerList = hnMap.map.layerList.filter((v: any) => v.id !== this.id)
140
- }
141
-
142
-
143
- flyTo() {
144
- let totalLng = 0;
145
- let totalLat = 0;
146
- this.config.data.map((item: any) => {
147
- totalLng += item.lng;
148
- totalLat += item.lat;
149
- })
150
- const centerLng = totalLng / this.config.data.length;
151
- const centerLat = totalLat / this.config.data.length;
152
- hnMap.map.map.setCenter([centerLng, centerLat])
153
- }
154
-
155
- }
156
-
157
- const fn: any = {
158
- mars3d: mars3d_class,
159
- gaode: gaode_class
160
- }
161
-
162
- return fn[hnMap.mapType]
163
- }
21
+ class mars3d_class {
22
+ type: any = "heatMap";
23
+ id: any = null;
24
+ option: any = JSON.parse(JSON.stringify(defaultOption));
25
+ config: any = null;
26
+ layerEntity: any = null;
27
+
28
+ constructor(option: any) {
29
+ this.id = option.id;
30
+ deepMerge(this.option, option);
31
+ this.config = this.formatConfig(this.option);
32
+ this.layerEntity = new mars3d.layer.HeatLayer(
33
+ JSON.parse(JSON.stringify(this.config))
34
+ );
35
+ }
36
+
37
+ formatConfig(option: any) {
38
+ return {
39
+ id: option.id,
40
+ positions: option.position,
41
+ allowDrillPick: option.allowDrillPick,
42
+ max: option.max,
43
+ min: option.min,
44
+ heatStyle: {
45
+ radius: option.radius,
46
+ minOpacity: option.minOpacity,
47
+ maxOpacity: option.maxOpacity,
48
+ blur: option.blur,
49
+ gradient: option.gradient,
50
+ },
51
+ style: {
52
+ opacity: option.opacity,
53
+ scaleByDistance: option.scaleByDistance,
54
+ clampToGround: option.clampToGround,
55
+ },
56
+ redrawZoom: true,
57
+ flyTo: true,
58
+ attr: option.data,
59
+ };
60
+ }
61
+
62
+ set(option: any) {
63
+ deepMerge(this.option, option);
64
+ this.config = this.formatConfig(this.option);
65
+ this.layerEntity.setOptions(this.config);
66
+ }
67
+
68
+ destroy() {
69
+ this.layerEntity.remove(true);
70
+ hnMap.map.layerList = hnMap.map.layerList.filter(
71
+ (v: any) => v.id !== this.id
72
+ );
73
+ }
74
+
75
+ flyTo() {
76
+ this.layerEntity.flyTo();
77
+ }
78
+
79
+ clear() {
80
+ this.layerEntity.clear();
81
+ }
82
+
83
+ setPosition(position: any) {
84
+ deepMerge(this.option, { position });
85
+ this.layerEntity.setPositions(position);
86
+ }
87
+
88
+ show() {
89
+ this.layerEntity.show = true;
90
+ }
91
+
92
+ hide() {
93
+ this.layerEntity.show = false;
94
+ }
95
+ }
96
+
97
+ class gaode_class {
98
+ id: any = null;
99
+ option: any = JSON.parse(JSON.stringify(defaultOption));
100
+ config: any = null;
101
+ layerEntity: any = null;
102
+
103
+ constructor(option: any) {
104
+ this.id = option.id;
105
+ deepMerge(this.option, option);
106
+ this.config = this.formatConfig(this.option);
107
+ this.layerEntity = new AMap.HeatMap(hnMap.map.map, this.config);
108
+ this.layerEntity.setDataSet({
109
+ data: this.config.data,
110
+ max: this.config.max,
111
+ min: this.config.min,
112
+ });
113
+ }
114
+
115
+ formatConfig(option: any) {
116
+ const data = option.position.map((v: any) => {
117
+ return { lng: v.lng, lat: v.lat, count: v.value };
118
+ });
119
+ const amapData = wgs84ToGcj02Format(data);
120
+
121
+ return {
122
+ id: option.id,
123
+ radius: option.radius,
124
+ max: option.max,
125
+ min: option.min,
126
+ opacity: [option.minOpacity, option.maxOpacity],
127
+ gradient: option.gradient,
128
+ data: amapData,
129
+ extData: {
130
+ id: option.id,
131
+ data: option.data,
132
+ },
133
+ };
134
+ }
135
+
136
+ set(option: any) {
137
+ deepMerge(this.option, option);
138
+ this.config = this.formatConfig(this.option);
139
+ this.layerEntity.setOptions(this.config);
140
+ }
141
+
142
+ destroy() {
143
+ this.layerEntity.setMap(null);
144
+ hnMap.map.layerList = hnMap.map.layerList.filter(
145
+ (v: any) => v.id !== this.id
146
+ );
147
+ }
148
+
149
+ flyTo() {
150
+ let totalLng = 0;
151
+ let totalLat = 0;
152
+ this.config.data.map((item: any) => {
153
+ totalLng += item.lng;
154
+ totalLat += item.lat;
155
+ });
156
+ const centerLng = totalLng / this.config.data.length;
157
+ const centerLat = totalLat / this.config.data.length;
158
+ hnMap.map.map.setCenter([centerLng, centerLat]);
159
+ }
160
+ }
161
+
162
+ class siji_class {
163
+ type: any = "heatMap";
164
+ id: any = null;
165
+ option: any = JSON.parse(JSON.stringify(defaultOption));
166
+ config_heatmap: any = null;
167
+ config_point: any = null;
168
+ layerEntity: any = null;
169
+
170
+ constructor(option: any) {
171
+ this.id = option.id;
172
+ deepMerge(this.option, option);
173
+ hnMap.map.map.addSource("themeData", {
174
+ type: "geojson",
175
+ data: {
176
+ type: "FeatureCollection",
177
+ features: this.option.position.map((v: any) => ({
178
+ type: "Feature",
179
+ geometry: {
180
+ type: "Point",
181
+ coordinates: [v.lng, v.lat],
182
+ },
183
+ properties: {
184
+ value: v.value,
185
+ },
186
+ })),
187
+ },
188
+ });
189
+ this.config_heatmap = this.formatConfig_heatmap(this.option);
190
+ this.config_point = this.formatConfig_point(this.option);
191
+ }
192
+
193
+ formatConfig_heatmap(option: any) {
194
+ let config: any = {};
195
+ config = {
196
+ id: option.id,
197
+ type: "heatmap",
198
+ source: "themeData",
199
+ maxzoom: 17,
200
+ paint: {
201
+ /**
202
+ * 数据点的影响力,weight=10的点相当于十个weight=1的点
203
+ * 下述为插值表达式,输入是点geojson的properties的mag,输出随mag线性增大
204
+ */
205
+ "heatmap-weight": [
206
+ "interpolate",
207
+ ["linear"],
208
+ ["get", "value"],
209
+ 0,
210
+ 0,
211
+ 150,
212
+ 1.5,
213
+ ],
214
+ /**
215
+ * 热力图强度,类似heatmap-weight
216
+ * 下述为插值表达式,输出随zoom线性变化,zoom为0时值为1,zoom为12时值为3
217
+ */
218
+ "heatmap-intensity": [
219
+ "interpolate",
220
+ ["linear"],
221
+ ["zoom"],
222
+ 0,
223
+ 1,
224
+ 17,
225
+ 1,
226
+ ],
227
+ /**
228
+ * 像素的颜色,必须以heatmap-density(热力图像素的密度)为输入
229
+ * 下述为插值表达式,输出随heatmap-density变化而变化
230
+ */
231
+ "heatmap-color": [
232
+ "interpolate",
233
+ ["linear"],
234
+ ["heatmap-density"],
235
+ 0,
236
+ "rgba(255, 0, 0, 0)",
237
+ 0.4,
238
+ option.gradient["0.4"],
239
+ 0.6,
240
+ option.gradient["0.6"],
241
+ 0.8,
242
+ option.gradient["0.8"],
243
+ 0.9,
244
+ option.gradient["0.9"],
245
+ // 0,
246
+ // "rgba(255, 0, 0, 0)",
247
+ // 0.1,
248
+ // option.gradient["0.4"], // "rgba(0, 30, 255, .6)",
249
+ // 0.2,
250
+ // "rgba(7, 208, 255, .6)",
251
+ // 0.3,
252
+ // option.gradient["0.6"], //"#2cc946",
253
+ // 0.4,
254
+ // "#d5fb0c",
255
+ // 0.5,
256
+ // option.gradient["0.8"], //"#e04e4e",
257
+ // 0.6,
258
+ // option.gradient["0.9"], //"#f33900",
259
+ // 0.9,
260
+ // "rgba(243, 57, 0, .6)",
261
+ // 1,
262
+ // "rgba(243, 57, 0, .8)",
263
+ ],
264
+ /**
265
+ * 该值越大,热力图越平滑,信息越不详细。
266
+ * 下述为插值表达式,输出随zoom线性变化,zoom为0时值为8,zoom为9时值为20
267
+ */
268
+ "heatmap-radius": ["interpolate", ["linear"], ["zoom"], 0, 5, 17, 50],
269
+ /**
270
+ * 透明度,输出为1则不透明
271
+ * 下述为插值表达式,输出随zoom线性变化,zoom为5时值为0.8,zoom为12时值为0.4
272
+ */
273
+ "heatmap-opacity": [
274
+ "interpolate",
275
+ ["linear"],
276
+ ["zoom"],
277
+ 5,
278
+ option.maxOpacity, //0.8,
279
+ 17,
280
+ option.minOpacity, // 0.8
281
+ ],
282
+ },
283
+ };
284
+ return config;
285
+ }
286
+
287
+ formatConfig_point(option: any) {
288
+ let config_point: any = {};
289
+ config_point = {
290
+ id: "earthquakes-point",
291
+ type: "circle",
292
+ source: "themeData",
293
+ minzoom: 17,
294
+ paint: {
295
+ "circle-radius": ["interpolate", ["linear"], ["zoom"], 5, 1, 20, 12],
296
+ "circle-color": "rgb(255, 148, 0)",
297
+ "circle-stroke-color": "white",
298
+ "circle-stroke-width": 1,
299
+ "circle-opacity": ["interpolate", ["linear"], ["zoom"], 9, 0, 20, 1],
300
+ },
301
+ };
302
+ return config_point;
303
+ }
304
+ }
305
+
306
+ const fn: any = {
307
+ mars3d: mars3d_class,
308
+ gaode: gaode_class,
309
+ siji: siji_class,
310
+ };
311
+
312
+ return fn[hnMap.mapType];
313
+ };