mapbox的标绘并能修改样式

目录

  • 效果
  • 代码
  • 源码说明
  • 其他

效果

可以动态修改颜色样式

代码

标绘功能直接使用了mapbox-gl-draw,并进行了二次封装
首先可以自己预先自定义样式,从mapbox-gl-draw中拷出来theme.js,并根据自己需要进行修改

theme.js

/**
 * 自定义绘制的默认样式
 * 测量绘制样式为默认
 * 标绘绘制样式根据属性渲染
 */
export default [
  {
    'id': 'gl-draw-polygon-fill-inactive',
    'type': 'fill',
    'filter': ['all',
      ['==', 'active', 'false'],
      ['==', '$type', 'Polygon'],
      ['!=', 'mode', 'static']
    ],
    /*'paint': {
      'fill-color':'#3bb2d0',
      'fill-outline-color':'#3bb2d0',
      'fill-opacity': 0.1
    }*/
    'paint': {
      'fill-color': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill"],
        '#3bb2d0'
      ],
      'fill-outline-color': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-outline-color"],
        '#3bb2d0'
      ],
      'fill-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-opacity"],
        0.1
      ]
    }
  },
  {
    'id': 'gl-draw-polygon-fill-active',
    'type': 'fill',
    'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
    'paint': {
      'fill-color':  [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill"],
        '#fbb03b'
      ],
      'fill-outline-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-outline-color"],
        '#fbb03b'
      ],
      'fill-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-opacity"],
        0.1
      ]
    }
  },
  {
    'id': 'gl-draw-polygon-midpoint',
    'type': 'circle',
    'filter': ['all',
      ['==', '$type', 'Point'],
      ['==', 'meta', 'midpoint']],
    'paint': {
      'circle-radius': 3,
      'circle-color': '#fbb03b'
    }
  },
  {
    'id': 'gl-draw-polygon-stroke-inactive',
    'type': 'line',
    'filter': ['all',
      ['==', 'active', 'false'],
      ['==', '$type', 'Polygon'],
      ['!=', 'mode', 'static']
    ],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    'paint': {
      'line-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-outline-color"],
        '#fbb03b'
      ],
      'line-width': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-width"],
        2
      ]
    }
  },
  {
    'id': 'gl-draw-polygon-stroke-active',
    'type': 'line',
    'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    'paint': {
      'line-color': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_fill-outline-color"],
        '#fbb03b'
      ],
      'line-dasharray': [0.2, 2],
      'line-width': 2
    }
  },

  //线
  {
    'id': 'gl-draw-line-inactive',
    'type': 'line',
    'filter': ['all',
      ['==', 'active', 'false'],
      ['==', '$type', 'LineString'],
      ['!=', 'mode', 'static']
    ],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    /*'paint': {
      'line-color': '#3bb2d0',
      'line-width': 2
    },*/
    'paint': {
      'line-color': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-color"],
        '#3bb2d0'
      ],
      'line-width': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-width"],
        2
      ],
      'line-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-opacity"],
        1
      ]

    }
  },
  {
    'id': 'gl-draw-line-active',
    'type': 'line',
    'filter': ['all',
      ['==', '$type', 'LineString'],
      ['==', 'active', 'true']
    ],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    'paint': {
      'line-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-color"],
        '#fbb03b'
      ],
      'line-dasharray': [0.2, 2],
      'line-width': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-width"],
        2
      ],
      'line-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_line-opacity"],
        1
      ]
    }
    /*'paint': {
      'line-color': '#fbb03b',
      'line-dasharray': [0.2, 2],
      'line-width': 2
    }*/
  },
  {
    'id': 'gl-draw-polygon-and-line-vertex-stroke-inactive',
    'type': 'circle',
    'filter': ['all',
      ['==', 'meta', 'vertex'],
      ['==', '$type', 'Point'],
      ['!=', 'mode', 'static']
    ],
    'paint': {
      'circle-radius': 5,
      'circle-color': '#fff'
    }
  },
  {
    'id': 'gl-draw-polygon-and-line-vertex-inactive',
    'type': 'circle',
    'filter': ['all',
      ['==', 'meta', 'vertex'],
      ['==', '$type', 'Point'],
      ['!=', 'mode', 'static']
    ],
    'paint': {
      'circle-radius': 3,
      'circle-color': '#fbb03b'
    }
  },
  {
    'id': 'gl-draw-point-point-stroke-inactive',
    'type': 'circle',
    'filter': ['all',
      ['==', 'active', 'false'],
      ['==', '$type', 'Point'],
      ['==', 'meta', 'feature'],
      ['!=', 'mode', 'static']
    ],
    'paint': {
      'circle-radius': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['+',['get', "user_circle-radius"],2],
        5
      ],
      'circle-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-opacity"],
        1
      ],
      'circle-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-color"],
        '#fff'
      ]
    }
  },

  // 点
  {
    'id': 'gl-draw-point-inactive',
    'type': 'circle',
    'filter': ['all',
      ['==', 'active', 'false'],
      ['==', '$type', 'Point'],
      ['==', 'meta', 'feature'],
      ['!=', 'mode', 'static']
    ],
    'paint':{
      'circle-radius': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-radius"],
        3
      ],
      'circle-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-color"],
        '#3bb2d0'
      ],
      'circle-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-opacity"],
        1
      ]
    }

    /*'paint': {
      'circle-radius': 3,
      'circle-color': '#3bb2d0'
    }*/
  },
  {
    'id': 'gl-draw-point-stroke-active',
    'type': 'circle',
    'filter': ['all',
      ['==', '$type', 'Point'],
      ['==', 'active', 'true'],
      ['!=', 'meta', 'midpoint']
    ],
    'paint': {
      //'circle-radius': 7,
      'circle-radius': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ["+", ['get', "user_circle-radius"], 5],
        7
      ],
      'circle-color':'#fff'
    }
  },
  {
    'id': 'gl-draw-point-active',
    'type': 'circle',
    'filter': ['all',
      ['==', '$type', 'Point'],
      ['!=', 'meta', 'midpoint'],
      ['==', 'active', 'true']],
    'paint':{
      'circle-radius': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ["+", ['get', "user_circle-radius"], 3],
        5
      ],
      'circle-color':[
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-color"],
        '#fbb03b'
      ],
      'circle-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-opacity"],
        1
      ]
    }
    /*'paint': {
      'circle-radius': 5,
      'circle-color': '#fbb03b'
    }*/
  },
  {
    'id': 'gl-draw-polygon-fill-static',
    'type': 'fill',
    'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
    'paint': {
      'fill-color': '#404040',
      'fill-outline-color': '#404040',
      'fill-opacity': 0.1
    }
  },
  {
    'id': 'gl-draw-polygon-stroke-static',
    'type': 'line',
    'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Polygon']],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    'paint': {
      'line-color': '#404040',
      'line-width': 2
    }
  },
  {
    'id': 'gl-draw-line-static',
    'type': 'line',
    'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'LineString']],
    'layout': {
      'line-cap': 'round',
      'line-join': 'round'
    },
    'paint': {
      'line-color': '#404040',
      'line-width': 2
    }
  },
  {
    'id': 'gl-draw-point-static',
    'type': 'circle',
    'filter': ['all', ['==', 'mode', 'static'], ['==', '$type', 'Point']],
    'paint': {
      'circle-radius': 5,
      'circle-color': '#404040',
      'circle-opacity': [
        "case",
        ['==', ['get', "user_isPlotFeature"], true], ['get', "user_circle-opacity"],
        1
      ]
    }
  }
];

