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 }