|
@@ -94,7 +94,6 @@
|
|
|
</div>
|
|
|
<el-form
|
|
|
class="height-filter panel"
|
|
|
- label-position="top"
|
|
|
>
|
|
|
高度筛选
|
|
|
<el-form-item
|
|
@@ -125,6 +124,53 @@
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</el-form>
|
|
|
+ <el-form
|
|
|
+ class="add-point panel"
|
|
|
+ >
|
|
|
+ 手动补点
|
|
|
+ <el-form-item
|
|
|
+ :label="`补点模式`"
|
|
|
+ >
|
|
|
+ <el-switch
|
|
|
+ v-model="formData.isAddingPoint"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ :label="`高度差上限`"
|
|
|
+ title="要想判定两个相邻区域连通,z坐标值之差不得超过此上限"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="formData.connectionMaxHeightGap"
|
|
|
+ type="number"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ :label="`补点高度`"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="formData.addPointHeight"
|
|
|
+ type="number"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <div class="btn-group">
|
|
|
+ <el-button
|
|
|
+ @click="onAddPointUndo"
|
|
|
+ >
|
|
|
+ undo
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ @click="onAddPointRedo"
|
|
|
+ >
|
|
|
+ redo
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="onAddPointSave"
|
|
|
+ >
|
|
|
+ 保存
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -133,24 +179,30 @@
|
|
|
import * as d3 from "d3";
|
|
|
import { getWholeData } from "./api.js";
|
|
|
import { ElLoading } from 'element-plus'
|
|
|
-
|
|
|
-const LENGTH_PER_POINT = 0.36
|
|
|
+import {computePointDistanceAndRowSlope} from '@/utils.js'
|
|
|
|
|
|
// 视口尺寸
|
|
|
let svgWidth = document.documentElement.clientWidth - 200
|
|
|
let svgHeight = document.documentElement.clientHeight - 280
|
|
|
let svgRatio = svgWidth / svgHeight
|
|
|
|
|
|
-let pxPerUnitLength = 0 // 原始单位长度对应的像素数
|
|
|
+let pxPerUnitLength = 0 // 原始数据1单位长度对应的像素数
|
|
|
let rawWholeData = []
|
|
|
let wholeDataForRender = []
|
|
|
let startPoint = null
|
|
|
let endPoint = null
|
|
|
+let pointDistance = 0 // 最近相邻点间距离(单位:原始数据中长度单位)
|
|
|
+let rowSlope = 0 // 点位构成的排的斜率 [0deg, 90deg)
|
|
|
|
|
|
// 全部数据点的分布中心
|
|
|
let xCenter = 0
|
|
|
let yCenter = 0
|
|
|
|
|
|
+let svgNode = null
|
|
|
+let gNode = null
|
|
|
+let zoomObj = null
|
|
|
+let brushObj = null
|
|
|
+
|
|
|
function resetGlobalVars() {
|
|
|
pxPerUnitLength = 0 // 原始单位长度对应的像素数
|
|
|
rawWholeData = []
|
|
@@ -159,16 +211,136 @@ function resetGlobalVars() {
|
|
|
yCenter = 0
|
|
|
startPoint = null
|
|
|
endPoint = null
|
|
|
+ pointDistance = 0
|
|
|
+ rowSlope = 0
|
|
|
}
|
|
|
|
|
|
-let svgNode = null
|
|
|
-let gNode = null
|
|
|
-let zoomObj = null
|
|
|
+function zoomed({transform}) {
|
|
|
+ gNode.attr("transform", transform);
|
|
|
+}
|
|
|
+
|
|
|
+function brushed(e, componentInst) {
|
|
|
+ if (e.selection) {
|
|
|
+ console.log('bursh area in px: ', e.selection[0][0], e.selection[0][1], e.selection[1][0], e.selection[1][1]);
|
|
|
+ const brushLeftPx = e.selection[0][0]
|
|
|
+ const brushTopPx = e.selection[0][1]
|
|
|
+ const brushRightPx = e.selection[1][0]
|
|
|
+ const brushBottomPx = e.selection[1][1]
|
|
|
+
|
|
|
+ let translateX = 0
|
|
|
+ let translateY = 0
|
|
|
+ let scale = 1
|
|
|
+ const gNodetransformStr = gNode.attr('transform')
|
|
|
+ if (gNodetransformStr) {
|
|
|
+ const translateStr = gNodetransformStr.split(' ')[0]
|
|
|
+ const translateRegex = /translate\(([^,]+),([^,]+)\)/;
|
|
|
+ const translateMatch = translateStr.match(translateRegex);
|
|
|
+
|
|
|
+ const scaleStr = gNodetransformStr.split(' ')[1]
|
|
|
+ const scaleRegex = /scale\(([^)]+)\)/;
|
|
|
+ const scaleMatch = scaleStr.match(scaleRegex);
|
|
|
+
|
|
|
+ translateX = Number(translateMatch[1]);
|
|
|
+ translateY = Number(translateMatch[2]);
|
|
|
+ scale = Number(scaleMatch[1]);
|
|
|
+ }
|
|
|
+ console.log('transform info: ', translateX, translateY, scale);
|
|
|
+
|
|
|
+ const brushLeftPxBeformTransform = (brushLeftPx - translateX) / scale
|
|
|
+ const brushTopPxBeformTransform = (brushTopPx - translateY) / scale
|
|
|
+ const brushRightPxBeformTransform = (brushRightPx - translateX) / scale
|
|
|
+ const brushBottomPxBeformTransform = (brushBottomPx - translateY) / scale
|
|
|
+
|
|
|
+ const brushLeft = (brushLeftPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
+ const brushTop = (brushTopPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
+ const brushRight = (brushRightPxBeformTransform - svgWidth / 2) / pxPerUnitLength + xCenter
|
|
|
+ const brushBottom = (brushBottomPxBeformTransform - svgHeight / 2) / pxPerUnitLength + yCenter
|
|
|
+ console.log('brush area in raw coordinate: ', brushLeft, brushTop, brushRight, brushBottom);
|
|
|
+
|
|
|
+ const affectionAreaLeft = brushLeft - pointDistance * 1.25
|
|
|
+ const affectionAreaTop = brushTop - pointDistance * 1.25
|
|
|
+ const affectionAreaRight = brushRight + pointDistance * 1.25
|
|
|
+ const affectionAreaBottom = brushBottom + pointDistance * 1.25
|
|
|
+ console.log('affection area in raw coordinate: ', affectionAreaLeft, affectionAreaTop, affectionAreaRight, affectionAreaBottom);
|
|
|
+
|
|
|
+ // 筛选出框选区域可能影响到的所有外围点
|
|
|
+ // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
+ // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ const affectedPointList = []
|
|
|
+ for (const point of rawWholeData) {
|
|
|
+ // 如果z不合格,pass
|
|
|
+ if (Math.abs(point.z - componentInst.formData.addPointHeight) > componentInst.formData.connectionMaxHeightGap) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在affection范围外,pass
|
|
|
+ if (point.x < affectionAreaLeft || point.x > affectionAreaRight || point.y < affectionAreaTop || point.y > affectionAreaBottom) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在框选区域内,pass
|
|
|
+ if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ affectedPointList.push(point)
|
|
|
+ }
|
|
|
+ // todo: 如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ console.log('affectedPointList: ', affectedPointList);
|
|
|
+
|
|
|
+ // 筛选出框选区域内所有已存在的点
|
|
|
+ // 筛选规则:除了x、y坐标,还要看z坐标是否在当前连通规则给定的高度区间。
|
|
|
+ // 筛选后,如果各已存在的点之间有重叠的,则报错,要求调整可判定连通的最大高度差。
|
|
|
+ const pointInBrushList = []
|
|
|
+ for (const point of rawWholeData) {
|
|
|
+ // 如果z不合格,pass
|
|
|
+ if (Math.abs(point.z - componentInst.formData.addPointHeight) > componentInst.formData.connectionMaxHeightGap) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 如果在框选区域内,留下
|
|
|
+ if (point.x >= brushLeft && point.x <= brushRight && point.y >= brushTop && point.y <= brushBottom) {
|
|
|
+ pointInBrushList.push(point)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gNode.selectAll('rect')
|
|
|
+ .attr('fill', (d) => {
|
|
|
+ if (affectedPointList.find((affectedPoint) => {
|
|
|
+ // console.log(affectedPoint.id, d[4]);
|
|
|
+ return affectedPoint.id === d[4]
|
|
|
+ })) {
|
|
|
+ return 'blue'
|
|
|
+ } else if (pointInBrushList.find((pointInBrush) => {
|
|
|
+ return pointInBrush.id === d[4]
|
|
|
+ })) {
|
|
|
+ return 'green'
|
|
|
+ } else {
|
|
|
+ return `black`
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (!affectedPointList.length) {
|
|
|
+ window.alert('请在已有点位附近新增点位。')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!pointInBrushList.length) {
|
|
|
+ window.alert('请保证框选区域内至少已有一个点位。')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let activePointIdx = 0
|
|
|
+ while(activePointIdx <= pointInBrushList.length - 1) {
|
|
|
+ console.log('shabi');
|
|
|
+ activePointIdx++
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ console.log('cancel brush');
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
export default {
|
|
|
name: 'App',
|
|
|
data() {
|
|
|
return {
|
|
|
+ // sceneNameOrUrl: 'SS-t-XkquhxxurM',
|
|
|
sceneNameOrUrl: 'SS-t-NZUICC2fRLi',
|
|
|
infoText: '',
|
|
|
loadingHandler: null,
|
|
@@ -179,6 +351,9 @@ export default {
|
|
|
endPoint: '-28.4, 12.2',
|
|
|
maxHeight: '',
|
|
|
minHeight: '',
|
|
|
+ isAddingPoint: false,
|
|
|
+ connectionMaxHeightGap: 100,
|
|
|
+ addPointHeight: '',
|
|
|
}
|
|
|
}
|
|
|
},
|
|
@@ -190,6 +365,19 @@ export default {
|
|
|
return this.getInputPathLength(this.formData.path2)
|
|
|
},
|
|
|
},
|
|
|
+ watch: {
|
|
|
+ 'formData.isAddingPoint': {
|
|
|
+ handler(vNew) {
|
|
|
+ if (vNew) {
|
|
|
+ svgNode.on(".zoom", null)
|
|
|
+ svgNode.append('g').attr("class", "brush").call(brushObj)
|
|
|
+ } else {
|
|
|
+ svgNode.call(zoomObj)
|
|
|
+ svgNode.selectAll('g.brush').remove()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
mounted() {
|
|
|
svgNode = d3.select('.svgWrapper').append("svg")
|
|
|
.attr("width", svgWidth)
|
|
@@ -281,6 +469,11 @@ export default {
|
|
|
getWholeData(this.sceneNameOrUrl).then((res) => {
|
|
|
rawWholeData = res
|
|
|
|
|
|
+ // 相邻点位间距离
|
|
|
+ const temp = computePointDistanceAndRowSlope(rawWholeData)
|
|
|
+ pointDistance = temp[0]
|
|
|
+ rowSlope = temp[1]
|
|
|
+
|
|
|
// 所有点的分布情况
|
|
|
let xArray = rawWholeData.map((eachPoint) => {
|
|
|
return eachPoint.x
|
|
@@ -298,7 +491,7 @@ export default {
|
|
|
})
|
|
|
let zLength = Math.max(...zArray) - Math.min(...zArray)
|
|
|
let zMin = Math.min(...zArray)
|
|
|
-
|
|
|
+ this.formData.addPointHeight = zMin + zLength / 2
|
|
|
let areaRatio = xLength / yLength
|
|
|
|
|
|
// 各个点坐标映射到视口坐标
|
|
@@ -321,10 +514,10 @@ export default {
|
|
|
}
|
|
|
|
|
|
gNode.selectAll('rect').data(wholeDataForRender).enter().append('rect')
|
|
|
- .attr('x', (d) => d[0] - LENGTH_PER_POINT * pxPerUnitLength / 2)
|
|
|
- .attr('y', (d) => d[1] - LENGTH_PER_POINT * pxPerUnitLength / 2)
|
|
|
- .attr('width', LENGTH_PER_POINT * pxPerUnitLength)
|
|
|
- .attr('height', LENGTH_PER_POINT * pxPerUnitLength)
|
|
|
+ .attr('x', (d) => d[0] - pointDistance * pxPerUnitLength / 2)
|
|
|
+ .attr('y', (d) => d[1] - pointDistance * pxPerUnitLength / 2)
|
|
|
+ .attr('width', pointDistance * pxPerUnitLength)
|
|
|
+ .attr('height', pointDistance * pxPerUnitLength)
|
|
|
.attr('fill', (d) => {
|
|
|
return `rgba(${Math.round((d[2] -zMin) / zLength * 255)}, 0, 0, 1)`
|
|
|
})
|
|
@@ -347,9 +540,9 @@ export default {
|
|
|
zoomObj = d3.zoom().on("zoom", zoomed)
|
|
|
svgNode.call(zoomObj);
|
|
|
|
|
|
- function zoomed({transform}) {
|
|
|
- gNode.attr("transform", transform);
|
|
|
- }
|
|
|
+ brushObj = d3.brush().on("end", (e) => {
|
|
|
+ brushed(e, this)
|
|
|
+ })
|
|
|
}).finally(() => {
|
|
|
this.loadingHandler.close()
|
|
|
})
|
|
@@ -425,14 +618,14 @@ export default {
|
|
|
.classed('path1', true)
|
|
|
.attr('cx', (d) => d[0])
|
|
|
.attr('cy', (d) => d[1])
|
|
|
- .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 2)
|
|
|
+ .attr('r', pointDistance * pxPerUnitLength / 2)
|
|
|
.attr('fill', '#000088')
|
|
|
.attr('pointer-events', 'none')
|
|
|
gNode.selectAll('circle.path2').data(pathDataForRender2).enter().append('circle')
|
|
|
.classed('path2', true)
|
|
|
.attr('cx', (d) => d[0])
|
|
|
.attr('cy', (d) => d[1])
|
|
|
- .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3)
|
|
|
+ .attr('r', pointDistance * pxPerUnitLength / 3)
|
|
|
.attr('fill', 'blue')
|
|
|
.attr('pointer-events', 'none')
|
|
|
},
|
|
@@ -495,7 +688,7 @@ export default {
|
|
|
.classed('start', true)
|
|
|
.attr('cx', (d) => d[0])
|
|
|
.attr('cy', (d) => d[1])
|
|
|
- .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3.5)
|
|
|
+ .attr('r', pointDistance * pxPerUnitLength / 3.5)
|
|
|
.attr('fill', 'rgba(50, 255, 50, 1)')
|
|
|
.attr('pointer-events', 'none')
|
|
|
}
|
|
@@ -509,7 +702,7 @@ export default {
|
|
|
.classed('end', true)
|
|
|
.attr('cx', (d) => d[0])
|
|
|
.attr('cy', (d) => d[1])
|
|
|
- .attr('r', LENGTH_PER_POINT * pxPerUnitLength / 3.5)
|
|
|
+ .attr('r', pointDistance * pxPerUnitLength / 3.5)
|
|
|
.attr('fill', 'green')
|
|
|
.attr('pointer-events', 'none')
|
|
|
}
|
|
@@ -558,7 +751,16 @@ export default {
|
|
|
gNode.selectAll('rect').attr('visibility', (d) => {
|
|
|
return 'visible'
|
|
|
})
|
|
|
- }
|
|
|
+ },
|
|
|
+ onAddPointUndo() {
|
|
|
+
|
|
|
+ },
|
|
|
+ onAddPointRedo() {
|
|
|
+
|
|
|
+ },
|
|
|
+ onAddPointSave() {
|
|
|
+
|
|
|
+ },
|
|
|
},
|
|
|
}
|
|
|
</script>
|
|
@@ -605,11 +807,14 @@ export default {
|
|
|
.map > .map-control-area > .panel > .btn {
|
|
|
margin: 10px;
|
|
|
}
|
|
|
-.map > .map-control-area > .panel.height-filter {
|
|
|
+.map > .map-control-area > .panel {
|
|
|
padding-left: 10px;
|
|
|
padding-right: 10px;
|
|
|
}
|
|
|
-.map > .map-control-area > .panel.height-filter > .btn-group {
|
|
|
+.map > .map-control-area > .panel input{
|
|
|
+ width: 80px;
|
|
|
+}
|
|
|
+.map > .map-control-area > .panel > .btn-group {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
margin-bottom: 10px;
|