封装Draw类
Draw.js

/**
 * @Author :TanShiJun [email protected]
 * @Date :2022/5/16
 * @Describe :绘制
 * Last Modified by : TSJ
 * Last Modified time :2022/5/23
 **/
import MapboxDraw from "@mapbox/mapbox-gl-draw/index.js";
import * as MapboxDrawWaypoint from 'mapbox-gl-draw-waypoint';
import DrawRectangle from 'mapbox-gl-draw-rectangle-mode';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
import {
  CircleMode,
  DragCircleMode,
  DirectMode,
  SimpleSelectMode
} from 'mapbox-gl-draw-circle';
import styles from './theme';
export default class Draw {
  constructor(_map,_appConfig) {
    this.map=_map
    this.appConfig=_appConfig
    let modes={
      ...MapboxDraw.modes,
      draw_rectangle:DrawRectangle,
      draw_circle  : CircleMode,
      drag_circle  : DragCircleMode,
      direct_select: DirectMode,
      simple_select: SimpleSelectMode
    }
    // 测量绘制的要素,只允许拖动节点,不允许拖动整个要素
    modes = MapboxDrawWaypoint.enable(modes,feature => feature.properties.isMeasureFeature);

    this.mapboxDraw=new MapboxDraw({
      displayControlsDefault: false,
      modes,
      userProperties: true,
      styles,
      /*controls: {
          polygon: true,
          trash: true
      }*/
    })
    // 触发 setup onAdd事件
    this.map.addControl(this.mapboxDraw);

    this._init()
  }
  _init() {
    this.map.on('draw.create', drawCreate);
    this.map.on('draw.delete', drawDelete);
    this.map.on('draw.update', drawUpdate);
    this.callBack=undefined
    let self=this
    function drawCreate(evt){
      //console.log('drawCreate',evt)
      if(self.callBack){
        self.callBack(evt)
      }
    }
    function drawDelete(){}
    function drawUpdate(){
    }
  }


