gdmap-utils 1.0.0
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/package.json +27 -0
- package/src/gdMap/gdHelper.js +203 -0
- package/src/gdMapUtils.js +259 -0
- package/src/index.html +14 -0
- package/src/index.ts +57 -0
- package/src/layers/CollectionPointLayerController.js +140 -0
- package/src/layers/MarkerPointerController.js +243 -0
- package/src/layers/OverlayGroupManager.js +252 -0
- package/tsconfig.json +22 -0
- package/webpack.config.js +51 -0
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gdmap-utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "高德地图工具库",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "webpack --mode production",
|
|
9
|
+
"dev": "webpack --mode development --watch",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"map",
|
|
14
|
+
"utils",
|
|
15
|
+
"typescript"
|
|
16
|
+
],
|
|
17
|
+
"author": "quyue",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^24.10.1",
|
|
21
|
+
"html-webpack-plugin": "^5.6.5",
|
|
22
|
+
"ts-loader": "^9.5.4",
|
|
23
|
+
"typescript": "^5.9.3",
|
|
24
|
+
"webpack": "^5.103.0",
|
|
25
|
+
"webpack-cli": "^6.0.1"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const gdHelperMixin = {
|
|
2
|
+
/**
|
|
3
|
+
* 设置地图中心
|
|
4
|
+
* @param lnglat [xxx,xx]
|
|
5
|
+
* @param zoom 地图层级
|
|
6
|
+
*/
|
|
7
|
+
setCenter(lnglat, zoom) {
|
|
8
|
+
if (zoom !== undefined) {
|
|
9
|
+
this.map.setZoomAndCenter(zoom, lnglat); //同时设置地图层级与中心点
|
|
10
|
+
} else {
|
|
11
|
+
this.map.setCenter(lnglat, true);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
/**
|
|
15
|
+
* 设置地图缩放级别
|
|
16
|
+
* @param zoom 地图层级
|
|
17
|
+
*/
|
|
18
|
+
setZoom(zoom) {
|
|
19
|
+
this.map.setZoom(zoom);
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* 根据地图上添加的覆盖物分布情况,自动缩放地图到合适的视野级别
|
|
23
|
+
* @param {Array} overlays [marker, marker1] 覆盖物数组 缺省为全部覆盖物
|
|
24
|
+
* @param {Boolean} immediately 是否立即过渡
|
|
25
|
+
* @param {Array<Number>} avoid [60,60,60,60] 四周边距,上、下、左、右
|
|
26
|
+
* @param {number} maxZoom 最大 地图zoom 级别 18
|
|
27
|
+
*/
|
|
28
|
+
setFitView(...opts) {
|
|
29
|
+
// 地图适应到最佳视角
|
|
30
|
+
this.map.setFitView(...opts);
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* 创建一个图标
|
|
34
|
+
* @param {[number, number]} size - 图标尺寸,格式为 [width, height]
|
|
35
|
+
* @param {string} image - 图片的 URL 地址
|
|
36
|
+
* @param {[number, number]} imageSize - 图标所用图片的大小,格式为 [width, height]
|
|
37
|
+
* @param {[number, number]} imageOffset - 图标取图的偏移量,格式为 [x, y]
|
|
38
|
+
*/
|
|
39
|
+
createIcon(size, image, imageSize, imageOffset = [0, 0]) {
|
|
40
|
+
return new this.AMap.Icon({
|
|
41
|
+
// 图标尺寸
|
|
42
|
+
size: this.Size(...size),
|
|
43
|
+
// 图标的取图地址
|
|
44
|
+
image: image,
|
|
45
|
+
// 图标所用图片大小
|
|
46
|
+
imageSize: this.Size(...imageSize),
|
|
47
|
+
// 图标取图偏移量
|
|
48
|
+
imageOffset: this.Pixel(...imageOffset),
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* 地物对象的像素尺寸
|
|
53
|
+
* @param {number} width 宽度
|
|
54
|
+
* @param {number} height 高度
|
|
55
|
+
*/
|
|
56
|
+
Size(width, height) {
|
|
57
|
+
return new this.AMap.Size(width, height);
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* 像素坐标,确定地图上的一个像素点。
|
|
61
|
+
* @param {number} x
|
|
62
|
+
* @param {number} y
|
|
63
|
+
*/
|
|
64
|
+
Pixel(x, y) {
|
|
65
|
+
return new this.AMap.Pixel(x, y);
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* 经纬度坐标,用来描述地图上的一个点位置
|
|
69
|
+
* @param {Number} lng 经度值
|
|
70
|
+
* @param {Number} lat 纬度值
|
|
71
|
+
* @param {boolean} noWrap 是否自动将经度值修正到 [-180,180] 区间内
|
|
72
|
+
*/
|
|
73
|
+
LngLat(lng, lat, noWrap = false) {
|
|
74
|
+
return new this.AMap.LngLat(lng, lat, noWrap);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// 加载第三方图层
|
|
78
|
+
createWMSTileLayer(options) {
|
|
79
|
+
let layer = new this.AMap.TileLayer.WMS({
|
|
80
|
+
url: options.wmstxdz,
|
|
81
|
+
blend: true,
|
|
82
|
+
tileSize: 256,
|
|
83
|
+
params: {
|
|
84
|
+
LAYERS: options.wmstcmc,
|
|
85
|
+
VERSION: '1.1.0',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
// 设置图层的层级
|
|
89
|
+
layer.setzIndex(options.hierarchy);
|
|
90
|
+
|
|
91
|
+
layer.setMap(this.map);
|
|
92
|
+
// 保存图层信息
|
|
93
|
+
this.mapTitleLayers[options.id] = layer;
|
|
94
|
+
},
|
|
95
|
+
// 删除图层
|
|
96
|
+
removeWMSTileLayer(id) {
|
|
97
|
+
if (!this.mapTitleLayers[id]) {
|
|
98
|
+
return this.error('图层并不存在,请检查输入!');
|
|
99
|
+
}
|
|
100
|
+
// 删除图层信息
|
|
101
|
+
this.mapTitleLayers[id].setMap(null);
|
|
102
|
+
delete this.mapTitleLayers[id];
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// 海量点数据
|
|
106
|
+
createLabelLayer({ zoom = [1, 20], zIndex = 1000, collision = true, layerClassName, ...rest }) {
|
|
107
|
+
const labelLayer = new this.AMap.LabelsLayer({
|
|
108
|
+
zIndex,
|
|
109
|
+
collision,
|
|
110
|
+
zoom,
|
|
111
|
+
...rest,
|
|
112
|
+
});
|
|
113
|
+
labelLayer.setMap(this.map);
|
|
114
|
+
// HACK: labelLayer获取图层节点方式与原来不同,获取可以抽象marker图层,与labelLayer图层,实现多态
|
|
115
|
+
// this.overlayGroupManagerMap.set(layerClassName,labelLayer);
|
|
116
|
+
return labelLayer;
|
|
117
|
+
},
|
|
118
|
+
// 创建labelMarker标注
|
|
119
|
+
createLabelLayerMarker({ icon, text, position, ...rest }) {
|
|
120
|
+
const label = new this.AMap.LabelMarker({
|
|
121
|
+
icon: icon,
|
|
122
|
+
text: text,
|
|
123
|
+
position: position,
|
|
124
|
+
...rest,
|
|
125
|
+
});
|
|
126
|
+
return label;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
createMarkerCluster(points, { _renderClusterMarker, _renderMarker, gridSize }) {
|
|
130
|
+
return new AMap.MarkerCluster(this.map, points, {
|
|
131
|
+
gridSize: gridSize, // 设置网格像素大小
|
|
132
|
+
renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
|
|
133
|
+
renderMarker: _renderMarker, // 自定义非聚合点样式
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
/*
|
|
137
|
+
创建点位信息窗体
|
|
138
|
+
*/
|
|
139
|
+
createInfoWindow({ content, isCustom = true, closeWhenClickMap = true, ...rest }) {
|
|
140
|
+
return new this.AMap.InfoWindow({
|
|
141
|
+
content: content,
|
|
142
|
+
isCustom,
|
|
143
|
+
closeWhenClickMap: closeWhenClickMap,
|
|
144
|
+
...rest,
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
/*
|
|
148
|
+
清楚地图所有信息窗体
|
|
149
|
+
*/
|
|
150
|
+
clearInfoWindow() {
|
|
151
|
+
this.map.clearInfoWindow();
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
/* 打开高德信息弹框 */
|
|
155
|
+
openInfoWindow(infoWindow, ...rest) {
|
|
156
|
+
infoWindow.open(this.map, ...rest);
|
|
157
|
+
},
|
|
158
|
+
// 绘制线路
|
|
159
|
+
drawPolyline(paths) {
|
|
160
|
+
return new this.AMap.Polyline({
|
|
161
|
+
map: this.map,
|
|
162
|
+
...paths,
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
clearOverlays() {
|
|
166
|
+
this.map.clearMap(); // 清除地图上的所有覆盖物
|
|
167
|
+
},
|
|
168
|
+
removeSingleOverlay(overlay) {
|
|
169
|
+
this.map.remove(overlay); // 清除地图上某个覆盖物
|
|
170
|
+
},
|
|
171
|
+
/**
|
|
172
|
+
* 创建高德地图驾车导航
|
|
173
|
+
* @param {Object} options 导航配置
|
|
174
|
+
* @returns {AMap.Driving} 导航对象
|
|
175
|
+
*/
|
|
176
|
+
createAMapDriving(options) {
|
|
177
|
+
return new this.AMap.Driving(options);
|
|
178
|
+
},
|
|
179
|
+
/**
|
|
180
|
+
* 创建高德地图标注
|
|
181
|
+
* @param {Object} options 标注配置
|
|
182
|
+
* @returns {AMap.Marker} 标注对象
|
|
183
|
+
*/
|
|
184
|
+
createAMapMarker(options) {
|
|
185
|
+
return new this.AMap.Marker({
|
|
186
|
+
map: this.map,
|
|
187
|
+
...options,
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
/**
|
|
191
|
+
* 创建高德地图折线
|
|
192
|
+
* @param {Object} options 折线配置
|
|
193
|
+
* @returns {AMap.Polyline} 折线对象
|
|
194
|
+
*/
|
|
195
|
+
createAMapPolyline(options) {
|
|
196
|
+
return new this.AMap.Polyline({
|
|
197
|
+
map: this.map,
|
|
198
|
+
...options,
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default gdHelperMixin;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import AMapLoader from '@amap/amap-jsapi-loader';
|
|
2
|
+
import gdHelperMixin from './gdHelper.js'; //抽取的高德mixin工具函数
|
|
3
|
+
import OverlayGroupManager from './OverlayGroupManager.js';
|
|
4
|
+
import eventMixin from '../eventMixin.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 针对高德的二次封装
|
|
8
|
+
* 目前高德地图使用的是 GCJ-02 坐标,如果你采集的是 WGS84 坐标或者其他,请先进行坐标转换
|
|
9
|
+
*/
|
|
10
|
+
class GdMapUtils {
|
|
11
|
+
// 地图实例对象
|
|
12
|
+
map = null;
|
|
13
|
+
//高德AMap对象
|
|
14
|
+
AMap = null;
|
|
15
|
+
// 地图Ui对象
|
|
16
|
+
AMapUI = null;
|
|
17
|
+
// loadOpts加载的配置信息 地图配置和加载地图配置分开
|
|
18
|
+
loadOpts = {};
|
|
19
|
+
// 地图容器id
|
|
20
|
+
id = '';
|
|
21
|
+
// 地图的配置对象
|
|
22
|
+
mapOpts = {};
|
|
23
|
+
|
|
24
|
+
mapTitleLayers = {}; //图层map对象 第三方图层
|
|
25
|
+
|
|
26
|
+
// 缓存实例集合
|
|
27
|
+
static mapInstance = new Map();
|
|
28
|
+
|
|
29
|
+
overlayGroupManagerMap = new Map(); //HACK 是否移入到OverlayGroupManager中。
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 加载地图和初始化地图分开
|
|
33
|
+
* @param {Object} options 加载高德初始化地图配置
|
|
34
|
+
*/
|
|
35
|
+
constructor(options) {
|
|
36
|
+
if (!options) {
|
|
37
|
+
this.error('请传入配置对象');
|
|
38
|
+
}
|
|
39
|
+
// 某些API加载前必须设置秘钥
|
|
40
|
+
window._AMapSecurityConfig = {
|
|
41
|
+
securityJsCode: process.env.VUE_APP_GD_MAP_CODE, // 安全密钥
|
|
42
|
+
};
|
|
43
|
+
options.key = process.env.VUE_APP_GD_MAP_KEY;
|
|
44
|
+
this.loadOpts = options;
|
|
45
|
+
this.clickMapRestMarkers = options.clickMapRestMarkers ?? true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
error(msg) {
|
|
49
|
+
console.error(`[AmapUtils Error]:${msg}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 异步加载地图插件
|
|
54
|
+
* @param {String} plugins AMap.ToolBar
|
|
55
|
+
* @returns {Promise}
|
|
56
|
+
* @memberof GdMapUtils
|
|
57
|
+
*/
|
|
58
|
+
loadPlugins(plugins) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
this.AMap.plugin(plugins, function (result) {
|
|
61
|
+
resolve(result);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 异步加载UI插件
|
|
68
|
+
* @param {String} plugins overlay/AwesomeMarker
|
|
69
|
+
* @return {Promise}
|
|
70
|
+
* @memberof GdMapUtils
|
|
71
|
+
*/
|
|
72
|
+
loadUIPlugins(plugins) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
if (!this.AMapUI) {
|
|
75
|
+
reject(new Error('AMapUI is not initialized.')); // 提供错误信息
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.AMapUI.loadUI(plugins, function (result) {
|
|
79
|
+
if (result) {
|
|
80
|
+
resolve(result);
|
|
81
|
+
} else {
|
|
82
|
+
reject(new Error('Failed to load UI plugin.')); // 处理加载失败的情况
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 初始化地图
|
|
90
|
+
* @param {String} id DOM 的id
|
|
91
|
+
* @param {Object} options Map地图配置项
|
|
92
|
+
* @return {Promise}
|
|
93
|
+
* @memberof GdMapUtils
|
|
94
|
+
*/
|
|
95
|
+
initMap(id, options) {
|
|
96
|
+
this.id = id;
|
|
97
|
+
this.mapOpts = options;
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
// 确保每次的AMap都是合法的所以不分开创建
|
|
100
|
+
AMapLoader.load(this.loadOpts)
|
|
101
|
+
.then((AMap) => {
|
|
102
|
+
// 将 AMap 全局对象挂载到 window 上
|
|
103
|
+
window.AMap = AMap;
|
|
104
|
+
|
|
105
|
+
this.AMapUI = window.AMapUI;
|
|
106
|
+
|
|
107
|
+
this.AMap = AMap;
|
|
108
|
+
|
|
109
|
+
this.map = new AMap.Map(this.id, this.mapOpts); //"container"为 <div> 容器的 id
|
|
110
|
+
|
|
111
|
+
resolve(this.map);
|
|
112
|
+
// 将当前实例存储到 mapInstance 中
|
|
113
|
+
GdMapUtils.mapInstance.set(id, this);
|
|
114
|
+
|
|
115
|
+
this.bindMapClickEvent(); //初始化绑定事件
|
|
116
|
+
})
|
|
117
|
+
.catch((e) => {
|
|
118
|
+
reject(e);
|
|
119
|
+
console.error(e);
|
|
120
|
+
throw new Error(e);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 连接到已有地图
|
|
126
|
+
* @param {String} id DOM 的id
|
|
127
|
+
* @param {Object} map 已经存在的地图实例
|
|
128
|
+
* @return {Promise}
|
|
129
|
+
* @memberof GdMapUtils
|
|
130
|
+
*/
|
|
131
|
+
linkToExistMap(id, gdMapIns) {
|
|
132
|
+
if (!id || !('options' in gdMapIns)) {
|
|
133
|
+
//HACK map上挂属性标识他为地图实例
|
|
134
|
+
return this.error('请传入正确的地图id和地图实例');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.id = id;
|
|
138
|
+
// 关联地图配置
|
|
139
|
+
this.mapOpts = gdMapIns.options;
|
|
140
|
+
|
|
141
|
+
this.AMap = AMap; //HACK 异步获取AMap对象, 根据map获取当前地图AMap
|
|
142
|
+
|
|
143
|
+
this.AMapUI = window.AMapUI; //HACK AMapUI 是个啥
|
|
144
|
+
|
|
145
|
+
this.map = gdMapIns; // 关联地图实例
|
|
146
|
+
|
|
147
|
+
this.bindMapClickEvent(); //初始化绑定事件
|
|
148
|
+
// 将当前实例存储到 mapInstance 中
|
|
149
|
+
GdMapUtils.mapInstance.set(id, this);
|
|
150
|
+
|
|
151
|
+
return this.map;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 初始化绑定地图事件
|
|
155
|
+
bindMapClickEvent() {
|
|
156
|
+
this.map.on('click', () => {
|
|
157
|
+
if (this.clickMapRestMarkers) {
|
|
158
|
+
this.overlayGroupManagerMap.forEach((overlayGroup) => {
|
|
159
|
+
overlayGroup.resetActiveMarker(); // 清除图层上的所有覆盖物
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 绑定缩放时间获取当前层级
|
|
165
|
+
this.map.on('zoomchange', () => {
|
|
166
|
+
const zoom = this.map.getZoom(); // 获取当前缩放级别
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//给所有的marker绑定事件
|
|
171
|
+
bindEventMarker(type, clickType, callback) {
|
|
172
|
+
if (!this.getOverlayGroupManager(type)) {
|
|
173
|
+
return this.error('图层不存在,请检查输入!');
|
|
174
|
+
}
|
|
175
|
+
this.getOverlayGroupManager(type).bindEventMarker(clickType, callback); // 绑定事件到图层管理器
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//创建点位
|
|
179
|
+
createMarker(type, Opts) {
|
|
180
|
+
const overlayGroupManager = this.createOverlayGroupManager(Opts, type); // 关联图层管理器
|
|
181
|
+
// 创建图标
|
|
182
|
+
const marker = new AMap.Marker(Opts);
|
|
183
|
+
|
|
184
|
+
// marker上地图
|
|
185
|
+
overlayGroupManager.addOverlay(marker);
|
|
186
|
+
|
|
187
|
+
return marker;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 关联图层管理器
|
|
191
|
+
createOverlayGroupManager(overlays, overlayType) {
|
|
192
|
+
const overlayManager = this.getOverlayGroupManager(overlayType); //获取图层管理器
|
|
193
|
+
|
|
194
|
+
if (overlayManager) return overlayManager; //图层已经关联了
|
|
195
|
+
|
|
196
|
+
const overlayGroupManager = new OverlayGroupManager({
|
|
197
|
+
overlays,
|
|
198
|
+
overlayType,
|
|
199
|
+
map: this.map,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
this.overlayGroupManagerMap.set(overlayType, overlayGroupManager); //保存图层管理器
|
|
203
|
+
|
|
204
|
+
return overlayGroupManager;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 获取图层管理器
|
|
208
|
+
getOverlayGroupManager(overlayType) {
|
|
209
|
+
if (typeof overlayType !== 'string' && overlayType.length === 0) {
|
|
210
|
+
return this.error('请传入图层类型');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return this.overlayGroupManagerMap.get(overlayType);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
updateMarker(marker, Opts) {
|
|
217
|
+
if (!marker) {
|
|
218
|
+
return this.error('参数错误');
|
|
219
|
+
}
|
|
220
|
+
// 获取旧marker的类型
|
|
221
|
+
const { type } = marker.getExtData();
|
|
222
|
+
// 移除点位数据
|
|
223
|
+
this.removeMarker(type, marker);
|
|
224
|
+
// 更新Marker
|
|
225
|
+
this.createMarker(type, Opts);
|
|
226
|
+
}
|
|
227
|
+
//移除某一个marker或者多个marker
|
|
228
|
+
removeMarker(overlayType, overlay) {
|
|
229
|
+
if (!this.overlayGroupManagerMap.has(overlayType)) {
|
|
230
|
+
return this.error('图层不存在,请检查输入!');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const overlayGroupManager = this.getOverlayGroupManager(overlayType);
|
|
234
|
+
|
|
235
|
+
overlayGroupManager.removeOverlay(overlay); // 关联图层管理器
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 清楚所有覆盖物
|
|
239
|
+
removeAllOverlay() {
|
|
240
|
+
//BUG 这里只能移除用overlayGroupManagerManager管理的图层
|
|
241
|
+
this.overlayGroupManagerMap.forEach((overlayGroup) => {
|
|
242
|
+
overlayGroup.OverlayGroup.clearOverlays(); // 清除图层上的所有覆盖物
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 高德地图添加覆盖物
|
|
247
|
+
mapToAdd(overlay, autoFit = true) {
|
|
248
|
+
this.map.add(overlay);
|
|
249
|
+
// 调整到合适的视角
|
|
250
|
+
// autoFit && this.map.setFitView();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// 加载工具类方法到gdMapUtils中
|
|
254
|
+
Object.assign(GdMapUtils.prototype, {
|
|
255
|
+
...eventMixin,
|
|
256
|
+
...gdHelperMixin,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
export default GdMapUtils;
|
package/src/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= htmlWebpackPlugin.options.title %></title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1>地图工具库演示</h1>
|
|
10
|
+
<div id="app">
|
|
11
|
+
<p>使用 F12 打开控制台,查看 MapUtils 的使用示例。</p>
|
|
12
|
+
</div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 地图工具库
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class MapUtils {
|
|
6
|
+
/**
|
|
7
|
+
* 计算两个经纬度之间的距离
|
|
8
|
+
* @param lat1 第一个点的纬度
|
|
9
|
+
* @param lon1 第一个点的经度
|
|
10
|
+
* @param lat2 第二个点的纬度
|
|
11
|
+
* @param lon2 第二个点的经度
|
|
12
|
+
* @returns 距离(单位:米)
|
|
13
|
+
*/
|
|
14
|
+
static calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
|
|
15
|
+
const R = 6371e3; // 地球半径(单位:米)
|
|
16
|
+
const φ1 = lat1 * Math.PI / 180;
|
|
17
|
+
const φ2 = lat2 * Math.PI / 180;
|
|
18
|
+
const Δφ = (lat2 - lat1) * Math.PI / 180;
|
|
19
|
+
const Δλ = (lon2 - lon1) * Math.PI / 180;
|
|
20
|
+
|
|
21
|
+
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
|
|
22
|
+
Math.cos(φ1) * Math.cos(φ2) *
|
|
23
|
+
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
|
|
24
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
25
|
+
|
|
26
|
+
return R * c;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 将经纬度转换为墨卡托坐标
|
|
31
|
+
* @param lat 纬度
|
|
32
|
+
* @param lon 经度
|
|
33
|
+
* @returns 墨卡托坐标 [x, y]
|
|
34
|
+
*/
|
|
35
|
+
static latLngToMercator(lat: number, lon: number): [number, number] {
|
|
36
|
+
const x = lon * 20037508.34 / 180;
|
|
37
|
+
let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
|
|
38
|
+
y = y * 20037508.34 / 180;
|
|
39
|
+
return [x, y];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 将墨卡托坐标转换为经纬度
|
|
44
|
+
* @param x 墨卡托坐标 x
|
|
45
|
+
* @param y 墨卡托坐标 y
|
|
46
|
+
* @returns 经纬度 [lat, lon]
|
|
47
|
+
*/
|
|
48
|
+
static mercatorToLatLng(x: number, y: number): [number, number] {
|
|
49
|
+
const lon = x / 20037508.34 * 180;
|
|
50
|
+
let lat = y / 20037508.34 * 180;
|
|
51
|
+
lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
|
|
52
|
+
return [lat, lon];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
console.log("你好世界哈哈哈");
|
|
56
|
+
|
|
57
|
+
export default MapUtils;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// import useEnvSanStore from "@/store/modules/envSan.js";
|
|
2
|
+
import GdMapUtils from '@/utils/gdMap/gdMapUtils.js';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
CollectionPointLayerController
|
|
6
|
+
*/
|
|
7
|
+
// 海量点图层渲染封装
|
|
8
|
+
export default class CollectionPointLayerController {
|
|
9
|
+
dataList = []; // 数据列表
|
|
10
|
+
|
|
11
|
+
// 去掉私有属性标识
|
|
12
|
+
layerInstance = null; // 图层实例
|
|
13
|
+
|
|
14
|
+
// 去掉私有属性标识
|
|
15
|
+
isLayerCreated = false; // 图层是否已创建
|
|
16
|
+
|
|
17
|
+
activeNames = []; // 存储激活图层显示name
|
|
18
|
+
/**
|
|
19
|
+
* 经纬度坐标,用来描述地图上的一个点位置
|
|
20
|
+
* @param {Object} config 图层的config
|
|
21
|
+
* @param {Function} createOverlay 创建marker的方法
|
|
22
|
+
* @param {Function} noWrap requestCallback 拉去marker请求数据并按规定格式返回
|
|
23
|
+
* @param {Boolean} detectingPosition 是否检测位置变化
|
|
24
|
+
*/
|
|
25
|
+
constructor({ config, createOverlay, requestCallback }) {
|
|
26
|
+
this.config = config ?? {}; //保存图层配置
|
|
27
|
+
|
|
28
|
+
this.createOverlay = createOverlay;
|
|
29
|
+
|
|
30
|
+
this.requestCallback = requestCallback;
|
|
31
|
+
|
|
32
|
+
this.activeNames = [...(this?.config?.extraActiveName ?? []), this.config.name]; //图层额外的激活数组
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 获取地图工具类实例
|
|
36
|
+
getGdMapUtilsIns(id = 'gisMap') {
|
|
37
|
+
return GdMapUtils.mapInstance.get(id);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 创建图层
|
|
41
|
+
async createLayer(gdMapUtils) {
|
|
42
|
+
// 获取数据
|
|
43
|
+
this.dataList = await this.requestCallback();
|
|
44
|
+
|
|
45
|
+
const { dataList, config } = this; //保存数据
|
|
46
|
+
|
|
47
|
+
if (!this.shouldCreationLayer(config.name)) return; //避免网络时间过长用户切换到切tab
|
|
48
|
+
|
|
49
|
+
// 创建海量点渲染
|
|
50
|
+
this.layerInstance = gdMapUtils.createMarkerCluster(dataList, {
|
|
51
|
+
gridSize: 80,
|
|
52
|
+
_renderClusterMarker(context) {
|
|
53
|
+
// 绘制聚合点时调用
|
|
54
|
+
const count = dataList.length;
|
|
55
|
+
const factor = Math.pow(context.count / count, 1 / 18);
|
|
56
|
+
const div = document.createElement('div');
|
|
57
|
+
const Hue = 180 - factor * 180;
|
|
58
|
+
const bgColor = 'hsla(' + Hue + ',100%,50%,0.7)';
|
|
59
|
+
const fontColor = 'hsla(' + Hue + ',100%,20%,1)';
|
|
60
|
+
const borderColor = 'hsla(' + Hue + ',100%,40%,1)';
|
|
61
|
+
const shadowColor = 'hsla(' + Hue + ',100%,50%,1)';
|
|
62
|
+
div.style.backgroundColor = bgColor;
|
|
63
|
+
const size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
|
|
64
|
+
div.style.width = div.style.height = size + 'px';
|
|
65
|
+
div.style.border = 'solid 1px ' + borderColor;
|
|
66
|
+
div.style.borderRadius = size / 2 + 'px';
|
|
67
|
+
div.style.boxShadow = '0 0 1px ' + shadowColor;
|
|
68
|
+
div.innerHTML = context.count;
|
|
69
|
+
div.style.lineHeight = size + 'px';
|
|
70
|
+
div.style.color = fontColor;
|
|
71
|
+
div.style.fontSize = '14px';
|
|
72
|
+
div.style.textAlign = 'center';
|
|
73
|
+
const Pixel = gdMapUtils.Size(-size / 2, -size / 2);
|
|
74
|
+
context.marker.setOffset(Pixel);
|
|
75
|
+
context.marker.setContent(div);
|
|
76
|
+
}, // 自定义聚合点样式
|
|
77
|
+
_renderMarker: (context) => {
|
|
78
|
+
// 外部控制单个marker的样式
|
|
79
|
+
this.createOverlay({ context, gdMapUtils, config }); // 创建marker
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 绑定监听控制label显示
|
|
84
|
+
this.layerInstance.on('click', (e) => {
|
|
85
|
+
const { lnglat, marker, clusterData } = e;
|
|
86
|
+
|
|
87
|
+
if (clusterData.length > 1) {
|
|
88
|
+
//点击集合样式地图放大一级
|
|
89
|
+
gdMapUtils.setCenter(lnglat, false);
|
|
90
|
+
gdMapUtils.map.zoomIn(); // 放大地图
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (marker instanceof AMap.Marker) {
|
|
94
|
+
// marker?.dom?.querySelector('.sydw-label').classList.remove('display-none');
|
|
95
|
+
gdMapUtils.trigger('pointerClick', marker, e, gdMapUtils.map, this.config);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.isLayerCreated = true; // 设置图层创建状态为true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 显示图层
|
|
103
|
+
showLayer(v) {
|
|
104
|
+
if (this.layerInstance && this.dataList.length && this.shouldCreationLayer(v)) {
|
|
105
|
+
this.layerInstance.setData(this.dataList);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// 判断是否跳过图层创建
|
|
109
|
+
shouldCreationLayer(activeName) {
|
|
110
|
+
return this.activeNames.includes(activeName);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 隐藏图层
|
|
114
|
+
hideLayer() {
|
|
115
|
+
if (this.layerInstance && this.dataList.length) {
|
|
116
|
+
this.layerInstance.setData([]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 监听地图类型变化
|
|
121
|
+
handleMapTypeChange(newVal, oldVal) {
|
|
122
|
+
let gdMapUtils = this.getGdMapUtilsIns(); // 获取地图实例
|
|
123
|
+
|
|
124
|
+
if (!gdMapUtils) return; // 如果地图实例不存在,则不执行后续操作
|
|
125
|
+
|
|
126
|
+
if (this.shouldCreationLayer(newVal)) {
|
|
127
|
+
if (this.isLayerCreated) {
|
|
128
|
+
this.showLayer(newVal); // 显示图层
|
|
129
|
+
} else {
|
|
130
|
+
this.createLayer(gdMapUtils); // 创建图层
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
this.hideLayer(); // 隐藏图层
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get dataOfLayer() {
|
|
138
|
+
return this.dataList;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import GdMapUtils from '@/utils/gdMap/gdMapUtils.js';
|
|
2
|
+
|
|
3
|
+
export default class MarkerLayerRender {
|
|
4
|
+
dataList = []; // 数据列表
|
|
5
|
+
|
|
6
|
+
// 去掉私密属性标识
|
|
7
|
+
updatePointerTimer = null;
|
|
8
|
+
|
|
9
|
+
layerInstance = null; // 图层实例
|
|
10
|
+
|
|
11
|
+
isLayerCreated = false; // 图层是否已创建
|
|
12
|
+
|
|
13
|
+
detectingPosition = null; //检测位置的变化
|
|
14
|
+
|
|
15
|
+
activeNames = [];
|
|
16
|
+
/**
|
|
17
|
+
* 经纬度坐标,用来描述地图上的一个点位置
|
|
18
|
+
* @param {Object} config 图层的config
|
|
19
|
+
* @param {Function} createOverlay 创建marker的方法
|
|
20
|
+
* @param {Function} noWrap requestCallback 拉去marker请求数据并按规定格式返回
|
|
21
|
+
* @param {Boolean} detectingPosition 是否检测位置变化
|
|
22
|
+
*/
|
|
23
|
+
constructor({
|
|
24
|
+
//!没有文档还需要了解内部细节,耗费时间精力
|
|
25
|
+
config,
|
|
26
|
+
createOverlay,
|
|
27
|
+
requestCallback,
|
|
28
|
+
detectingPosition = false,
|
|
29
|
+
}) {
|
|
30
|
+
this.config = config ?? {}; //保存图层配置
|
|
31
|
+
|
|
32
|
+
this.createOverlay = createOverlay;
|
|
33
|
+
|
|
34
|
+
this.requestCallback = requestCallback; //
|
|
35
|
+
|
|
36
|
+
this.detectingPosition = detectingPosition; // 是否检测位置变化
|
|
37
|
+
|
|
38
|
+
this.activeNames = [...(this?.config?.extraActiveName ?? []), this.config.name];
|
|
39
|
+
|
|
40
|
+
// 添加页面可见性变化监听
|
|
41
|
+
document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 获取地图工具类实例
|
|
45
|
+
getGdMapUtilsIns(id = 'gisMap') {
|
|
46
|
+
return GdMapUtils.mapInstance.get(id);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 处理页面可见性变化
|
|
50
|
+
handleVisibilityChange() {
|
|
51
|
+
const gdMapUtils = this.getGdMapUtilsIns();
|
|
52
|
+
if (document.hidden) {
|
|
53
|
+
// 页面隐藏时停止检测
|
|
54
|
+
this.stopDetectingPositionChange();
|
|
55
|
+
} else {
|
|
56
|
+
// 页面显示时启动检测
|
|
57
|
+
if (this.detectingPosition && this.layerInstance) {
|
|
58
|
+
this.startDetectingPositionChange(gdMapUtils);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 创建图层
|
|
64
|
+
async createLayer(gdMapUtils) {
|
|
65
|
+
// 获取数据
|
|
66
|
+
this.dataList = await this.requestCallback();
|
|
67
|
+
|
|
68
|
+
if (!this.shouldCreationLayer(this.config.name)) return; // 接口请求缓慢,避免用户切换菜单
|
|
69
|
+
|
|
70
|
+
// 处理数据
|
|
71
|
+
this.dataList.forEach((item) => {
|
|
72
|
+
// const { jd, wd, title } = item;
|
|
73
|
+
// 创建标记 用户自己决定创建marker类型
|
|
74
|
+
|
|
75
|
+
this.createOverlay(gdMapUtils, this.config, item);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.layerInstance = gdMapUtils.getOverlayGroupManager(this.config.className); // 获取图层对象
|
|
79
|
+
|
|
80
|
+
const markers = this.layerInstance.OverlayGroup.getOverlays();
|
|
81
|
+
//HACK 名字不够可读
|
|
82
|
+
gdMapUtils.trigger('markerShowed', markers);
|
|
83
|
+
|
|
84
|
+
//! 不需要响应click的marker如何处理
|
|
85
|
+
gdMapUtils.bindEventMarker(this.config.className, 'click', (e) => {
|
|
86
|
+
const marker = e.target;
|
|
87
|
+
|
|
88
|
+
if (marker.getExtData().type === this.config.className) {
|
|
89
|
+
this.layerInstance.resetActiveMarker(); // 重置激活的标记
|
|
90
|
+
this.layerInstance.setActiveMarker(marker); // 设置激活的标记
|
|
91
|
+
marker.setzIndex(1001);
|
|
92
|
+
gdMapUtils.trigger('pointerClick', marker, e, gdMapUtils.map, this.config);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// 检测车辆经纬度是否发生变化
|
|
97
|
+
this.startDetectingPositionChange(gdMapUtils);
|
|
98
|
+
|
|
99
|
+
this.isLayerCreated = true; // 设置图层显示状态为true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 显示图层
|
|
103
|
+
showLayer() {
|
|
104
|
+
if (this.layerInstance && this.dataList.length && this.shouldCreationLayer()) {
|
|
105
|
+
this.layerInstance.showOverlay(); // 显示图层
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 隐藏图层
|
|
110
|
+
hideLayer() {
|
|
111
|
+
if (this.layerInstance && this.dataList.length) {
|
|
112
|
+
this.layerInstance.hideOverlay(); // 隐藏图层
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 手动触发高亮某个点位
|
|
117
|
+
highlightMarker(id) {
|
|
118
|
+
const layerInstance = this.layerInstance;
|
|
119
|
+
|
|
120
|
+
if (!layerInstance) return; // 如果图层不存在,则不执行后续操作
|
|
121
|
+
|
|
122
|
+
const marker = layerInstance.findLayerMarker(id);
|
|
123
|
+
if (marker) {
|
|
124
|
+
layerInstance.resetActiveMarker(); // 重置激活的标记
|
|
125
|
+
layerInstance.setActiveMarker(marker); // 设置激活的标记
|
|
126
|
+
marker.setzIndex(1001); // 设置zIndex
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 由get 访问器描述符 相较于getter函数不能传递更多参数
|
|
131
|
+
shouldCreationLayer(activeName = this.config.name) {
|
|
132
|
+
return this.activeNames.includes(activeName);
|
|
133
|
+
}
|
|
134
|
+
// 启动检测车辆经纬度变化
|
|
135
|
+
startDetectingPositionChange(getGdMapUtilsIns) {
|
|
136
|
+
if (!this.layerInstance || !this.detectingPosition) return;
|
|
137
|
+
this.stopDetectingPositionChange(); //先停止在开启,避免多次执行
|
|
138
|
+
// 修改定时器属性引用
|
|
139
|
+
this.updatePointerTimer = setInterval(
|
|
140
|
+
() => this.updatePointer(getGdMapUtilsIns),
|
|
141
|
+
this.config.updateTime,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 停止检测车辆经纬度变化
|
|
146
|
+
stopDetectingPositionChange() {
|
|
147
|
+
// 修改定时器属性引用
|
|
148
|
+
clearInterval(this.updatePointerTimer);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 更新车辆位置
|
|
152
|
+
async updatePointer(getGdMapUtilsIns) {
|
|
153
|
+
if (!this.layerInstance) return; // 如果图层不存在,则不执行后续操作
|
|
154
|
+
// 获取车辆数据
|
|
155
|
+
const newestDataList = await this.requestCallback();
|
|
156
|
+
|
|
157
|
+
if (this.config.name !== this.envSanStore.mapActiveType) return; // 接口请求缓慢,避免用户切换菜单
|
|
158
|
+
|
|
159
|
+
// 比较新旧数据,找出需要更新的标记
|
|
160
|
+
const changedData = this.differenceWith(newestDataList, this.dataList);
|
|
161
|
+
|
|
162
|
+
changedData.forEach((item) => {
|
|
163
|
+
const marker = this.layerInstance.findLayerMarker(item.id);
|
|
164
|
+
|
|
165
|
+
const iconImage = item.extData.onLine ? this.config.onLineIcon : this.config.icon;
|
|
166
|
+
// 激活图标不需要更新位置和图标
|
|
167
|
+
const activesMarkerIds = this.layerInstance.activesMarkerIds; // 获取激活的markerId
|
|
168
|
+
|
|
169
|
+
if (!activesMarkerIds.includes(item.id)) {
|
|
170
|
+
if (marker) {
|
|
171
|
+
//存在的marker需要更新位置和图标
|
|
172
|
+
|
|
173
|
+
marker.setPosition(getGdMapUtilsIns.LngLat(item.jd, item.wd));
|
|
174
|
+
|
|
175
|
+
const icon = getGdMapUtilsIns.createIcon(this.config.size, iconImage, this.config.size);
|
|
176
|
+
|
|
177
|
+
marker.setIcon(icon);
|
|
178
|
+
} else {
|
|
179
|
+
//没有的marker需要重新创建
|
|
180
|
+
this.createOverlay(getGdMapUtilsIns, iconImage, item);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
this.dataList = newestDataList; // 更新数据列表
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 比较新旧数据,找出经纬度发生变化的项
|
|
189
|
+
differenceWith(newData, oldData) {
|
|
190
|
+
return newData.filter((nItem) => {
|
|
191
|
+
const oldItem = oldData.find((oItem) => oItem.id === nItem.id); // 没有直接返回
|
|
192
|
+
return !oldItem || nItem.jd !== oldItem.jd || nItem.wd !== oldItem.wd;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 处理地图类型变化
|
|
197
|
+
handleMapTypeChange(newVal, oldVal, id) {
|
|
198
|
+
const gdMapUtils = this.getGdMapUtilsIns(id); // 获取地图实例
|
|
199
|
+
|
|
200
|
+
if (!gdMapUtils) return; // 如果地图实例不存在,则不执行后续操作
|
|
201
|
+
if (this.shouldCreationLayer(newVal)) {
|
|
202
|
+
if (this.isLayerCreated) {
|
|
203
|
+
this.showLayer(); // 显示图层
|
|
204
|
+
} else {
|
|
205
|
+
this.createLayer(gdMapUtils); // 创建图层
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
this.hideLayer(); // 隐藏图层
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 离开中转页时,停止检测车辆经纬度变化
|
|
212
|
+
if (this.shouldCreationLayer(oldVal) && !this.shouldCreationLayer(newVal)) {
|
|
213
|
+
this.stopDetectingPositionChange();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 进入中转页时,启动检测车辆经纬度变化
|
|
217
|
+
if (this.shouldCreationLayer(newVal) && !this.shouldCreationLayer(oldVal)) {
|
|
218
|
+
this.startDetectingPositionChange(gdMapUtils);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 销毁实例时移除事件监听
|
|
223
|
+
destroy() {
|
|
224
|
+
document.removeEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
|
|
225
|
+
|
|
226
|
+
this.layerInstance.OverlayGroup.clearOverlays();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 获取所有marker
|
|
230
|
+
get markerList() {
|
|
231
|
+
//HACK 暂时注释掉
|
|
232
|
+
if (!this.layerInstance) return;
|
|
233
|
+
|
|
234
|
+
const markers = this.layerInstance.OverlayGroup.getOverlays();
|
|
235
|
+
|
|
236
|
+
return markers;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
get dataOfLayer() {
|
|
240
|
+
return this.dataList;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// 扩展事件
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { addClassToDiv } from '@/utils/ruoyi.js';
|
|
2
|
+
export default class OverlayGroupManager {
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
_overlayType = null; //统一管理的marker类型
|
|
6
|
+
|
|
7
|
+
OverlayGroup = null; //分组管理对象
|
|
8
|
+
|
|
9
|
+
events = new Map(); //保存事件的集合
|
|
10
|
+
|
|
11
|
+
activesMarkerIds = []; //保存激活的marker集合
|
|
12
|
+
|
|
13
|
+
overlayActiveIcon = null; //激活的图标
|
|
14
|
+
|
|
15
|
+
overlayDefaultIcon = null;
|
|
16
|
+
|
|
17
|
+
allPointTitlesShow = false; //标识点位title是否打开
|
|
18
|
+
|
|
19
|
+
map = null; //图层关联的Map对象
|
|
20
|
+
// 构造函数
|
|
21
|
+
constructor(options) {
|
|
22
|
+
if (!options || AMap === undefined) {
|
|
23
|
+
return this.error('AMap is undefined or options is undefined');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { overlayType, overlays, map } = options;
|
|
27
|
+
|
|
28
|
+
this.OverlayGroup = new AMap.OverlayGroup(overlays);
|
|
29
|
+
|
|
30
|
+
this._overlayType = overlayType; //图层类型
|
|
31
|
+
|
|
32
|
+
this.overlayActiveIcon = overlays?.activeIcon || null; //图层激活的图标
|
|
33
|
+
|
|
34
|
+
this.overlayDefaultIcon = overlays?.defaultIcon || null; //图层默认的图标
|
|
35
|
+
|
|
36
|
+
this.map = map; //保存图层关联的Map对象
|
|
37
|
+
|
|
38
|
+
this.OverlayGroup.setMap(map); //设置图层的地图对象
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 添加覆盖物
|
|
42
|
+
addOverlay(overlays) {
|
|
43
|
+
if (!overlays) {
|
|
44
|
+
this.error('请传入图层对象');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let overlayList = [].concat(overlays); // 处理传入的参数为数组
|
|
49
|
+
|
|
50
|
+
overlayList.forEach((item) => {
|
|
51
|
+
this.addMarkerBindEvent(item); // 绑定事件
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.OverlayGroup.addOverlays(overlayList);
|
|
55
|
+
}
|
|
56
|
+
// 移除覆盖物
|
|
57
|
+
removeOverlay(overlays) {
|
|
58
|
+
if (!overlays) {
|
|
59
|
+
this.error('请传入图层对象');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let overlayList = [].concat(overlays); // 处理传入的参数为数组
|
|
64
|
+
|
|
65
|
+
this.OverlayGroup.removeOverlays(overlayList);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
hideOverlay() {
|
|
69
|
+
this?.OverlayGroup?.hide();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
showOverlay() {
|
|
73
|
+
this?.OverlayGroup?.show();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//给所有的marker绑定事件
|
|
77
|
+
bindEventMarker(clickType, callback) {
|
|
78
|
+
if (typeof callback !== 'function') {
|
|
79
|
+
this.error('请传入事件回调函数');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 获取地图的所有点位,绑定上事件
|
|
84
|
+
this.OverlayGroup.on(clickType, callback);
|
|
85
|
+
|
|
86
|
+
// 保存事件
|
|
87
|
+
this.events.set(clickType, callback);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
addMarkerBindEvent(marker) {
|
|
91
|
+
// 获取对应marker的事件,绑定给对应的marker
|
|
92
|
+
for (const element of this.events) {
|
|
93
|
+
// 遍历事件集合,给marker绑定事件
|
|
94
|
+
const [clickType, callback] = element;
|
|
95
|
+
|
|
96
|
+
marker.on(clickType, callback);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 查找图层对象中的某一个marker
|
|
101
|
+
findLayerMarker(markerId) {
|
|
102
|
+
if (!markerId) {
|
|
103
|
+
this.error('请传入markerId');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (markerId instanceof AMap.Marker) {
|
|
107
|
+
return markerId; // 如果传入的是marker对象,直接返回
|
|
108
|
+
}
|
|
109
|
+
const marker = this.OverlayGroup.getOverlays().find((item) => {
|
|
110
|
+
return item.getExtData().id === markerId;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return marker || null; // 如果没有找到,返回null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 设置激活的marker
|
|
117
|
+
setActiveMarker(marker) {
|
|
118
|
+
if (this.overlayActiveIcon === null) return; //表明用户不需要激活
|
|
119
|
+
marker = this.findLayerMarker(marker); // 查找图层对象中的某一个marker
|
|
120
|
+
|
|
121
|
+
if (!marker) {
|
|
122
|
+
// 如果没有找到对应的marker
|
|
123
|
+
return this.error('marker is not found');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const curOpts = marker.getIcon()._opts;
|
|
127
|
+
|
|
128
|
+
const activeIcon = marker._originOpts.activeIcon;
|
|
129
|
+
|
|
130
|
+
const icon = this.createIcon(activeIcon, curOpts); // 创建新图标
|
|
131
|
+
|
|
132
|
+
// 获取点击的标记对象
|
|
133
|
+
marker.setIcon(icon);
|
|
134
|
+
|
|
135
|
+
const labelParams = marker.getLabel(); //保存配置
|
|
136
|
+
|
|
137
|
+
// 激活始终显示title
|
|
138
|
+
labelParams.content = labelParams.content.replace('display-none', '');
|
|
139
|
+
|
|
140
|
+
marker.setLabel(labelParams);
|
|
141
|
+
|
|
142
|
+
// 保存激活状态
|
|
143
|
+
this.activesMarkerIds.push(marker.getExtData().id);
|
|
144
|
+
}
|
|
145
|
+
// 点位全选是否打开
|
|
146
|
+
toggleAllPointTitles(selector, val) {
|
|
147
|
+
// this.refreshMap(); //重新加载图层
|
|
148
|
+
const elms = document.querySelectorAll('.amap-marker'); //获取所有的marker元素
|
|
149
|
+
this.allPointTitlesShow = val;
|
|
150
|
+
if (!val) {
|
|
151
|
+
elms.forEach((elm) => {
|
|
152
|
+
elm.querySelector(selector).classList.add('display-none');
|
|
153
|
+
elm.querySelector('.amap-marker-label').style.pointerEvents = 'none';
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
elms.forEach((elm) => {
|
|
157
|
+
elm.querySelector(selector).classList.remove('display-none');
|
|
158
|
+
elm.querySelector('.amap-marker-label').style.pointerEvents = '';
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//业务场景是单个图标为激活状态 重置激活的marker
|
|
164
|
+
resetActiveMarker() {
|
|
165
|
+
// 遍历所有的marker,重置 their icon
|
|
166
|
+
this.OverlayGroup.getOverlays().forEach((item) => {
|
|
167
|
+
if (this.activesMarkerIds.includes(item.getExtData().id)) {
|
|
168
|
+
const curOpts = item.getIcon()._opts;
|
|
169
|
+
|
|
170
|
+
const defaultIcon = item._originOpts.defaultIcon;
|
|
171
|
+
|
|
172
|
+
const icon = this.createIcon(defaultIcon, curOpts); // 创建新图标
|
|
173
|
+
|
|
174
|
+
// 如果是激活的marker,重置图标
|
|
175
|
+
item.setIcon(icon); // 设置默认图标
|
|
176
|
+
|
|
177
|
+
let labelParams = item.getLabel();
|
|
178
|
+
if (!this.allPointTitlesShow) {
|
|
179
|
+
labelParams.content = addClassToDiv(labelParams?.content || '', 'display-none');
|
|
180
|
+
} else {
|
|
181
|
+
labelParams.content = labelParams.content.replace('display-none', '');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
item.setLabel(labelParams);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// 清空激活状态
|
|
188
|
+
this.activesMarkerIds = [];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 获取点位存储的marker数据
|
|
192
|
+
getDataOfMarkers() {
|
|
193
|
+
if (!this.OverlayGroup) return [];
|
|
194
|
+
return this.OverlayGroup.getOverlays().map((item) => item.getExtData());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 错误提示
|
|
198
|
+
error(msg) {
|
|
199
|
+
console.error(`[OverlayGroupManager Error]:${msg}`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//业务场景是单个图标为激活状态 重置激活的marker
|
|
203
|
+
resetActiveNavMarker() {
|
|
204
|
+
// 遍历所有的marker,重置 their icon
|
|
205
|
+
this.OverlayGroup.getOverlays().forEach((item) => {
|
|
206
|
+
if (this.activesMarkerIds.includes(item.getExtData().id)) {
|
|
207
|
+
const curOpts = item.getIcon()._opts;
|
|
208
|
+
|
|
209
|
+
const markerOpts = item._opts;
|
|
210
|
+
|
|
211
|
+
const icon = this.createIcon(this.overlayDefaultIcon, curOpts); // 创建新图标
|
|
212
|
+
|
|
213
|
+
// 如果是激活的marker,重置图标
|
|
214
|
+
item.setIcon(icon); // 设置默认图标
|
|
215
|
+
|
|
216
|
+
item.setLabel(markerOpts.originLabel);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
// 清空激活状态
|
|
220
|
+
this.activesMarkerIds = [];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const gdMixin = {
|
|
225
|
+
// 提取创建 Icon 的逻辑
|
|
226
|
+
createIcon(imageUrl, iconOpts) {
|
|
227
|
+
//HACK 单一原则
|
|
228
|
+
return new AMap.Icon({
|
|
229
|
+
image: imageUrl, // 图标图片 URL
|
|
230
|
+
size: new AMap.Size(...iconOpts.size), // 图标大小
|
|
231
|
+
imageSize: new AMap.Size(...iconOpts.imageSize), // 图片实际大小
|
|
232
|
+
// anchor: "bottom-center", // 图标锚点位置
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// 混入mixin
|
|
238
|
+
Object.assign(OverlayGroupManager.prototype, gdMixin);
|
|
239
|
+
/*
|
|
240
|
+
责单一原则:
|
|
241
|
+
|
|
242
|
+
gdMixin 的职责是封装与 AMap.Icon 相关的逻辑,即创建图标。
|
|
243
|
+
setIcon 是直接操作 marker 对象的方法,属于业务逻辑的一部分,与 AMap.Icon 的创建逻辑分离更符合职责单一原则。
|
|
244
|
+
复用性
|
|
245
|
+
|
|
246
|
+
createIcon 方法可以在多个地方复用,例如在 setActiveMarker 和 resetActiveMarker 中。
|
|
247
|
+
setIcon 是直接操作 marker 的方法,通常只在特定的业务逻辑中使用,复用性较低。
|
|
248
|
+
代码清晰性:
|
|
249
|
+
|
|
250
|
+
将 setIcon 保留在业务逻辑中,可以让代码更直观,便于理解。
|
|
251
|
+
如果将其放入 gdMixin 中,可能会让混入的逻辑变得复杂,降低代码的可读性。
|
|
252
|
+
*/
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES5",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES5", "ES2015", "ES2016", "ES2017", "DOM"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "node",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"noEmit": false
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
entry: './src/index.ts',
|
|
6
|
+
output: {
|
|
7
|
+
// 打包后的文件名
|
|
8
|
+
filename: 'index.js',
|
|
9
|
+
// 输出目录路径
|
|
10
|
+
path: path.resolve(__dirname, 'dist'),
|
|
11
|
+
// 库的全局变量名,在浏览器中可以通过 window.MapUtils 访问
|
|
12
|
+
library: 'MapUtils',
|
|
13
|
+
// 库的输出格式,umd 是一种兼容多种模块系统的格式(CommonJS、AMD、ES modules)
|
|
14
|
+
libraryTarget: 'umd',
|
|
15
|
+
// 浏览器: window node.js:global Web Worker: self
|
|
16
|
+
globalObject: 'this',
|
|
17
|
+
// 为 UMD 模块命名,提高可读性
|
|
18
|
+
umdNamedDefine: true,
|
|
19
|
+
// 构建前清理输出目录
|
|
20
|
+
clean: true
|
|
21
|
+
},
|
|
22
|
+
resolve: {
|
|
23
|
+
extensions: ['.ts', '.tsx', '.js', '.jsx']
|
|
24
|
+
},
|
|
25
|
+
externals: {
|
|
26
|
+
'@amap/amap-jsapi-loader': { // 外部依赖,不打包进 bundle
|
|
27
|
+
commonjs: '@amap/amap-jsapi-loader',
|
|
28
|
+
commonjs2: '@amap/amap-jsapi-loader',
|
|
29
|
+
amd: '@amap/amap-jsapi-loader',
|
|
30
|
+
root: 'AMapLoader'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
module: {
|
|
34
|
+
rules: [
|
|
35
|
+
{
|
|
36
|
+
test: /\.tsx?$/,
|
|
37
|
+
use: 'ts-loader',
|
|
38
|
+
exclude: /node_modules/
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
plugins: [
|
|
43
|
+
new HtmlWebpackPlugin({
|
|
44
|
+
title: 'Map Utils Demo',
|
|
45
|
+
template: path.resolve(__dirname, 'src', 'index.html'),
|
|
46
|
+
filename: 'index.html'
|
|
47
|
+
})
|
|
48
|
+
],
|
|
49
|
+
devtool: 'source-map',
|
|
50
|
+
mode: 'production'
|
|
51
|
+
};
|