openlayers6 踩坑系列(三)绘制和编辑(矩形,圆,多边形,点,线)

重要:博客内使用地图全替换为WMap,想使用的请参考我这篇博客(WMap)

最近在做矢量图形这一块,发现圆形和多边形,都可以使用openlayers自带的Modify类进行编辑,然后随意放大缩小旋转,但是 矩形尤为特别,因为矩形属于Polygon, 也是多边形的一种, 但是却实现不了矩形的编辑,为此,我以下做了最全的矢量图 主动绘制、根据坐标点绘制和编辑 的 功能, 有不懂的可以评论哦,我一般都会看的~

(因为是我自己封装的类,我就不把全部代码发出来,就发关键代码,下面有些代码你不知道做什么的,可以和我说下喔~)

根据已有坐标点绘制矩形、圆、多边形、点、线:

矩形实例化是通过下列方式生成:

// 根据已有坐标生成矩形

// path 为传进来的坐标数组 [minX, minY, maxX, maxY] 
// 通俗说来就是西南顶点的经纬度和东北顶点的经纬度
// extData 是自定义数据

import { fromExtent } from 'ol/geom/Polygon'

/**
   * 矩形
   */
  

点、线、矩形、圆形和多边形是通过这种方式生成:

// formatMetersToRadius 方法可以参考我前面的文章 踩坑系列二
// fromLonLat 是用来经纬度转换的,因为实际经纬度和地图经纬度存在误差
// commonStyle 是我自己封装的通用样式方法, 其就是返回new Style的函数
// extData 是我想在图形中添加一些自定义的数据, 需要你才加这行代码

// OlFeature类是继承于Feature的自定义类
// this.olTarget 是OlFeature类的实例属性,用来代替每个实例化的图形的,
// 因为我这里图形实例,是通过调用方法形式来的,下面的都是方法,而不是类,
// 当然如果你想做成和高德地图一样的,那就要写成内部类

import { fromLonLat } from 'ol/proj'

// 点绘制
Point (center, option = {}, extData = {}) {
    let centerPath = fromLonLat([center[0], center[1]], 'EPSG:4326')
    const tmp = new Point(centerPath)
    this.olTarget = new OlFeature(tmp)
    this.olTarget.set('extData', extData)
    if (extData.id) {
      this.olTarget.setId(extData.id)
    } else {
      this.olTarget.setId(this.olTarget.ol_uid)
    }
    this.olTarget.setStyle(this.commonStyle(option))
    this.graphTool.Point.style = this.commonStyle(option)
    this.source.addFeature(this.olTarget)
    return this
  }
// 线段绘制
Line (path, option = {}, extData = {}) {
    const finalPath = []
    // 进行坐标系转换 转4326坐标系
    for (let i = 0; i < path.length; i++) {
      const tmpPoint = fromLonLat([path[i][0], path[i][1]], 'EPSG:4326')
      finalPath.push(tmpPoint)
    }
    const tmp = new LineString(finalPath)
    this.olTarget = new OlFeature({
      geometry: tmp
    })
    this.olTarget.set('extData', extData)
    if (extData.id) {
      this.olTarget.setId(extData.id)
    } else {
      this.olTarget.setId(this.olTarget.ol_uid)
    }
    this.olTarget.setStyle(this.commonStyle(option))
    this.graphTool.Line.style = this.commonStyle(option)
    this.source.addFeature(this.olTarget)
    return this
  }
// 矩形绘制
Rectangle (path, option = {}, extData = {}) {
    const tmpArr = []
    if (path.length !== 4) {
      console.warn('WMap 图形类警告: 传入路径错误,请检查~')
      return
    }
    tmpArr[0] = path.slice(0, path.length / 2)
    tmpArr[1] = path.slice(-(path.length / 2))

    const finalPath = []
    // 进行坐标系转换 转4326坐标系
    for (let i = 0; i < tmpArr.length; i++) {
      const tmpPoint = fromLonLat([tmpArr[i][0], tmpArr[i][1]], 'EPSG:4326')
      finalPath.push(tmpPoint)
    }
    const tmp = fromExtent(finalPath.flat())
    this.olTarget = new OlFeature({
      geometry: tmp
    })
    this.olTarget.set('extData', extData)
    if (extData.id) {
      this.olTarget.setId(extData.id)
    } else {
      this.olTarget.setId(this.olTarget.ol_uid)
    }
    this.olTarget.setStyle(this.commonStyle(option))
    this.graphTool.Rectangle.style = this.commonStyle(option)
    this.source.addFeature(this.olTarget)
    return this
  }