  startDraw(type,callback){
    this.callBack=undefined
    if(callback){
      this.callBack=callback
    }
    switch (type) {
      case 'draw_point':
        this.mapboxDraw.changeMode('draw_point')
        break
      case 'draw_line_string':
        this.mapboxDraw.changeMode('draw_line_string')
        break
      case 'draw_polygon':
        this.mapboxDraw.changeMode('draw_polygon')
        break
      case 'draw_rectangle':
        this.mapboxDraw.changeMode('draw_rectangle')
        break
      case 'draw_circle':
        this.mapboxDraw.changeMode('draw_circle',{ initialRadiusInKm: 0.5 })
        break
      default :
        console.error("未知的绘制类型")
    /*  case 'draw_polygon':
        this.mapboxDraw.changeMode('draw_polygon')
        break
      case 'draw_polygon':
        this.mapboxDraw.changeMode('draw_polygon')
        break
      case 'draw_polygon':
        this.mapboxDraw.changeMode('draw_polygon')
        break*/
    }
  }
  static getInstance(_map,_appConfig) {
    if (!this.instance) {
      this.instance = new Draw(_map,_appConfig)
    }
    return this.instance
  }
}

标绘管理类 可绘制点、线、面、矩形、圆、文字,包括样式的自定义
PlotManager.js

/**
 * @Author :TanShiJun [email protected]
 * @Date :2022/5/18
 * @Describe :地图标绘
 * Last Modified by : TSJ
 * Last Modified time :2022/5/18
 **/
import Draw from "../Draw";
export default class PlotManager {
  constructor(map,appConfig) {
    this.map=map
    this.appConfig=appConfig
    this.draw=Draw.getInstance(map,appConfig)
    this.currentSelectFeature=null
    this.map.on('draw.selectionchange',(data)=>{
      if(data.features.length>0&&data.features[0].properties.isPlotFeature){
        this.currentSelectFeature=data.features[0]
      }
      if(data.features.length===0){
        this.currentSelectFeature=null
      }
    })
    //绘制的默认样式
    this.style={
      lineStrokeColor: '#ff0000', // 线条的轮廓颜色
      lineBorderStyle: '0', // 线条的样式,实线、虚线等
      lineBorderWidth: 1, // 线条的宽度
      lineOpacity: 48, // 线条的透明度
      opacity:50,
      shapeFillColor: '#00ff00', // 形状的填充颜色
      shapeStrokeColor: '#ff0000', // 形状的轮廓颜色
      shapeBorderWidth: 1, // 形状的边框线宽度
      shareOpacity: 0.5, // 形状的透明度
      fontContent: '', // 文字的内容
      fontColor: '#ff0000', // 文字的颜色
      fontSize: 18, // 文字的大小
    }
    this._initTextLayer()
  }
  _initTextLayer() {
    //添加文字标记图层
    this.plotTextlayerId='plot_text'
    this.map.addLayer({
      'id':this.plotTextlayerId,
      'type': 'symbol',
      'source':'mapbox-gl-draw-cold' ,
      "layout": {
        "text-field": "{user_text}",
        'text-size': [
          "case",
          ['==', ['get', "user_isPlotFeature"], true], ['get', "user_text-size"],
          14
        ],
        //"text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"],
        "text-offset": [0, -0.6],
        "text-anchor": "bottom"
      },
      'paint': {//绘制类属性
        /*'text-color':"#fff",*/
        'text-color':[
          "case",
          ['==', ['get', "user_isPlotFeature"], true], ['get', "user_text-color"],
          '#ff0000'
        ],
        'text-halo-color':"#fff",
        'text-halo-width':1,
        'text-opacity':[
          "case",
          ['==', ['get', "user_isPlotFeature"], true], ['get', "user_text-opacity"],
          0
        ],
        'text-translate': [0,0],//右、下 偏移量
      }
    })
    this._bindLayerEvent(this.plotTextlayerId)
  }
  _bindLayerEvent(layerId){
    let self=this
    this.map.on('click', layerId, function (e) {
      if(e.features[0]){
        self.draw.mapboxDraw.changeMode('simple_select',{featureIds:[e.features[0].properties.id]})
      }
    });
    this.map.on('mouseenter', layerId, function (e) {
      self.map.getCanvas().style.cursor = 'pointer';

    });
    this.map.on('mouseleave',layerId, function (e) {
      self.map.getCanvas().style.cursor = '';
    });
  }

  //获取当前的选中绘制图形
  getSelectedDraw() {
    const ids=this.draw.mapboxDraw.getSelectedIds()
    if(ids.length>0){
      let feature=this.draw.mapboxDraw.get(ids[0])
      if(feature&&feature.properties.isPlotFeature){
        this.currentSelectFeature=feature
        return feature
      }
    }else{
      this.currentSelectFeature=null
    }
    return null
  }
  cancelDraw() {
    this.currentSelectFeature=null
    this.draw.mapboxDraw.changeMode('simple_select',{featureIds:[]})
  }
  deleteSelected() {
    const ids=this.draw.mapboxDraw.getSelectedIds()
    if(ids.length>0){
      this.draw.mapboxDraw.delete(ids)
    }
  }
  deleteAll() {
    this.draw.mapboxDraw.deleteAll()
  }
  startDraw(geometryType,style,callback){
    if(style){
      this.style=style
    }
    let drawType=''
    switch (geometryType) {
      case 'POLYGON':// 绘制面
        drawType='draw_polygon'
        this.drawPolygon(drawType,geometryType,this.style,(data)=>{
          if(callback){
            callback(data)
          }
        })
        break
      case 'EXTENT':// 绘制矩形
        drawType='draw_rectangle'
        this.drawPolygon(drawType,geometryType,this.style,(data)=>{
          if(callback){
            callback(data)
          }
        })
        break
      case 'POLYLINE':
        drawType='draw_line_string'
        this.drawPolyline(drawType,geometryType,this.style,(data)=>{
          if(callback){
            callback(data)
          }

        })
        break
      case 'CIRCLE':
        drawType='draw_circle'
        this.drawPolygon(drawType,geometryType,this.style,(data)=>{
          if(callback){
            callback(data)
          }

        })
        break
      case 'POINT':
      case 'FONT':
        drawType='draw_point'
        this.drawPoint(drawType,geometryType,this.style,(data)=>{
          if(callback){
            callback(data)
          }
        })
        break
    }
  }

