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 }