Turf.js 的使用(一)

Turf.js 是一个开源的空间分析库,由 Mapbox 提供。源码地址,在其官网中都有 Mapbox 作为底图的示例。

并且在 Mapbox 官网也推荐使用 Turf.js 作为空间分析库。

用 Turf.js 是因为最近一个项目中要用到线的分隔等功能,因为使用的是高德地图,对这一项空间分析、拓扑的功能不足,所以找到了这个库。

一、功能

主要使用的功能:

1、线的交点--lineIntersect

给定两条线,返回两条线的交点,交点个数可以是:0、1、2……n个

从源码中可知,就是遍历两条线的线段(每两个点位一个线段),根据其坐标范围得到是否有交点,有交点再计算交点位置。

下面是高德地图的示例(用 MouseTool 画线)

    sliceLine() {
      if (this.gaodeMap) {
        if (this.aMapSliceLine) {
          this.gaodeMap.baseMap.remove(this.aMapSliceLine)
          this.aMapSliceLine = null
          this.gaodeMap.baseMap.remove(this.intersectPointMarker)
          this.intersectPointMarker = []
        }
        this.gaodeMap.baseMap.setDefaultCursor('crosshair')
        this.gaodeMap.drawPolyline({ strokeColor: '#33FF66', strokeWeight: 4 })

        this.mouseToolEvent &&
          this.gaodeMap.amapApi.event.removeListener(this.mouseToolEvent)
        this.mouseToolEvent = this.gaodeMap.amapApi.event.addListener(
          this.gaodeMap.mouseTool,
          'draw',
          event => {
            this.aMapSliceLine = event.obj
            this.gaodeMap.baseMap.setDefaultCursor('pointer')
            this.gaodeMap.mouseTool.close()

            // 进行交点计算
            if (this.aMapLine) {
              let line1 = [],
                line2 = []
              this.aMapLine.getPath().map(item => {
                line1.push([item.lng, item.lat])
              })
              this.aMapSliceLine.getPath().map(item => {
                line2.push([item.lng, item.lat])
              })

              let intersectPoint = lineIntersect(
                lineString(line1),
                lineString(line2)
              )
              intersectPoint.features &&
                intersectPoint.features.map(item => {
                  this.intersectPointMarker.push(
                    this.gaodeMap.addMarker({
                      position: item.geometry.coordinates
                    })
                  )
                })
            }
            this.mouseToolEvent &&
              this.gaodeMap.amapApi.event.removeListener(this.mouseToolEvent)
          }
        )
      }
    }

2、线上的点分隔线--lineSlice

根据给定起始点和结束点,返回两点间的线,不在要分隔的线上面,就找这两点离线最近的点,作为分隔点。

3、分隔线--lineSplit

这个分隔线,用来分隔的可以是点、多点、线、多线、面等,对于非点的,第一步会找出和被分割线的交点,再根据这些点分隔线。(即最终都是用电分隔)

下面是切割线的高德地图示例(MouseTool 画线)

    // 切割线
    splitLine() {
      if (this.gaodeMap) {
        if (this.aMapSplitLine) {
          this.gaodeMap.baseMap.remove(this.aMapSplitLine)
          this.aMapSplitLine = null
          this.gaodeMap.baseMap.remove(this.splitLines)
          this.splitLines = []
        }
        this.gaodeMap.baseMap.setDefaultCursor('crosshair')
        this.gaodeMap.drawPolyline({ strokeColor: '#dd3333', strokeWeight: 4 })

        this.mouseToolEvent &&
          this.gaodeMap.amapApi.event.removeListener(this.mouseToolEvent)
        this.mouseToolEvent = this.gaodeMap.amapApi.event.addListener(
          this.gaodeMap.mouseTool,
          'draw',
          event => {
            this.aMapSplitLine = event.obj
            this.gaodeMap.baseMap.setDefaultCursor('pointer')
            this.gaodeMap.mouseTool.close()

            // 进行切割
            if (this.aMapLine) {
              let line1 = [], line2 = []
              this.aMapLine.getPath().map(item => {
                line1.push([item.lng, item.lat])
              })
              this.aMapSplitLine.getPath().map(item => {
                line2.push([item.lng, item.lat])
              })

              let split = lineSplit(
                lineString(line1),
                lineString(line2)
              )
              split.features &&
                split.features.map(item => {
                  this.splitLines.push(
                    this.gaodeMap.drawLine({
                      path: item.geometry.coordinates,
                      strokeColor: randomColor2(),
                      strokeWeight: 3,
                    })
                  )
                })
            }
            this.mouseToolEvent &&
              this.gaodeMap.amapApi.event.removeListener(this.mouseToolEvent)
          }
        )
      }
    }

二、线的差集

在实现了线的分隔、高亮等。在编辑的时候需要回显,那么一条父级线以及有了分隔线,要对剩下的线进行分隔,那么久要对父级线和子级线进行差集计算。

查找了 Truf.js 和别的库,没有发现这样的功能,看来只能自己根据自己的实际业务需求写了。

具体业务下的思路

线的差集(子线一定是父线的子集,那么可以特殊考虑下面几种情况)

1、子线和父线一致,差集为空;

2、子线的开始点是父线开始点:父线切除子线中共同的部分,子线终点作为父线的起点;

3、子线的结束点是父线结束点:父线切除子线中共同的部分,子线起点作为父线的终点;

4、子线是父线中间部分:父线切除子线中共同的部分,子线起点为切割线段1的终点,子线终点为切割线2的起点点;

对于4中,可能子线的两端是父线中的节点,对于画线分隔这样的概率很小,所以在这里没有考虑(视情况是否考虑)。

下面是代码实现(线的坐标转为字符串)

    // 子级线和父级线的差集计算
    getLineDifference(parentPointStrList, sonPointStr) {
      const newParentPointStrList = []
      parentPointStrList.map(parentPointStr => {
        const temp = sonPointStr.slice(1, -1).split('],')
        const startPoint = temp[0] + ']'
        const startPoints = temp.slice(0, -1).join('],') + ']'
        const endPoint = temp[temp.length - 1]
        const endPoints = temp.slice(1).join('],')
        const centerPoints = temp.slice(1, -1).join('],') + ']'

        if (parentPointStr.indexOf(sonPointStr) > -1) {
          console.log('相同')
        } else if (parentPointStr.indexOf(startPoints) > -1) {
          newParentPointStrList.push(parentPointStr.replace(startPoints, endPoint))
        } else if (parentPointStr.indexOf(endPoints) > -1) {
          newParentPointStrList.push(parentPointStr.replace(endPoints, startPoint))
        } else if (parentPointStr.indexOf(centerPoints) > -1) {
          newParentPointStrList.push(parentPointStr.split(centerPoints)[0] + startPoint + ']')
          newParentPointStrList.push('[' + endPoint + parentPointStr.split(centerPoints)[1])
        } else {
          newParentPointStrList.push(parentPointStr)
        }
      })

      return newParentPointStrList
    }

 

posted @ 2020-01-18 15:49  漠里  阅读(14328)  评论(0编辑  收藏  举报