  drawPolygon(drawType,geometryType,style,callback){
    this.draw.startDraw(drawType,(data)=>{
      if(data.features.length>0){
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'isPlotFeature',true)
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'plotType',geometryType)
        //以下添加样式
        this.updatePolygonStyle(data.features[0].id,style)
      }
      if(callback){
        callback(data)
      }
    })
  }

  /**
   * 更新样式属性
   * @param feature 绘制的面要素id
   * @param style 样式
   */
  updatePolygonStyle(featureId,style) {
    let feature=this.draw.mapboxDraw.get(featureId)
    let layers=this.map.getStyle().layers;
    this.draw.mapboxDraw.setFeatureProperty(featureId,'fill',style.shapeFillColor)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'fill-opacity',style.shareOpacity)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'fill-outline-color',style.shapeStrokeColor)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'line-width',style.shapeBorderWidth)
  }

  drawPolyline(drawType,geometryType,style,callback){
    this.draw.startDraw(drawType,(data)=>{
      if(data.features.length>0){
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'isPlotFeature',true)
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'plotType',geometryType)
        //以下添加样式
        this.updatePolylineStyle(data.features[0].id,style)

      }
      if(callback){
        callback(data)
      }
    })
  }

  /**
   * 更新样式属性
   * @param feature 绘制的面要素id
   * @param style 样式
   */
  updatePolylineStyle(featureId,style) {
    this.draw.mapboxDraw.setFeatureProperty(featureId,'line-opacity',style.lineOpacity)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'line-color',style.lineStrokeColor)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'line-width',style.lineBorderWidth)
  }

  drawPoint(drawType,geometryType,style,callback){
    this.draw.startDraw(drawType,(data)=>{
      if(data.features.length>0){
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'isPlotFeature',true)
        this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'plotType',geometryType)
        //以下添加样式
        if(geometryType==='POINT'){
          this.updatePointStyle(data.features[0].id,style)
        }
        else if(geometryType==='FONT'){
          // 文本定义默认点样式
          this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'text','')

          // 文本标记修改点样式
          this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'circle-opacity',0.1)
          this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'circle-radius',2)
          this.draw.mapboxDraw.setFeatureProperty(data.features[0].id,'circle-color','#e8ff0c')

          this.updateTextStyle(data.features[0].id,style)
        }

      }
      if(callback){
        callback(data)
      }
    })
  }
  updatePointStyle(featureId,style) {
    let source=this.map.getSource('mapbox-gl-draw-cold')
    console.log(source)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'circle-opacity',style.pointOpacity)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'circle-radius',style.pointRadius)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'circle-color',style.pointColor)
  }
  updateTextStyle(featureId,style){
    this.draw.mapboxDraw.setFeatureProperty(featureId,'text-color',style.fontColor)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'text-size',style.fontSize)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'text-opacity',style.fontOpacity)
    this.draw.mapboxDraw.setFeatureProperty(featureId,'text',style.fontContent)


  }

  static getInstance(map,appConfig) {
    if(!this.instance){
      this.instance=new PlotManager(map,appConfig)
    }
    return this.instance
  }
}

vue组件调用
index.vue

