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 }; }