| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950 |
- // 闭包封装:内部所有变量和方法全部私有化!
- (function (window) {
- /**
- * MapDraw 独立封装类
- * 引入后直接 new MapDraw(mapboxMap实例) 即可使用
- */
- class MapDraw {
- /**
- * 构造函数:new MapDraw() 时自动执行
- * @param {mapboxgl.Map} map - 已经初始化好的 mapbox 实例
- */
- constructor(map, callbacks = {}) {
- const that = this;
- // 接收 map 实例
- that.map = map;
- // 编辑完成后 → 调用这个回调把数据抛出去
- that.onEditComplete = callbacks.onEditComplete || null;
- that.tempData = null; // 当前绘制的图形数据
- that.tempSource = null; // 当前绘制的source
- that.tempPoints = []; // 存储绘制的点
- that.drawType = ''; // 绘制类型
- that.isDrawing = false; // 是否正在绘制
- that.hoveredPolygonId = null; // 当前悬停的面ID
- that.tempLineSource = null; // 当前绘制的线的source
- that.itemId = ''; // 当前绘制的图形ID
- that.totalDistance = 0; // 总距离
- that.drawMarkers = []; // 存储节点标签
- that.moveMarkerTips = null; //移动节点显示提示标签
- // 用一个变量防止双击触发单击
- that.lastClickTime = 0;
- that.clickTimer = null;
- that.clickCount = 0;
- // 编辑相关状态
- that.mouseDownPos = null; // 鼠标按下时的位置
- that.isDragWhole = false; // 拖拽整个图形
- that.lastLngLat = null; // 最后一次点击的经纬度
- that.isEditMode = false; // 编辑模式
- that.isEditFlag = false; // 是否正在编辑
- that.selectedFeature = null; // 当前选中的图形
- that.selectedFeatureId = ''; // 当前选中图形的ID
- that.layersIds = []; // 当前选中图形的图层ID
- that.editMarkers = []; // 存储编辑节点的标记
- that.isDragging = false; // 是否正在拖拽
- that.draggingPointIndex = -1; // 正在拖拽的节点索引
- that.dragEventHandlers = null; // 拖拽事件处理器
- that.dragEventDrawHandlers = null; // 拖拽事件处理器2
- that.unit = 'kilometers'; // 单位 (kilometers/meters)
- that.unitLabel = 'km'; // 单位标签 (km/m)
- that.editLayerIds = []; // 当前选中图形的图层ID
-
- // 缓存事件句柄,用于精准解绑(核心防重复触发)
- that._mapClickHandle = null;
- that._mapDblClickHandle = null;
- that._mapMouseDownHandle = null;
- that._mapMouseMoveHandle = null;
- that._mapMouseUpHandle = null;
- // 加载地图绘制图层
- that.loadDrawLayer();
- // 绑定地图事件
- that.bindMapEvents();
- }
- // 编辑后回调返回的完整数据格式
- getCompleteFeature(feature) {
- const that = this;
- // 复制一个全新的对象,和原 feature 无关
- let feat = JSON.parse(JSON.stringify(feature));
- delete feat.properties.drawType;
- delete feat.properties.isCircle;
- if (that.onEditComplete) {
- that.onEditComplete({
- type: "Feature",
- geometry: feat.geometry,
- properties: feat.properties || {}
- });
- }
- }
- // 绑定地图事件
- bindMapEvents() {
- const that = this;
- // ========== 精准解绑所有旧鼠标事件,防止触发两次 ==========
- if(that._mapClickHandle) that.map.off('click', that._mapClickHandle);
- if(that._mapDblClickHandle) that.map.off('dblclick', that._mapDblClickHandle);
- if(that._mapMouseDownHandle) that.map.off('mousedown', that._mapMouseDownHandle);
- if(that._mapMouseMoveHandle) that.map.off('mousemove', that._mapMouseMoveHandle);
- if(that._mapMouseUpHandle) that.map.off('mouseup', that._mapMouseUpHandle);
- // 清空旧句柄
- that._mapClickHandle = null;
- that._mapDblClickHandle = null;
- that._mapMouseDownHandle = null;
- that._mapMouseMoveHandle = null;
- that._mapMouseUpHandle = null;
- // 初始化编辑鼠标事件
- that.initMapEditEvents();
- // 单击事件【缓存句柄】
- that._mapClickHandle = (e) => {
- that.handleFeatureClick(e);
- if (that.isEditMode) {
- const now = Date.now();
- const DOUBLE_CLICK_THRESHOLD = 300;
- clearTimeout(that.clickTimer);
- if (now - that.lastClickTime > DOUBLE_CLICK_THRESHOLD) {
- that.clickCount = 1;
- that.lastClickTime = now;
- that.clickTimer = setTimeout(() => {
- if (that.clickCount === 1) {
- that.handleMapClick(e);
- }
- that.clickCount = 0;
- }, 10);
- } else {
- that.clickCount = 2;
- that.lastClickTime = 0;
- }
- }
- };
- that.map.on('click', that._mapClickHandle);
- // 双击事件【缓存句柄】
- that._mapDblClickHandle = (e) => {
- clearTimeout(that.clickTimer);
- that.clickCount = 0;
- that.lastClickTime = 0;
- that.handleMapDblClick(e);
- };
- that.map.on('dblclick', that._mapDblClickHandle);
- // ========== 键盘 DEL 删除选中节点 ==========
- that.bindDeleteHotkey();
- }
- initMapEditEvents() {
- const that = this;
- // 鼠标按下【缓存句柄】
- that._mapMouseDownHandle = (evt) => {
- // console.log('[ _mapMouseDownHandle ] >', that.selectedFeature)
- if (!that.selectedFeature) return;
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
- // console.log('[ _mapMouseDownHandle ] >', evt)
- that.mouseDownPos = evt.point;
- that.lastLngLat = evt.lngLat;
- that.isDragging = false;
- evt.preventDefault();
- that.map.getCanvas().style.cursor = 'grabbing';
- };
- that.map.on('mousedown', that._mapMouseDownHandle);
- // 鼠标移动【缓存句柄】
- that._mapMouseMoveHandle = (evt) => {
- if (!that.selectedFeature) return;
- if ((that.isEditMode || that.isEditFlag) && that.isDragging) {
- that.map.getCanvas().style.cursor = 'move';
- that.handleDrag(evt);
- }
-
- // console.log('[ _mapMouseMoveHandle ] >', that.selectedFeature)
- if (!that.mouseDownPos || !that.selectedFeature) return;
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
- that.draggingPointIndex = -1;
- const dx = Math.abs(evt.point.x - that.mouseDownPos.x);
- const dy = Math.abs(evt.point.y - that.mouseDownPos.y);
- if (dx > 1 || dy > 1) {
- that.isDragging = true;
- const curLngLat = evt.lngLat;
- const dLng = curLngLat.lng - that.lastLngLat.lng;
- const dLat = curLngLat.lat - that.lastLngLat.lat;
- that.clearEditMarkers();
- that.moveWholeFeature(dLng, dLat);
- that.lastLngLat = curLngLat;
- }
- };
- that.map.on('mousemove', that._mapMouseMoveHandle);
- // 鼠标抬起【缓存句柄】
- that._mapMouseUpHandle = () => {
- if (!that.selectedFeature) return;
-
- that.mouseDownPos = null;
- if (that.isDragging) {
- that.getCompleteFeature(that.selectedFeature);
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area'){
- that.clearEditMarkers();
- }else{
- that.showEditMarkers();
- }
- }else{
- that.clearEditMarkers();
- }
- // console.log('[ _mapMouseUpHandle ] >', that.selectedFeature)
- that.isDragging = false;
- // that.draggingPointIndex = -1;
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] });
- that.map.getCanvas().style.cursor = '';
- // console.log('[ _mapMouseUpHandle==== ] >', that.selectedFeature)
- };
- that.map.on('mouseup', that._mapMouseUpHandle);
- }
- // 剩余原有方法完全不变
- loadDrawLayer(){
- const that = this;
- if(!that.map.getSource('temp-line')){
- that.map.addSource('temp-line', {
- type: 'geojson',
- data: { type: 'FeatureCollection', features: [] }
- });
- that.map.addLayer({
- id: 'temp-line-layer',
- type: 'line',
- source: 'temp-line',
- paint: {
- 'line-color': 'red',
- 'line-width': 2,
- 'line-dasharray': [3, 3]
- }
- });
- }
- that.tempLineSource = that.map.getSource('temp-line');
- that.map.on('mousemove', (e) => {
- if (!that.isDrawing) return
- if(that.moveMarkerTips){
- that.moveMarkerTips.setLngLat(e.lngLat);
- }else{
- let text = "鼠标双击完成";
- if(that.drawType === 'circle' || that.drawType === 'rectangle'){
- text = "鼠标双击/单击完成";
- }
- that.addMoveMarkerTips(e.lngLat, text);
- }
- const end = [e.lngLat.lng, e.lngLat.lat];
- if (that.drawType === 'rectangle'){
- if (that.tempPoints.length < 1) return;
- const bbox = [
- Math.min(that.tempPoints[0][0], end[0]),
- Math.min(that.tempPoints[0][1], end[1]),
- Math.max(that.tempPoints[0][0], end[0]),
- Math.max(that.tempPoints[0][1], end[1])
- ];
- that.addToMap(turf.bboxPolygon(bbox))
- }else if (that.drawType === 'circle' && that.tempPoints.length >= 1){
- let center = that.tempPoints[0];
- let radius = turf.distance(turf.point(center), turf.point(end), { units: 'kilometers' })
- that.addToMap(turf.circle(center, radius, 64))
- }
- if (that.tempPoints.length < 1 || that.drawType === 'rectangle') return;
- const mousePt = [e.lngLat.lng, e.lngLat.lat];
- const lastPt = that.tempPoints[that.tempPoints.length - 1];
- const line = turf.lineString([lastPt, mousePt]);
- that.tempLineSource.setData(line);
- });
- }
- startDraw(type, flag, unit) {
- const that = this;
- that.drawType = type
- that.isDrawing = true
- that.tempPoints = []
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] })
- that.itemId = 'draw-' + Date.now();
- that.map.getCanvas().style.cursor = 'crosshair';
- that.totalDistance = 0;
- that.unit = unit || 'kilometers';
- that.unitLabel = that.unit === 'kilometers' ? 'km' : 'm';
- flag = flag || true;
- that.isEditDrawTool(flag);
- }
- isEditDrawTool(flag) {
- const that = this;
- that.isEditMode = flag;
- if (!that.isEditMode) {
- that.clearEditMarkers();
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- that.layersIds = [];
- }
- // console.log('[ isEditDrawTool ] >', that.selectedFeature)
- }
- isEdit(flag) {
- const that = this;
- that.isEditFlag = flag;
- if (!that.isEditFlag) {
- that.clearEditMarkers();
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- that.layersIds = [];
- }
- // console.log('[ isEdit ] >', that.selectedFeature)
- }
-
- handleMapClick(e) {
- const that = this;
- if (!that.isDrawing) return
- const coord = [e.lngLat.lng, e.lngLat.lat]
- that.tempPoints.push(coord)
- if (that.drawType === 'point') {
- let feature = turf.point(coord)
- feature.properties.drawType = 'point'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- that.finishDraw()
- }
- if (that.drawType === 'line' && that.tempPoints.length >= 2) {
- let feature = turf.lineString(that.tempPoints)
- feature.properties.drawType = 'line'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- }
- if (that.drawType === 'polygon' && that.tempPoints.length >= 2) {
- if(that.tempPoints.length >= 3){
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
- feature.properties.drawType = 'polygon'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- }else{
- let feature = turf.lineString(that.tempPoints)
- feature.properties.drawType = 'polygon'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- }
- }
- if (that.drawType === 'circle' && that.tempPoints.length >= 2) {
- const center = that.tempPoints[0]
- const radius = turf.distance(turf.point(center), turf.point(that.tempPoints[1]), { units: that.unit })
- let feature = turf.circle(center, radius, 64)
- feature.properties.drawType = 'circle'
- feature.properties.isCircle = true;
- that.addToMap(feature)
- that.finishDraw()
- }
- if (that.drawType === 'distance' && that.tempPoints.length >= 2) {
- let feature = turf.lineString(that.tempPoints)
- feature.properties.drawType = 'distance'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- const distance = turf.distance(turf.point(that.tempPoints[0]), turf.point(that.tempPoints[1]), { units: that.unit })
- that.totalDistance += distance;
- that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + " " + that.unitLabel);
- }
- if (that.drawType === 'area' && that.tempPoints.length >= 2) {
- if(that.tempPoints.length >= 3){
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
- feature.properties.drawType = 'area'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- const area = turf.area(feature)
- that.totalDistance += area;
- }else{
- let feature = turf.lineString(that.tempPoints)
- feature.properties.drawType = 'area'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- }
- }
- if (that.drawType === 'rectangle' && that.tempPoints.length >= 2) {
- that.finishDraw()
- }
- that.updateTempLayer()
- }
- handleMapDblClick(e) {
- const that = this;
- if (!that.isDrawing || that.drawType === 'point') return
- if (that.drawType === 'area' && that.tempPoints.length >= 2) {
- if(that.tempPoints.length >= 3){
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
- feature.properties.drawType = 'area'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- const areaNumber = (turf.area(feature)/1000000).toFixed(2);
- that.addMarker(e.lngLat, areaNumber + ' 平方公里');
- }else{
- let feature = turf.lineString(that.tempPoints)
- feature.properties.drawType = 'area'
- feature.properties.isCircle = false;
- that.addToMap(feature)
- const distance = turf.distance(turf.point(that.tempPoints[0]), turf.point(that.tempPoints[1]), { units: that.unit })
- that.totalDistance += distance;
- that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + " " + that.unitLabel);
- }
- }
- that.finishDraw()
- }
- updateTempLayer() {
- const that = this;
- let feature
- if (that.drawType === 'point' && that.tempPoints.length) feature = turf.point(that.tempPoints[0])
- else if (that.drawType === 'line' && that.tempPoints.length >= 2) feature = turf.lineString(that.tempPoints)
- else if (that.drawType === 'polygon' && that.tempPoints.length >= 2) feature = turf.lineString([...that.tempPoints, that.tempPoints[0]])
- else if (that.drawType === 'circle' && that.tempPoints.length >= 1) feature = turf.point(that.tempPoints[0])
- else if (that.drawType === 'distance' && that.tempPoints.length >= 2) feature = turf.lineString(that.tempPoints)
- else if (that.drawType === 'area' && that.tempPoints.length >= 2) feature = turf.lineString([...that.tempPoints, that.tempPoints[0]])
-
- that.tempSource?.setData(feature || { type: 'FeatureCollection', features: [] })
- }
- addMoveMarkerTips(pt, text) {
- const that = this;
- let el = document.createElement('div');
- el.className = 'marker-label-tips';
- el.innerText = text;
- that.moveMarkerTips = new mapboxgl.Marker(el, { anchor: 'center', offset: [0, -30] })
- .setLngLat(pt)
- .addTo(that.map);
- }
- addMarker(pt, text) {
- const that = this;
- let el = document.createElement('div');
- el.className = 'marker-label-tips';
- el.innerText = text;
- let marker = new mapboxgl.Marker(el, { anchor: 'center' })
- .setLngLat(pt)
- .addTo(that.map);
- that.drawMarkers.push(marker);
- }
- addEditMarker(coord, index) {
- const that = this;
- let el = document.createElement('div');
- el.className = 'edit-marker';
- el.style.width = '10px';
- el.style.height = '10px';
- el.style.borderRadius = '50%';
- el.style.backgroundColor = 'red';
- el.style.border = '2px solid white';
- el.style.cursor = 'pointer';
- el.addEventListener('mousedown', (e) => {
- e.stopPropagation();
- that.isDragging = true;
- that.draggingPointIndex = index;
- that.map.getCanvas().style.cursor = 'grabbing';
- });
-
- let marker = new mapboxgl.Marker(el)
- .setLngLat(coord)
- .addTo(that.map);
- that.editMarkers.push(marker);
- if(!that.selectedFeature.properties.isCircle){
- that.insertTwoMiddlePoints(marker, index);
- }
- }
- clearNodeMarkers() {
- const that = this;
- that.drawMarkers.forEach(m => m.remove());
- that.drawMarkers = [];
- }
- clearEditMarkers() {
- const that = this;
- that.editMarkers.forEach(marker => marker.remove());
- that.editMarkers = [];
- }
- showEditMarkers() {
- const that = this;
- if (!that.selectedFeature) return;
- const feature = that.selectedFeature;
- if (feature.type === 'Feature') {
- if (feature.geometry.type === 'Point') {
- that.addEditMarker(feature.geometry.coordinates, 0);
- } else if (feature.geometry.type === 'LineString') {
- feature.geometry.coordinates.forEach((coord, index) => {
- that.addEditMarker(coord, index);
- });
- } else if (feature.geometry.type === 'Polygon') {
- feature.geometry.coordinates[0].forEach((coord, index) => {
- if(index == feature.geometry.coordinates[0].length - 1){
- return;
- }
- that.addEditMarker(coord, index);
- });
- }
- }
- }
- clearAll() {
- const that = this;
- that.clearNodeMarkers();
- that.clearEditMarkers();
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- that.layersIds = [];
- const layers = that.map.getStyle().layers;
- layers.sort((a, b) => a.type.localeCompare(b.type));
- layers.forEach(layer => {
- if (layer.id.startsWith('draw-')) {
- if(layer.id.indexOf("-point") !== -1){
- that.map.removeLayer(layer.id)
- }
- if(layer.id.indexOf("-line") !== -1){
- that.map.removeLayer(layer.id)
- let ids = layer.id.replace("-line",'');
- that.map.removeSource(ids)
- }
- if(layer.id.indexOf("-polygon") !== -1){
- that.map.removeLayer(layer.id)
- }
- }
- })
- }
- addToMap(feature) {
- const that = this;
- let id = that.itemId;
- if (!that.map.getSource(id)) {
- that.map.addSource(id, { type: 'geojson', data: feature })
- if(that.drawType === 'line'){
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else if(that.drawType === 'polygon'){
- if(that.tempPoints.length >= 3){
- that.map.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
- }
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else if(that.drawType === 'circle'){
- that.map.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else if(that.drawType === 'distance'){
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else if(that.drawType === 'area'){
- if(that.tempPoints.length >= 3){
- that.map.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
- }
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else if(that.drawType === 'rectangle'){
- that.map.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
- that.map.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
- }else{
- that.map.addLayer({ id: id + '-point', type: 'circle', source: id, paint: { 'circle-radius': 5, 'circle-color': 'red' } })
- }
- }else{
- that.map.getSource(id).setData(feature)
- if(that.tempPoints.length >= 3 && (that.drawType === 'area' || that.drawType === 'polygon')){
- if(!that.map.getLayer(id + '-polygon')){
- that.map.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
- }
- }
- }
- that.tempData = feature;
- }
- finishDraw() {
- const that = this;
- that.getCompleteFeature(that.tempData);
- that.isDrawing = false
- that.drawType = ''
- that.tempPoints = []
- that.tempSource?.setData({ type: 'FeatureCollection', features: [] })
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] })
- that.map.getCanvas().style.cursor = '';
- if(that.moveMarkerTips){
- that.moveMarkerTips.remove();
- that.moveMarkerTips=null;
- }
- }
- handleFeatureClick(e) {
- const that = this;
- const drawLayers = that.map.getStyle().layers
- .filter(layer => layer.id.startsWith('draw-'))
- .map(layer => layer.id);
- const point = e.point;
- const buffer = 5;
- const box = [
- [point.x - buffer, point.y - buffer],
- [point.x + buffer, point.y + buffer]
- ];
- // 追加自定义绘制图层
- that.editLayerIds.forEach(layerId => {
- drawLayers.push(layerId);
- })
- const features = that.map.queryRenderedFeatures(box, {
- layers: drawLayers
- });
- // console.log('[ =====handleFeatureClick===== ] >', that.selectedFeature)
- if (features.length > 0) {
- if(that.isEditFlag){// 编辑绘制
- const feature = features[0];
- const sourceId = feature.layer.source;
- if (!that.map.getSource(sourceId)) return;
- that.selectedFeatureId = sourceId;
- const sourceData = that.map.getSource(sourceId)._data;
- if(sourceData.geometry){
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
- }else{
- sourceData.features.forEach(item => {
- if(item.properties.dms_id === feature.properties.dms_id){
- that.selectedFeature = JSON.parse(JSON.stringify(item));
- return;
- }
- })
- }
- // 深拷贝切断引用
- // that.selectedFeature = JSON.parse(JSON.stringify(feature));
- that.getCompleteFeature(that.selectedFeature);
- that.clearEditMarkers();
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
- that.showEditMarkers();
- }else{ // 编辑工具绘制
- const feature = features[0];
- const layerId = feature.layer.id;
- const featureId = layerId.split('-').slice(0, -1).join('-');
- if (!that.map.getSource(featureId)) return;
- that.selectedFeatureId = featureId;
- const sourceData = that.map.getSource(featureId)._data;
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
- // console.log('[ handleFeatureClick ] >', that.selectedFeature)
- that.clearEditMarkers();
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
- that.showEditMarkers();
- }
-
- } else {
- that.clearEditMarkers();
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- // console.log('[ handleFeatureClick======== ] >', that.selectedFeature)
- }
- }
-
- // 编辑绘制点击事件 弃用 已被 handleFeatureClick 替代
- handleEditDrawClick(e) {
- const that = this;
- const point = e.point;
- const buffer = 5;
- const box = [
- [point.x - buffer, point.y - buffer],
- [point.x + buffer, point.y + buffer]
- ];
- const features = that.map.queryRenderedFeatures(box, {
- layers: that.editLayerIds
- });
- // console.log('[ handleEditDrawClick ] >', features)
- if (features.length > 0) {
- const feature = features[0];
- const sourceId = feature.layer.source;
- if (!that.map.getSource(sourceId)) return;
- that.selectedFeatureId = sourceId;
- const sourceData = that.map.getSource(sourceId)._data;
- if(sourceData.geometry){
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
- }else{
- sourceData.features.forEach(item => {
- if(item.properties.dms_id === feature.properties.dms_id){
- that.selectedFeature = JSON.parse(JSON.stringify(item));
- return;
- }
- })
- }
- // 深拷贝切断引用
- // that.selectedFeature = JSON.parse(JSON.stringify(feature));
- that.getCompleteFeature(that.selectedFeature);
- that.clearEditMarkers();
- that.showEditMarkers();
- } else {
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- }
- }
- moveWholeFeature(dx, dy) {
- const that = this;
- if (!that.selectedFeature) return;
- const feature = that.selectedFeature;
- if (feature.geometry.type === 'Point') {
- feature.geometry.coordinates[0] += dx;
- feature.geometry.coordinates[1] += dy;
- } else if (feature.geometry.type === 'LineString') {
- feature.geometry.coordinates.forEach(coord => {
- coord[0] += dx;
- coord[1] += dy;
- });
- } else if (feature.geometry.type === 'Polygon') {
- if(feature.properties.isCircle){
- const centerPt = turf.centroid(feature);
- const center = centerPt.geometry.coordinates;
- const newCenter = [center[0] + dx, center[1] + dy];
- const radiusMeters = turf.distance(center, feature.geometry.coordinates[0][0], { units: 'meters' });
- const newCircle = turf.circle(newCenter, radiusMeters, {
- units: 'meters',
- steps: 64
- });
- newCircle.properties = feature.properties;
- that.selectedFeature = newCircle;
- } else {
- feature.geometry.coordinates.forEach(ring => {
- ring.forEach(coord => {
- coord[0] += dx;
- coord[1] += dy;
- });
- });
- }
- }
- that.map.getSource(that.selectedFeatureId).setData(that.selectedFeature);
- const sourceData = that.map.getSource(that.selectedFeatureId)._data;
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
- }
- editCircleVertex(newPoint) {
- const that = this;
- const feature = that.selectedFeature;
- const centerPt = turf.centroid(feature);
- const center = centerPt.geometry.coordinates;
- const radiusMeters = turf.distance(center, newPoint, { units: 'meters' });
- if (radiusMeters < 5) return;
- const newCircle = turf.circle(center, radiusMeters, {
- units: 'meters',
- steps: 64
- });
- newCircle.properties = feature.properties;
- const line = turf.lineString([center, newPoint]);
- that.tempLineSource.setData(line);
- that.selectedFeature = newCircle;
- return newCircle;
- }
- insertTwoMiddlePoints(marker, index) {
- const that = this;
- marker.getElement().addEventListener('click', (e) => {
- e.stopPropagation();
- if (!that.selectedFeature) return;
- const geom = that.selectedFeature.geometry;
- let coords = [];
- let isPolygon = false;
- if (geom.type === 'LineString') {
- coords = [...geom.coordinates];
- } else if (geom.type === 'Polygon') {
- coords = [...geom.coordinates[0]];
- isPolygon = true;
- }
- if (coords.length < 2) return;
- if (isPolygon) {
- // ======================
- // 面对称加两点(左边1个 + 右边1个)
- // ======================
- let pureCoords = coords.slice(0, -1);
- const total = pureCoords.length;
- let realIndex = index;
- if (index === coords.length - 1) {
- realIndex = 0;
- }
- const prevIdx = (realIndex - 1 + total) % total;
- const nextIdx = (realIndex + 1) % total;
- const prevPt = pureCoords[prevIdx];
- const currPt = pureCoords[realIndex];
- const nextPt = pureCoords[nextIdx];
- const midPrev = [(prevPt[0] + currPt[0]) / 2, (prevPt[1] + currPt[1]) / 2];
- const midNext = [(currPt[0] + nextPt[0]) / 2, (currPt[1] + nextPt[1]) / 2];
- pureCoords.splice(realIndex, 0, midPrev);
- pureCoords.splice(realIndex + 2, 0, midNext);
- coords = [...pureCoords, pureCoords[0]];
- geom.coordinates[0] = coords;
- } else {
- // ======================
- // 线和面对称加两点(左边1个 + 右边1个)
- // ======================
- if (index === 0) {
- // 起点 → 只在右侧加点(和面对齐)
- const nextPt = coords[index + 1];
- const midNext = [(coords[index][0] + nextPt[0]) / 2, (coords[index][1] + nextPt[1]) / 2];
- coords.splice(index + 1, 0, midNext);
- }
- else if (index === coords.length - 1) {
- // 终点 → 只在左侧加点
- const prevPt = coords[index - 1];
- const midPrev = [(prevPt[0] + coords[index][0]) / 2, (prevPt[1] + coords[index][1]) / 2];
- coords.splice(index, 0, midPrev);
- }
- else {
- // 中间点 → 左右各加 1 点(和面完全一样!)
- const prevPt = coords[index - 1];
- const nextPt = coords[index + 1];
- const currPt = coords[index];
- const midPrev = [(prevPt[0] + currPt[0]) / 2, (prevPt[1] + currPt[1]) / 2];
- const midNext = [(currPt[0] + nextPt[0]) / 2, (currPt[1] + nextPt[1]) / 2];
- coords.splice(index, 0, midPrev);
- coords.splice(index + 2, 0, midNext);
- }
- geom.coordinates = coords;
- }
- that.refreshFeature();
- });
- }
- // ========== 键盘 DEL 删除选中节点 / 图形 ==========
- bindDeleteHotkey() {
- const that = this;
- // 先清掉旧事件,防止多实例、上下文错乱
- window.removeEventListener('keydown', that._deleteKeyHandler);
- // 保存成实例方法,保证永远指向最新实例
- that._deleteKeyHandler = function (e) {
- // 🔥 这里用 that,永远是当前最新实例
- if (e.key === 'Delete' && that.selectedFeature && (that.isEditMode || that.isEditFlag)) {
- e.preventDefault();
- // console.log('DEL 触发 - 当前图形:', that.selectedFeature);
- // 如果正在编辑、有选中节点 → 删除当前节点
- if (that.draggingPointIndex !== -1) {
- that.deleteVertex(that.draggingPointIndex);
- }
- // else {
- // that.deleteSelected();
- // }
- }
- };
- window.addEventListener('keydown', that._deleteKeyHandler);
- }
- // ========== 删除顶点(点/线/面 通用) ==========
- deleteVertex(index) {
- const that = this;
- if (!that.selectedFeature) return;
- const geom = that.selectedFeature.geometry;
- let coords;
- let isPolygon = false;
- // 先重置拖拽状态!防止索引错乱
- that.draggingPointIndex = -1;
- that.isDragging = false;
- if (geom.type === 'LineString') {
- coords = geom.coordinates;
- if (coords.length <= 2) return; // 线至少保留2个点
- }
- else if (geom.type === 'Polygon') {
- isPolygon = true;
- coords = geom.coordinates[0];
- if (coords.length <= 4) return; // 面至少保留3个点
- }
- // 删除顶点
- coords.splice(index, 1);
- // 闭合面处理
- if (isPolygon) {
- if (index === 0) {
- coords[coords.length - 1] = coords[0];
- }
- }
- // 刷新
- that.refreshFeature();
- }
- refreshFeature() {
- const that = this;
- if (!that.selectedFeature || !that.selectedFeatureId) return;
- // ========== 必须重置拖拽状态!否则必乱 ==========
- that.draggingPointIndex = -1;
- that.isDragging = false;
- that.map.getSource(that.selectedFeatureId).setData(that.selectedFeature);
- that.selectedFeature = JSON.parse(JSON.stringify(that.map.getSource(that.selectedFeatureId)._data));
- that.clearEditMarkers();
- that.showEditMarkers();
- }
-
- handleDrag(e) {
- const that = this;
- // console.log("handleDrag",that.draggingPointIndex);
- if (!that.selectedFeature || that.draggingPointIndex === -1) return;
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
- let feature = that.selectedFeature;
- let newCoord = [e.lngLat.lng, e.lngLat.lat];
- if(that.selectedFeature.properties.isCircle){
- that.clearEditMarkers();
- feature = that.editCircleVertex(newCoord);
- }else{
- if (feature.geometry.type === 'Point') {
- feature.geometry.coordinates = newCoord;
- } else if (feature.geometry.type === 'LineString') {
- feature.geometry.coordinates[that.draggingPointIndex] = newCoord;
- } else if (feature.geometry.type === 'Polygon') {
- feature.geometry.coordinates[0][that.draggingPointIndex] = newCoord;
- if (that.draggingPointIndex === 0) {
- feature.geometry.coordinates[0][feature.geometry.coordinates[0].length - 1] = newCoord;
- }
- }
- that.clearEditMarkers();
- that.showEditMarkers();
- }
- that.selectedFeature = JSON.parse(JSON.stringify(feature));
- that.map.getSource(that.selectedFeatureId).setData(feature);
- that.getCompleteFeature(that.selectedFeature);
-
- // console.log("handleDrag selectedFeature",that.selectedFeature);
- }
- setEditLayerIds(arr){
- const that = this;
- that.editLayerIds = arr;
- }
-
- deleteSelected() {
- let that = this;
- if (!that.selectedFeatureId) return;
- const layers = window.mapboxMap.getStyle().layers;
- layers.forEach(layer => {
- if (layer.id.startsWith(that.selectedFeatureId)) {
- that.map.removeLayer(layer.id);
- }
- });
- that.map.removeSource(that.selectedFeatureId);
- that.clearEditMarkers();
- that.selectedFeature = null;
- that.selectedFeatureId = '';
- that.layersIds = [];
- }
- }
- function init(map, options) {
- const instance = new MapDraw(map, options);
- return {
- startDraw: (type, flag, unit) => instance.startDraw(type, flag, unit),
- isEdit: (flag) => instance.isEdit(flag),
- clearAll: () => instance.clearAll(),
- setEditLayerIds: (arr) => instance.setEditLayerIds(arr),
- };
- }
- window.MapDraw = { init };
- })(window);
|