<template>
  <move-able-panel headName="标绘"
                   titleIcon="el-icon-edit"
                   :width="400"
                   :height="300"
                   :top="110"
                   :right="20"
                   :visible="visible"
                   @close="closePanel"
  >
    <div class="gis-plotting-box">
      <!-- 图标类型 -->
      <div class="gis-graphical-wrap">
        <div v-for="(item, index) in graphicals"
             :key="index"
             class="gis-graphical-item"
             @click="onChangeType(item)"
              :title="item.name">
          <div class="gis-graphical-block" :class="[item.selected ? 'selected' : '']">
            <i :class="['iconfont', item.icon]"></i>
          </div>
        </div>
      </div>
      <!-- 图标属性 -->
      <div class="gis-attribute-wrap">
        <!-- 线条 -->
        <el-form v-show="activeType === 'line'"
                 inline
                 size="mini"
                 label-width="68px">
          <el-row>
            <el-col :span="12">
              <el-form-item label="轮廓颜色">
                <el-color-picker v-model="lineStrokeColor" :predefine="predefineColors"/>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="线条样式">
                <el-select v-model="lineBorderStyle"  placeholder="请选择">
                  <el-option v-for="(item, index) in borderStyles"
                             :key="index"
                             :label="item.name"
                             :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12">
              <el-form-item label="线条宽度">
                <el-input-number v-model="lineBorderWidth" controls-position="right" :min="1"/>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <!--<div class="block">
                <span class="demonstration">透明度</span>
                <el-slider v-model="opacity"></el-slider>
              </div>-->
              <el-form-item label="透明度">
                <el-input-number v-model="lineOpacity"
                                 controls-position="right"
                                 :min="0"
                                 :max="1"
                                 :step="0.1"/>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-button icon="el-icon-edit" type="primary" @click="startDraw">绘制</el-button>
            <el-button icon="el-icon-close" type="info" @click="cancelDraw">取消</el-button>
            <el-button icon="el-icon-check" type="success" @click="applySetting">确定</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="clearDraw">清除</el-button>
          </el-row>
          <!--<el-row>
            <el-button type="text" size="mini" style="color: red">注:双击绘制的图形可选中,进行删除和添加属性</el-button>
          </el-row>-->
        </el-form>
        <!-- 形状 -->
        <el-form v-show="activeType === 'shape'"
                 inline
                 size="mini"
                 label-width="68px">
          <el-row>
            <el-col :span="12">
              <el-form-item label="填充颜色">
                <el-color-picker v-model="shapeFillColor" :predefine="predefineColors"/>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="透明度">
                <el-input-number v-model="shareOpacity"
                                 controls-position="right"
                                 :min="0"
                                 :max="1"
                                 :step="0.1"/>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="12">
              <el-form-item label="轮廓颜色">
                <el-color-picker v-model="shapeStrokeColor" :predefine="predefineColors"/>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="线条宽度">
                <el-input-number v-model="shapeBorderWidth"
                                 controls-position="right"
                                 :min="1"/>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-button icon="el-icon-edit" type="primary" @click="startDraw">绘制</el-button>
            <el-button icon="el-icon-close" type="info" @click="cancelDraw">取消</el-button>
            <el-button icon="el-icon-check" type="success" @click="applySetting">确定</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="clearDraw">清除</el-button>
          </el-row>
          <!--<el-row>
            <el-button type="text" size="mini" style="color: red">注:双击绘制的图形可选中,进行删除和添加属性</el-button>
          </el-row>-->
        </el-form>
        <!-- 文字 -->
        <el-form v-show="activeType === 'font'"
                 inline
                 size="mini"
                 label-width="68px">
          <el-form-item label="文本内容" class="form-block">
            <el-input v-model="fontContent"/>
          </el-form-item>
          <el-form-item label="文字颜色">
            <el-color-picker v-model="fontColor" :predefine="predefineColors"/>
          </el-form-item>
          <el-form-item label="文字大小">
            <el-input-number v-model="fontSize"
                             controls-position="right"
                             :min="12"
                             :max="100"
                             :step="1"/>
          </el-form-item>
          <el-form-item label="透明度">
            <el-input-number v-model="fontOpacity"
                             controls-position="right"
                             :min="0"
                             :max="1"
                             :step="0.1"/>
          </el-form-item>
          <el-row>
            <el-button icon="el-icon-edit" type="primary" @click="startDraw">绘制</el-button>
            <el-button icon="el-icon-close" type="info" @click="cancelDraw">取消</el-button>
            <el-button icon="el-icon-check" type="success" @click="applySetting">确定</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="clearDraw">清除</el-button>
          </el-row>
          <!--<el-row>
            <el-button type="text" size="mini" style="color: red">注:双击绘制的图形可选中,进行删除和添加属性</el-button>
          </el-row>-->
        </el-form>
        <!---->
        <el-form v-show="activeType === 'point'"
                 inline
                 size="mini"
                 label-width="68px">
          <el-form-item label="点颜色">
            <el-color-picker v-model="pointColor" :predefine="predefineColors"/>
          </el-form-item>
          <el-form-item label="点大小">
            <el-input-number v-model="pointRadius"
                             controls-position="right"
                             :min="1"
                             :max="100"
                             :step="1"/>
          </el-form-item>
          <el-row>
            <el-button icon="el-icon-edit" type="primary" @click="startDraw">绘制</el-button>
            <el-button icon="el-icon-close" type="info" @click="cancelDraw">取消</el-button>
            <el-button icon="el-icon-check" type="success" @click="applySetting">确定</el-button>
            <el-button icon="el-icon-delete" type="danger" @click="clearDraw">清除</el-button>
          </el-row>
          <!--<el-row>
            <el-button type="text" size="mini" style="color: red">注:双击绘制的图形可选中,进行删除和添加属性</el-button>
          </el-row>-->
        </el-form>
      </div>
    </div>
  </move-able-panel>