// 圆形绘制 
Circle (center, radius, option = {}, extData = {}) {
    // 进行坐标系转换 转4326坐标系
    const finalPath = fromLonLat([center[0], center[1]], 'EPSG:4326')
    const finalRadius = this.formatMetersToRadius(radius)
    const tmp = new Circle(finalPath, finalRadius)
    this.olTarget = new OlFeature(tmp)
    this.olTarget.set('extData', extData)
    if (extData.id) {
      this.olTarget.setId(extData.id)
    } else {
      this.olTarget.setId(this.olTarget.ol_uid)
    }
    this.olTarget.setStyle(this.commonStyle(option))
    this.graphTool.Circle.style = this.commonStyle(option)
    this.source.addFeature(this.olTarget)
    return this
  }

// 多边形绘制

Polygon (path, option = {}, extData = {}) {
    const finalPath = []
    // 进行坐标系转换 转4326坐标系
    for (let i = 0; i < path.length; i++) {
      const tmpPoint = fromLonLat([path[i][0], path[i][1]], 'EPSG:4326')
      finalPath.push(tmpPoint)
    }
    const tmp = new Polygon([finalPath])
    this.olTarget = new OlFeature(tmp)
    this.olTarget.set('extData', extData)
    if (extData.id) {
      this.olTarget.setId(extData.id)
    } else {
      this.olTarget.setId(this.olTarget.ol_uid)
    }
    this.olTarget.setStyle(this.commonStyle(option))
    this.graphTool.Polygon.style = this.commonStyle(option)
    this.source.addFeature(this.olTarget)
    return this
  }

主动绘制矩形、圆、多边形

// 开始主动绘制的方法 
// dispatchEvent 这里使用了浏览器的事件派发,有不懂的可以问下我

this.beginPaint = function (type) {
      let geometryFunction // 更新几何坐标时调用的函数。
      let penValue = 'Circle' // 笔尖类型 默认为Circle
      switch (type) {
        case 'Point':
          penValue = 'Point'
          break
        case 'Line':
          penValue = 'LineString'
          break
        // 默认不写参数 即为圆形
        case 'Circle':
          break
        case 'Polygon':
          penValue = 'Polygon'
          break
        case 'Rectangle':
          geometryFunction = this.paintRectangle()
          break
        case 'Square':
          geometryFunction = this.paintSquare()
          break
      }
      if (this.graphTool[type].style) {
        this.vector.setStyle(this.graphTool[type].style)
      }
      let freehandFlag = false
      switch (type) {
        case 'Polygon':
        case 'Line':
          freehandFlag = false
          break
        default: 
          freehandFlag = true
          break
      }
      this.drawTarget = new OlDraw({
        // 数据源
        source: this.source,
        // 绘制类型
        type: penValue,
        geometryFunction: geometryFunction,
        freehand: freehandFlag, // 手绘模式
        stopClick: true
      })
      // 将draw对象添加到map中,然后就可以进行图形绘制了
      this.map.addGraph(this.drawTarget)
      this.drawTarget.setActive(true)
      this.drawTarget.addEventListener('drawend', e => {
        // 将当前绘制的矢量图形 通过done事件触发抛出去
        let targets = this.WEvents.get('WGraph(done)')
        this.drawFeature = this.buildFeature(e.feature)
        e.feature.set('WTYPE', 'OlDraw')
        window.dispatchEvent(targets.costomEvent)
      })
    }

/**
   * 绘制矩形
   */
  paintRectangle () {
    return createBox()
  }
  /**
   * 绘制正方形
   * @param {*} params
   */
  paintSquare () {
    return createRegularPolygon(4)
  }

编辑点、线、矩形、圆、多边形:

这里编辑多边形和圆都很简单,其他类似的博客或帖子都用了 Select和Modify来编辑,但是我在编辑矩形时,发现矩形居然和多边形一样,可以随意改变,但是我们生成矩形时 fromExtent 就限定了只能传 [minX, minY, maxX, maxY] 这种坐标数组, 而不是像多边形一样,把所有坐标点传上去,所以就要按我下面的这种方式来编辑图形

(当类型为线、多边形(矩形除外)、圆形时,允许新增点, 类型为点、矩形时,就利用克隆图形,不新增点)

import { always, never, platformModifierKeyOnly, primaryAction } from 'ol/events/condition'

/**
 * 计算矢量图形中心点相关参数
 * @param {*} geometry 
 * @returns 
 */
const calculateCenter = geometry => {
	let center, coordinates, minRadius
	const type = geometry.getType()
	if (type === 'Polygon') {
		let x = 0
		let y = 0
		let i = 0
		coordinates = geometry.getCoordinates()[0].slice(1)
		coordinates.forEach(function(coordinate) {
			x += coordinate[0]
			y += coordinate[1]
			i++
		})
		center = [x / i, y / i]
	} else if (type === 'LineString') {
		center = geometry.getCoordinateAt(0.5)
		coordinates = geometry.getCoordinates()
	} else {
		center = getCenter(geometry.getExtent())
	}
	let sqDistances
	if (coordinates) {
		sqDistances = coordinates.map(function(coordinate) {
			const dx = coordinate[0] - center[0]
			const dy = coordinate[1] - center[1]
			return dx * dx + dy * dy
		})
		minRadius = Math.sqrt(Math.max.apply(Math, sqDistances)) / 3
	} else {
		minRadius = Math.max(getWidth(geometry.getExtent()), getHeight(geometry.getExtent())) / 3
	}
	return {
		center: center,
		coordinates: coordinates,
		minRadius: minRadius,
		sqDistances: sqDistances
	}
}


