import AMapLoader from '@amap/amap-jsapi-loader';
import { nanoid } from 'nanoid';
import { deepClone, initDrag } from '@/utils';
import { mergeGeoJsonPolygons, wgs_gcj_encrypts } from '@/utils/gisUtils';
import carImg from '@/assets/images/car.png';
export function useAMap(options) {
let AMap, map, nowLayer, labelsLayer, scale, cluster;
const markers = {
point: []
};
let clickMarker = null;
let addPoints = [];
let layers = [];
let defaultLayer;
// 初始化事件
const initMap = (options) => {
window._AMapSecurityConfig = {
securityJsCode: options.securityJsCode
};
AMapLoader.load({
key: options.key, // 申请好的Web端开发者Key,首次调用 load 时必填
version: !!options.version ? options.version : '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: options.plugins
? options.plugins
: ['AMap.Scale', 'AMap.RangingTool', 'AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.MarkerCluster', 'AMap.DistrictSearch', 'AMap.MoveAnimation', 'AMap.Driving', 'AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.GeoJSON']
}).then((res) => {
AMap = res;
defaultLayer = AMap.createDefaultLayer();
map = new AMap.Map(options.el ? options.el : 'aMap', {
WebGLParams: {
preserveDrawingBuffer: true
},
layers: [defaultLayer],
// 是否为3D地图模式
// viewMode: '3D',
pitch: options.pitch,
// 初始化地图级别
zoom: options.zoom ? options.zoom : 11,
// 初始化地图中心点位置
center: options.center,
// 是否可拖拽
dragEnable: options.dragEnable,
// 是否允许通过鼠标滚轮来缩放
scrollWheel: options.scrollWheel,
maxZoom: options.maxZoom ? options.maxZoom : 20
});
// 初始化比例尺
if (options.showScale) {
scale = new AMap.Scale();
map.addControl(scale);
}
if (typeof options.onLoadCompleted === 'function') {
options.onLoadCompleted(AMap, map);
}
});
};
const getAMap = () => {
return AMap;
};
const getMap = () => {
return map;
};
const getScale = () => {
return scale;
};
const initLayer = (type: string) => {
if (defaultLayer) {
map.removeLayer(defaultLayer);
defaultLayer = null;
}
let keys = ['http://t0.tianditu.gov.cn/vec_w/wmts', 'http://t0.tianditu.gov.cn/cva_w/wmts'];
if (type === 'satellite') {
keys = ['http://t0.tianditu.gov.cn/img_w/wmts', 'http://t0.tianditu.gov.cn/cia_w/wmts'];
}
const layer = new AMap.TileLayer.WMTS({
url: keys[0],
blend: false,
tileSize: 256,
params: {
Layer: 'img',
Version: '1.0.0',
Format: 'tiles',
TileMatrixSet: 'w',
STYLE: 'default',
tk: 'a8df87f1695d224d2679aa805c1268d9'
}
});
const layerMark = new AMap.TileLayer.WMTS({
url: keys[1],
blend: false,
tileSize: 256,
params: {
Layer: type === 'satellite' ? 'cia' : 'cva',
Version: '1.0.0',
Format: 'tiles',
TileMatrixSet: 'w',
STYLE: 'default',
tk: 'a8df87f1695d224d2679aa805c1268d9'
}
});
return [layer, layerMark];
};
// 切换地图
const switchMap = (type: string) => {
layers.forEach((layer) => {
map.removeLayer(layer);
});
layers = initLayer(type);
layers.forEach((layer) => {
layer.setMap(map);
});
};
// 添加搜索的标记的
const addSearchMarker = (item) => {
map.setZoom(18);
map.setCenter(item.lnglat);
addMarker([item], true);
clickMarker = item;
options.onMarkerClick(item);
};
// 添加多个点
const addMarker = (points, notClean?: boolean) => {
if (!notClean) {
clearMarker('point');
}
addPoints = points;
const count = points.length;
const _renderClusterMarker = function (context) {
// 聚合中点个数
const clusterCount = context.count;
const div = document.createElement('div');
div.style.backgroundColor = 'rgba(78,179,211,.5)';
const size = Math.round(25 + Math.pow(clusterCount / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px rgba(78,179,211,1)';
div.style.borderRadius = size / 2 + 'px';
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = '#ffffff';
div.style.fontSize = '12px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div);
context.marker.on('click', (e) => {
const bounds = e.target.getBounds();
map.setZoomAndCenter(map.getZoom() + 1, bounds.getCenter());
});
};
const _renderMarker = function (context) {
const content =
'
' +
'
' +
// '
'+ context.data[0].name +'
' +
'
';
const offset = new AMap.Pixel(-9, -9);
context.marker.setContent(content);
context.marker.setOffset(offset);
context.marker.setExtData(context.data[0]);
context.marker.on('click', function (e) {
const extData = e.target.getExtData();
let index = 0;
let index2 = 0;
for (let i = 0; i < addPoints.length; i++) {
if (addPoints[i].id === extData.id && addPoints[i].imageHover) {
addPoints[i].icon = addPoints[i].imageHover;
e.target.setContent(content);
index++;
} else if (!!clickMarker) {
const extData2 = clickMarker.getExtData();
if (addPoints[i].id === extData2.id) {
addPoints[i].icon = addPoints[i].image;
clickMarker.setContent(content);
index2++;
}
}
if ((!!clickMarker && index === 1 && index2 === 1) || (!clickMarker && index === 1)) {
break;
}
}
addMarker(addPoints);
clickMarker = e.target;
options.onMarkerClick(extData);
});
};
cluster = new AMap.MarkerCluster(
map, //地图实例
points, //海量点数据,数据中需包含经纬度信息字段 lnglat
{
gridSize: 30, //数据聚合计算时网格的像素大小
renderClusterMarker: _renderClusterMarker, //上述步骤的自定义聚合点样式
renderMarker: _renderMarker //上述步骤的自定义非聚合点样式
}
);
points.forEach((item) => {
markers['point'].push(item);
});
};
// 清除所有标加
const clearMarker = (id) => {
if (!cluster || !markers[id]) return;
cluster.setMap(null);
markers[id] = [];
};
const handleHover = (extData, dataType) => {
map.setZoom(18);
map.setCenter([extData.lng, extData.lat]);
setTimeout(() => {
let index = 0;
let index2 = 0;
let data = {};
for (let i = 0; i < addPoints.length; i++) {
if (addPoints[i].id === extData.id.toString() && addPoints[i].dataType === dataType) {
addPoints[i].icon = addPoints[i].imageHover;
index++;
data = addPoints[i];
} else if (!!clickMarker) {
const extData2 = clickMarker.getExtData ? clickMarker.getExtData() : clickMarker;
if (addPoints[i].id === extData2.id) {
addPoints[i].icon = addPoints[i].image;
index2++;
}
}
if ((!!clickMarker && index === 1 && index2 === 1) || (!clickMarker && index === 1)) {
break;
}
}
addMarker(addPoints);
options.onMarkerClick(data);
}, 2000);
};
const getMarkers = () => {
return markers;
};
// 显示信息框
let infoWindow;
const showInfo = (content, position, isCustom) => {
hideInfo();
// 实例化InfoWindow
infoWindow = new AMap.InfoWindow({
// 完全自定义
isCustom: isCustom,
autoMove: false,
offset: new AMap.Pixel(0, -20) // 信息窗体的偏移量
// 可以根据需要设置其他InfoWindow的属性
});
const lnglat = new AMap.LngLat(position[0], position[1]);
// 打开InfoWindow,并设置其内容和位置
infoWindow.setContent(content);
infoWindow.open(map, lnglat);
initDrag(infoWindow.dom);
// 解决2.0版本无法滚动问题
infoWindow.on('mouseover', () => map.setStatus({ zoomEnable: false }));
infoWindow.on('mouseout', () => map.setStatus({ zoomEnable: true }));
};
const hideInfo = (e) => {
map.setStatus({ zoomEnable: true });
if (!!infoWindow) {
infoWindow.close();
if (!!clickMarker && e) {
const extData = clickMarker.getExtData ? clickMarker.getExtData() : clickMarker;
for (let i = 0; i < addPoints.length; i++) {
if (addPoints[i].id === extData.id) {
addPoints[i].icon = addPoints[i].image;
clickMarker = null;
addMarker(addPoints);
break;
}
}
}
}
};
let maskPolygon;
const creatMask = (options, name = '茂名市') => {
new AMap.DistrictSearch({
extensions: 'all',
subdistrict: 0
}).search(name, function (status, result) {
// 外多边形坐标数组和内多边形坐标数组
const outer = [
new AMap.LngLat(-360, 90, true),
new AMap.LngLat(-360, -90, true),
new AMap.LngLat(360, -90, true),
new AMap.LngLat(360, 90, true)
];
options.forEach((option) => {
const holes = result.districtList[0].boundaries;
const pathArray = [outer];
pathArray.push.apply(pathArray, holes);
maskPolygon = new AMap.Polygon({
pathL: pathArray,
strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
fillColor: option.fillColor ? option.fillColor : '#10243b',
fillOpacity: option.fillOpacity ? option.fillOpacity : 0.65
});
maskPolygon.setPath(pathArray);
map.add(maskPolygon);
});
});
};
const removeMask = () => {
if (!!maskPolygon) {
map.remove(maskPolygon);
maskPolygon = null;
}
};
let maskPolygon2 = [];
const creatMask2 = (data, option) => {
if (maskPolygon2 && maskPolygon2.length > 0) {
maskPolygon2.forEach((polygon) => {
polygon.show();
});
} else {
// data = convertCoordinates(data);
// 遮罩部分
// const outer = [
// new AMap.LngLat(-180, 90, true),
// new AMap.LngLat(-180, -90, true),
// new AMap.LngLat(180, -90, true),
// new AMap.LngLat(180, 90, true)
// ];
// const pathArray = [outer];
// 合并区边界
// const data2 = mergeGeoJsonPolygons(data);
// data2.geometry.coordinates.forEach((coords) => {
// pathArray.push(coords[0]);
// });
// const maskPolygon = new AMap.Polygon({
// path: pathArray,
// strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
// strokeOpacity: 1,
// strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
// fillColor: option.fillColor ? option.fillColor : '#10243b',
// fillOpacity: option.fillOpacity ? option.fillOpacity : 0.65
// });
// maskPolygon2.push(maskPolygon);
// map.add(maskPolygon);
// 边界部分
data.features.forEach((feature) => {
if (feature.geometry.type === 'Polygon') {
const polygonPath = feature.geometry.coordinates[0].map((coord) => {
return [coord[0], coord[1]];
});
const polygon = new AMap.Polyline({
path: polygonPath,
strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
clickable: false
});
maskPolygon2.push(polygon);
map.add(polygon);
} else if (feature.geometry.type === 'MultiPolygon') {
feature.geometry.coordinates.forEach((polygonCoords) => {
const polygonPath = polygonCoords.map((ring) => {
return ring.map((coord) => {
return [coord[0], coord[1]];
});
});
const outerPath = polygonPath[0];
const innerPaths = polygonPath.slice(1);
const polygon = new AMap.Polyline({
path: outerPath,
holes: innerPaths,
strokeColor: option.strokeColor ? option.strokeColor : '#268ab9',
strokeOpacity: option.strokeOpacity ? option.strokeOpacity : 1,
strokeWeight: option.strokeWeight ? option.strokeWeight : 1,
clickable: false
});
maskPolygon2.push(polygon);
map.add(polygon);
});
}
});
}
};
const removeMask2 = (isHidden) => {
if (maskPolygon2 && maskPolygon2.length > 0) {
maskPolygon2.forEach((polygon) => {
polygon.hide();
});
if (!isHidden) {
maskPolygon2 = [];
}
}
};
let moveMarker, movePolyline, movePassedPolyline, timerId;
const trackPlayback = (lineArr, speed = 1000) => {
if (timerId) {
clearTimeout(timerId);
}
movePolyline?.remove();
movePassedPolyline?.remove();
moveMarker?.remove();
const icon = new AMap.Icon({
size: new AMap.Size(13, 26),
image: carImg
});
moveMarker = new AMap.Marker({
map: map,
position: lineArr[0],
icon: icon,
anchor: 'center'
});
// 绘制轨迹
movePolyline = new AMap.Polyline({
map: map,
path: lineArr,
showDir: true,
strokeColor: '#28F', //线颜色
// strokeOpacity: 1, //线透明度
strokeWeight: 6 //线宽
// strokeStyle: "solid" //线样式
});
movePassedPolyline = new AMap.Polyline({
map: map,
strokeColor: '#AF5', //线颜色
strokeWeight: 6 //线宽
});
moveMarker.on('moving', function (e) {
movePassedPolyline.setPath(e.passedPath);
map.setCenter(e.target.getPosition(), true);
});
// moveMarker.on('moveend', function (e) {
// index++;
// if (index === lineArr.length - 1) {
// timerId = setTimeout(() => {
// movePolyline.remove();
// movePassedPolyline.remove();
// moveMarker.remove();
// }, 5000);
// }
// });
moveMarker.moveAlong(lineArr, {
// 每一段的时长
duration: speed, //可根据实际采集时间间隔设置
// JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
autoRotation: true
});
return { movePolyline, movePassedPolyline, moveMarker };
};
const drawData = (data) => {
const res = [];
data.forEach((item) => {
let graphic;
if (['rectangle', 'polygon', 'anyLine'].includes(item.type)) {
graphic = new AMap.Polygon({
path: item.path,
strokeColor: item.strokeColor,
strokeOpacity: item.strokeOpacity,
strokeWeight: item.strokeWeight,
fillColor: item.fillColor,
fillOpacity: item.fillOpacity
});
graphic._opts.extData = {
id: item.id
};
map.add(graphic);
res.push(graphic);
} else if (item.type === 'circle') {
graphic = new AMap.Circle({
center: item.center,
radius: item.radius * 100000,
strokeColor: item.strokeColor,
strokeOpacity: item.strokeOpacity,
strokeWeight: item.strokeWeight,
fillColor: item.fillColor,
fillOpacity: item.fillOpacity
});
graphic._opts.extData = {
id: item.id
};
map.add(graphic);
res.push(graphic);
} else if (item.type === 'straightLine') {
graphic = new AMap.Polyline({
path: item.path,
strokeColor: item.strokeColor,
strokeOpacity: item.strokeOpacity,
strokeWeight: item.strokeWeight,
strokeStyle: item.strokeStyle
});
graphic._opts.extData = {
id: item.id
};
map.add(graphic);
res.push(graphic);
} else if (item.type === 'text') {
const { text } = addText(item);
res.push(text);
} else if (item.type === 'measureArea') {
graphic = new AMap.Polygon({
path: item.path,
strokeColor: item.strokeColor,
strokeOpacity: item.strokeOpacity,
strokeWeight: item.strokeWeight,
fillColor: item.fillColor,
fillOpacity: item.fillOpacity
});
graphic._opts.extData = {
id: item.id
};
map.add(graphic);
// 计算区域面积
const area = Math.round(AMap.GeometryUtil.ringArea(item.path));
const text = new AMap.Text({
position: item.path[item.path.length - 1],
text: '区域面积' + area + '平方米',
offset: new AMap.Pixel(-20, -20)
});
text._opts.extData = {
id: item.id
};
data.area = area;
map.add([graphic, text]);
// res.push(text);
} else if (item.type === 'marker') {
// 创建标注点
const marker = new AMap.Marker({
position: new AMap.LngLat(item.longitude, item.latitude), // 标注点的位置
icon: new AMap.Icon({
size: item.size, //图标所处区域大小
image: item.icon
}),
size: item.size
});
marker._opts.extData = {
id: item.id
};
marker.setLabel({
content: '' + item.title + '
',
direction: 'top'
});
map.add(marker);
res.push(marker);
}
});
return res;
};
const addText = (options) => {
// 文本覆盖物的样式
const textStyle = {
fontSize: options.fontSize,
color: options.fontColor,
borderColor: 'transparent',
backgroundColor: 'transparent',
borderWidth: 0,
cursor: 'pointer' // 鼠标悬停时显示指针
};
// 创建文本覆盖物
const text = new AMap.Text({
text: options.text, // 文本内容,可以根据需要自定义
position: options.lnglat, // 文本位置(经纬度)
style: textStyle, // 文本样式
zIndex: 100, // 文本层级
draggable: false // 是否可拖动(可选)
});
// 将文本覆盖物添加到地图
map.add(text);
const id = nanoid();
text._opts.extData = {
id: id
};
const data: any = deepClone(options);
data.id = id;
return { text, data };
};
const convertCoordinates = (geoJson) => {
const features = geoJson.features.map((feature) => {
const geometry = feature.geometry;
let newGeometry;
if (geometry.type === 'Point') {
const [x, y] = geometry.coordinates;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
newGeometry = { ...geometry, coordinates: [obj.lng, obj.lat] };
} else if (geometry.type === 'MultiPoint') {
const newCoordinates = geometry.coordinates.map((coords) => {
const [x, y] = coords;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
return [obj.lng, obj.lat];
});
newGeometry = { ...geometry, coordinates: newCoordinates };
} else if (geometry.type === 'LineString') {
const newCoordinates = geometry.coordinates.map((coords) => {
const [x, y] = coords;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
return [obj.lng, obj.lat];
});
newGeometry = { ...geometry, coordinates: newCoordinates };
} else if (geometry.type === 'MultiLineString') {
const newCoordinates = geometry.coordinates.map((line) =>
line.map((coords) => {
const [x, y] = coords;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
return [obj.lng, obj.lat];
})
);
newGeometry = { ...geometry, coordinates: newCoordinates };
} else if (geometry.type === 'Polygon') {
const newCoordinates = geometry.coordinates.map((polygon) =>
polygon.map((coords) => {
const [x, y] = coords;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
return [obj.lng, obj.lat];
})
);
newGeometry = { ...geometry, coordinates: newCoordinates };
} else if (geometry.type === 'MultiPolygon') {
const newCoordinates = geometry.coordinates.map((polygon) =>
polygon.map((ring) =>
ring.map((coords) => {
const [x, y] = coords;
const obj = wgs_gcj_encrypts([{ lng: x, lat: y }])[0];
return [obj.lng, obj.lat];
})
)
);
newGeometry = { ...geometry, coordinates: newCoordinates };
}
return { ...feature, geometry: newGeometry };
});
return { ...geoJson, features };
};
onMounted(() => {
initMap(options);
});
return {
initMap,
getAMap,
getMap,
switchMap,
addMarker,
addSearchMarker,
clearMarker,
getMarkers,
getScale,
showInfo,
hideInfo,
handleHover,
creatMask,
removeMask,
creatMask2,
removeMask2,
trackPlayback,
drawData
};
}