</template>

<script>
  import MoveAblePanel from '../../../components/MoveAblePanel'
  import PlotWidget from "./PlotWidget";
  export default {
    name: 'Plot',
    components: {
      MoveAblePanel,
    },
    props: {
      visible:{
        type:Boolean,
        default:true
      }
    },
    computed: {
      mapLoaded() {
        return this.$store.getters['gis/getIsLoadedMap']
      },
      // 预定义颜色
      predefineColors() {
        return this.$store.getters['gis/getPredefineColors'];
      },
      styleConfig() {
        return {
          lineBorderStyle:this.lineBorderStyle,
          lineStrokeColor:this.lineStrokeColor,
          lineBorderWidth:this.lineBorderWidth,
          lineOpacity:this.lineOpacity,
          shapeFillColor:this.shapeFillColor,
          shapeStrokeColor:this.shapeStrokeColor,
          shapeBorderWidth:this.shapeBorderWidth,
          shareOpacity:this.shareOpacity,
          fontContent: this.fontContent,
          fontColor:this.fontColor,
          fontSize:this.fontSize,
          fontOpacity:this.fontOpacity,
          pointRadius:this.pointRadius,
          pointColor:this.pointColor,
          pointOpacity:this.pointOpacity,
          drawType:this.currentGraphical?this.currentGraphical.drawType:'POLYLINE'
        }
      }
    },
    data: function () {
      return {
        isShow: this.visible, // 是否显示
        activeId: '', // 当前选择的图形id
        activeType: 'line', // 当前选择的图形类型
        graphicals: [
          { id: 0, name: '折线', type: 'line', drawType:'POLYLINE',selected:true, icon: 'icon-icon-line-graph' },
          { id: 2, name: '圆形', type: 'shape',drawType:'CIRCLE',selected:false,  icon: 'icon-yuanxing' },
        /*  { id: 3, name: '椭圆形', type: 'shape',drawType:'ELLIPSE',selected:false,  icon: 'icon-tx-fill-tuoyuanxing' },*/
          { id: 4, name: '矩形', type: 'shape',drawType:'EXTENT',selected:false,  icon: 'icon-juxing' },
          { id: 5, name: '多边形', type: 'shape',drawType:'POLYGON',selected:false,  icon: 'icon-duobianxingchaxun' },
          { id: 3, name: '点', type: 'point',drawType:'POINT',selected:false,  icon: 'icon-wired-weizhidian' },
          { id: 6, name: '文字', type: 'font',drawType:'FONT',selected:false,  icon: 'icon-wenziyanse' }
        ], // 绘图类型
        borderStyles: [
          { id: '0', name: '实线' },
          { id: '1', name: '虚线' }
        ], // 线条样式
        lineStrokeColor: '#ff0000', // 线条的轮廓颜色
        lineBorderStyle: '0', // 线条的样式,实线、虚线等
        lineBorderWidth: 2, // 线条的宽度
        lineOpacity: 1, // 线条的透明度
        opacity:50,
        shapeFillColor: '#00ff00', // 形状的填充颜色
        shapeStrokeColor: '#ff0000', // 形状的轮廓颜色
        shapeBorderWidth: 1, // 形状的边框线宽度
        shareOpacity: 0.5, // 形状的透明度
        fontContent: '', // 文字的内容
        fontColor: '#ff0000', // 文字的颜色
        fontSize: 18, // 文字的大小
        fontOpacity:0.8,
        pointRadius:3,// 点大小,半径
        pointColor:'#00ff00',// 点颜色
        pointOpacity:1,

        currentSelectedGraphic:null
      }
    },
    watch:  {
      styleConfig(newVal,oldValue) {
        this.widget&&this.widget.updateDrawStyle(newVal)
        // map.getSource('trace').setData(data);
      }
    },
    mounted() {

    },
    methods: {
      selectedPlottingGraphic(graphic) {
        this.currentSelectedGraphic=graphic;
      },
      /**
       * 显示Dialog
       */
      onShow() {
        this.$set(this, 'isShow', true);
      },
      //开始绘制
      startDraw(){
        if(!mapbox3D){
          return this.$message.warning(`请先等待地图初始化完成...`)
        }
        if(!this.currentGraphical){
          this.currentGraphical=this.graphicals[0]
        }
        this.widget=PlotWidget.getInstance(mapbox3D.map,appConfig)
        let geometryType=this.currentGraphical.drawType;
        this.widget.plottingDraw(geometryType,this.styleConfig,(data)=>{
        })
      },
      //取消绘制
      cancelDraw() {
        this.widget.plotManager.cancelDraw()
      },
      //确定
      applySetting() {
        let drawFeature=this.widget.plotManager.getSelectedDraw()
        if(!drawFeature){
          return this.$message.warning(`请先选中绘制的要素`)
        }else{
          if(this.currentGraphical.drawType&&this.currentGraphical.drawType===drawFeature.properties.plotType){
            this.widget.updateDrawStyle(this.styleConfig)
          }else{
            return this.$message.warning(`请切换到对应的样式修改面板`)
          }
        }

      },
      //清除
      clearDraw() {
        let drawFeature=this.widget.plotManager.getSelectedDraw()
        if(!drawFeature){
          return this.$message.warning(`请先选中绘制的要素`)
        }else{
          this.widget.plotManager.deleteSelected()
        }

        /*this.currentSelectedGraphic=null;
        if(this.currentGraphical&&this.currentGraphical.drawType){
          this.$ArcGIS.graphicsLayerManager.clearGraphicByType(this.currentGraphical.drawType);
        }*/

      },
      /**
       * 切换图标类型
       * @param {Object} graphical 选择的图标对象
       */
      onChangeType(graphical) {
        this.$set(this, 'activeId', graphical.id);
        this.$set(this, 'activeType', graphical.type);
        for(let i=0;i<this.graphicals.length;i++){
          this.graphicals[i].selected=false;
        }
        graphical.selected=true;
        this.currentGraphical=graphical;
      },
      closePanel() {
        this.$emit('close')
      }
    },
  }