// 判断图形类型
/**
   * 判断矢量图形形状
   * e 等同于 feature.getGeometry()
   */
  judgeShape (e) {
    let type = e.getType()
    if (type === 'Polygon') {
      let arr = e.getCoordinates().flat()
      if (arr.length === 5) {
        const rect = arr.slice(0, 4)
        if ((rect[0][0] === rect[1][0] && rect[0][1] === rect[3][1]) || (rect[0][1] === rect[1][1] && rect[0][0] === rect[3][0])) {
          type = 'Rectangle'
        }
      }
    } else if (type === 'LineString') {
      type = 'Line'
    }
    return type
  }

/**
* 编辑矢量图形
*/
editPaint () {
    const defaultStyle = new Modify({ source: this.source }).getOverlay().getStyleFunction()
    let _this = this
    const modify = new Modify({
      source: this.source,
      condition: function (event) {
        return primaryAction(event) && !platformModifierKeyOnly(event)
      },
      deleteCondition: never,
      insertVertexCondition: always,
      style: function (feature) {
        let type = _this.judgeShape(feature.get('geometries')[0])
        if (type !== 'Line'){
        feature.get('features').forEach(function (modifyFeature) {
            const modifyGeometry = modifyFeature.get('modifyGeometry')
            if (modifyGeometry) {
              const point = feature.getGeometry().getCoordinates()
              let modifyPoint = modifyGeometry.point
              if (!modifyPoint) {
                // save the initial geometry and vertex position
                modifyPoint = point
                modifyGeometry.point = modifyPoint
                modifyGeometry.geometry0 = modifyGeometry.geometry
                // get anchor and minimum radius of vertices to be used
                const result = _this.calculateCenter(modifyGeometry.geometry0)
                modifyGeometry.center = result.center
                modifyGeometry.minRadius = result.minRadius
              }

              const center = modifyGeometry.center
              const minRadius = modifyGeometry.minRadius
              let dx, dy
              dx = modifyPoint[0] - center[0]
              dy = modifyPoint[1] - center[1]
              const initialRadius = Math.sqrt(dx * dx + dy * dy)
              if (initialRadius > minRadius) {
                const initialAngle = Math.atan2(dy, dx)
                dx = point[0] - center[0]
                dy = point[1] - center[1]
                const currentRadius = Math.sqrt(dx * dx + dy * dy)
                if (currentRadius > 0) {
                  const currentAngle = Math.atan2(dy, dx)
                  
                  const geometry = modifyGeometry.geometry0.clone()
                  geometry.scale(currentRadius / initialRadius, undefined, center)
                  const typeName = modifyGeometry.geometry0.getType()
                  if (typeName === 'Polygon') {
                    let shapeName = _this.judgeShape(modifyGeometry.geometry0)
                    if (shapeName !== 'Rectangle') {
                      geometry.rotate(currentAngle - initialAngle, center)
                    }
                  } else if (typeName === 'Circle') {
                    geometry.translate(dx, dy)
                  } else if (typeName === 'Point') {
                    geometry.translate(dx, dy)
                  }
                  modifyGeometry.geometry = geometry
                }
              }
            }
        })
      }
        return defaultStyle(feature)
      }
    })
    this.map.addGraph(modify)
    const trans = new Translate({
      condition: function (event) {
        return primaryAction(event) && platformModifierKeyOnly(event)
      },
      layers: [this.vector]
    })
    this.map.addGraph(trans)
    modify.on('modifystart', e => {
      let _this = this
      e.features.forEach(function (feature) {
        let shape = _this.judgeShape(feature.getGeometry())
        if (shape === 'Line' || shape === 'Polygon' || shape === 'Circle') {
          // 当类型为线、多边形(矩形除外)、圆形时,允许新增点
        } else {
          // 克隆图形,不能新增点
          feature.set('modifyGeometry', { geometry: feature.getGeometry().clone() }, true)
        }
      })
    })

    modify.on('modifyend', e => {
      let _this = this
      e.features.forEach(function (feature) {
        const modifyGeometry = feature.get('modifyGeometry')
        if (modifyGeometry) {
          feature.setGeometry(modifyGeometry.geometry)
          feature.unset('modifyGeometry', true)
        }
        let targets = _this.WEvents.get('WGraph(modifyend)')
        _this.drawFeature = _this.buildFeature(feature)
        feature.set('WTYPE', 'OlDraw')
        window.dispatchEvent(targets.costomEvent)
      })
    })
  }

你可能感兴趣的:(openlayers,css,css3,前端)