</script>

<style lang="scss">
  .gis-plotting-box {
    .el-form-item__label {
      color: #ffffff;
    }
    padding: 10px;

    .gis-graphical-wrap {
      display: flex;
      flex-direction: row;
      align-items: center;
      margin-bottom: 25px;
      overflow: hidden;
    }

    .gis-graphical-item {
      flex: 1;
    }

    .gis-graphical-block {
      margin: auto;
      width: 24px;
      height: 24px;
      line-height: 24px;
      color: #fff;
      text-align: center;
      background-color: #0b87e7;
      border-radius: 3px;
      transition: all .3s;
      cursor: pointer;

      &:hover {
        background-color: #316892;
      }
      &.selected {
        background-color: #316892;
      }
    }

    .el-input__inner {
      width: 100px;
    }

    .form-block {
      width: 100%;
      .el-input__inner {
        width: 280px;
      }
    }

    .el-form-item__label {
      text-align: left;
    }

    .el-input-number {
      width: 100px;

      .el-input__inner {
        width: 100px;
        padding-right: 40px;
      }
    }
  }

</style>

PlotWidget.js

/**
 * @Author :TanShiJun [email protected]
 * @Date :2022/5/19
 * @Describe :
 * Last Modified by : TSJ
 * Last Modified time :2022/5/19
 **/
import PlotManager from "../../../gis/core/PlotManager";
export default class PlotWidget {
  constructor(map,appConfig) {
    this.map=map
    this.appConfig=appConfig
    this.plotManager=PlotManager.getInstance(map,appConfig)
    //挂在到全局
    global.mapbox3D.plotManager=this.plotManager
  }


  plottingDraw(type,style,callback){
    this.plotManager.startDraw(type,style,(data)=>{
      if(callback){
        callback(data)
      }
    })

  }

  // 更新绘制的样式
  updateDrawStyle(style){
    debugger
    if(this.plotManager.currentSelectFeature&&this.plotManager.currentSelectFeature.properties.isPlotFeature){
      const type=this.plotManager.currentSelectFeature.properties.plotType
      switch (type) {
        case 'POLYGON':
        case 'EXTENT':
        case 'CIRCLE':
          this.plotManager.updatePolygonStyle(this.plotManager.currentSelectFeature.id,style)
          break
        case 'POLYLINE':
          this.plotManager.updatePolylineStyle(this.plotManager.currentSelectFeature.id,style)
          break
        case 'POINT':
          this.plotManager.updatePointStyle(this.plotManager.currentSelectFeature.id,style)
          break
        case 'FONT':
          this.plotManager.updateTextStyle(this.plotManager.currentSelectFeature.id,style)
          break
      }
      this.changeModeForRender(this.plotManager.currentSelectFeature.id,type)
    }
  }
  // 添加模式切换能保证样式改变能重新渲染
  changeModeForRender(featureId,type){
    if(type!=='POINT'&&type!=='FONT'){
      this.plotManager.draw.mapboxDraw.changeMode('simple_select')
      this.plotManager.draw.mapboxDraw.changeMode('direct_select',{featureId:featureId})
    }else{
      this.plotManager.draw.mapboxDraw.changeMode('simple_select')
    }
  }
  static getInstance(_map,_appConfig) {
    if (!this.instance) {
      this.instance = new PlotWidget(_map,_appConfig)
    }
    return this.instance
  }
}

源码说明

首先第一步初始化

this.mapboxDraw=new MapboxDraw({
      displayControlsDefault: false,
      modes,
      userProperties: true,
      styles,
      /*controls: {
          polygon: true,
          trash: true
      }*/
    })

function MapboxDraw(options) {
  setupDraw(options, this);
}

const setupDraw = function(options, api) {
  options = setupOptions(options);

  const ctx = {
    options
  };

  api = setupAPI(ctx, api);
  ctx.api = api;

  const setup = runSetup(ctx);

  api.onAdd = setup.onAdd;
  api.onRemove = setup.onRemove;
  api.types = Constants.types;
  api.options = options;

  return api;
};
setup中进一步初始化
import events from './events';
import Store from './store';
import ui from './ui';
import * as Constants from './constants';
import xtend from 'xtend';

export default function(ctx) {

  let controlContainer = null;
  let mapLoadedInterval = null;

  const setup = {
    onRemove() {
      // Stop connect attempt in the event that control is removed before map is loaded
      ctx.map.off('load', setup.connect);
      clearInterval(mapLoadedInterval);

      setup.removeLayers();
      ctx.store.restoreMapConfig();
      ctx.ui.removeButtons();
      ctx.events.removeEventListeners();
      ctx.ui.clearMapClasses();
      ctx.map = null;
      ctx.container = null;
      ctx.store = null;

      if (controlContainer && controlContainer.parentNode) controlContainer.parentNode.removeChild(controlContainer);
      controlContainer = null;

      return this;
    },
    connect() {
      ctx.map.off('load', setup.connect);
      clearInterval(mapLoadedInterval);
      setup.addLayers();
      ctx.store.storeMapConfig();
      ctx.events.addEventListeners();
    },
    onAdd(map) {
      if (process.env.NODE_ENV !== 'test') {
        // Monkey patch to resolve breaking change to `fire` introduced by
        // mapbox-gl-js. See mapbox/mapbox-gl-draw/issues/766.
        const _fire = map.fire;
        map.fire = function(type, event) {
          // eslint-disable-next-line
          let args = arguments;

          if (_fire.length === 1 && arguments.length !== 1) {
            args = [xtend({}, { type }, event)];
          }

          return _fire.apply(map, args);
        };
      }

      ctx.map = map;
      ctx.events = events(ctx);
      ctx.ui = ui(ctx);
      ctx.container = map.getContainer();
      ctx.store = new Store(ctx);


      controlContainer = ctx.ui.addButtons();

      if (ctx.options.boxSelect) {
        map.boxZoom.disable();
        // Need to toggle dragPan on and off or else first
        // dragPan disable attempt in simple_select doesn't work
        map.dragPan.disable();
        map.dragPan.enable();
      }

      if (map.loaded()) {
        setup.connect();
      } else {
        map.on('load', setup.connect);
        mapLoadedInterval = setInterval(() => { if (map.loaded()) setup.connect(); }, 16);
      }

      ctx.events.start();
      return controlContainer;
    },
    addLayers() {
      // drawn features style
      ctx.map.addSource(Constants.sources.COLD, {
        data: {
          type: Constants.geojsonTypes.FEATURE_COLLECTION,
          features: []
        },
        type: 'geojson'
      });

      // hot features style
      ctx.map.addSource(Constants.sources.HOT, {
        data: {
          type: Constants.geojsonTypes.FEATURE_COLLECTION,
          features: []
        },
        type: 'geojson'
      });

      ctx.options.styles.forEach((style) => {
        ctx.map.addLayer(style);
      });

      ctx.store.setDirty(true);
      ctx.store.render();
    },
    // Check for layers and sources before attempting to remove
    // If user adds draw control and removes it before the map is loaded, layers and sources will be missing
    removeLayers() {
      ctx.options.styles.forEach((style) => {
        if (ctx.map.getLayer(style.id)) {
          ctx.map.removeLayer(style.id);
        }
      });

      if (ctx.map.getSource(Constants.sources.COLD)) {
        ctx.map.removeSource(Constants.sources.COLD);
      }

      if (ctx.map.getSource(Constants.sources.HOT)) {
        ctx.map.removeSource(Constants.sources.HOT);
      }
    }
  };

  ctx.setup = setup;

  return setup;
}

然后执行 setup中的回调函数,当时一直不知道在哪调用这个回调函数的,
直到看了下mapbox源码才发现
 addControl(control: IControl, position?: ControlPosition): this {
        if (position === undefined) {
            if (control.getDefaultPosition) {
                position = control.getDefaultPosition();
            } else {
                position = 'top-right';
            }
        }
        if (!control || !control.onAdd) {
            return this.fire(new ErrorEvent(new Error(
                'Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.')));
        }
        //在此处调用了onAdd方法,我们发现mapbox在添加Control中,必须得定义onAdd和onRemove回调函数
        const controlElement = control.onAdd(this);
        this._controls.push(control);

        const positionContainer = this._controlPositions[position];
        if (position.indexOf('bottom') !== -1) {
            positionContainer.insertBefore(controlElement, positionContainer.firstChild);
        } else {
            positionContainer.appendChild(controlElement);
        }
        return this;
    }

其他

后面还封装了测量模块等有时间再写吧,欢迎大家留言讨论 QQ 1826356125

自己花了好几个月写了一个基于mapbox的gis框架,下载地址,能很方便大家学习,也很容易应用到gis展示的项目中,感兴趣加我QQ

你可能感兴趣的:(mapbox,前端,javascript,html)