fuchengshen
2022-07-09 3a25c05b5ab837714f30469cdcb1e2a77d8d7f6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
import * as mars3d from "mars3d"
 
export let map // mars3d.Map三维地图对象
let polygonLayer
let graphicLayer
 
export const mapOptions = {
  scene: {
    center: { lat: 31.855058, lng: 117.312337, alt: 79936, heading: 0, pitch: -90 }
  }
}
 
/**
 * 初始化地图业务,生命周期钩子函数(必须)
 * 框架在地图初始化完成后自动调用该函数
 * @param {mars3d.Map} mapInstance 地图对象
 * @returns {void} 无
 */
export function onMounted(mapInstance) {
  map = mapInstance // 记录map
 
  // 加载面数据
  loadPolygon()
 
  graphicLayer = new mars3d.layer.GraphicLayer()
  map.addLayer(graphicLayer)
}
 
/**
 * 释放当前地图业务的生命周期函数
 * @returns {void} 无
 */
export function onUnmounted() {
  map = null
}
export function clearGraphicLayer() {
  graphicLayer.clear()
}
 
// 绘制线
export function drawLine() {
  clearGraphicLayer()
 
  graphicLayer.startDraw({
    type: "polyline",
    maxPointNum: 2,
    style: {
      color: "#55ff33",
      width: 3
    },
    success: (graphic) => {
      const clipLine = graphic.toGeoJSON()
      clipAllPolygon(clipLine)
 
      graphic.remove()
    }
  })
}
 
// 加载面数据
function loadPolygon() {
  polygonLayer = new mars3d.layer.GraphicLayer()
  map.addLayer(polygonLayer)
 
  const postionArr = [
    {
      postions: [
        [117.21983, 31.780687],
        [117.179661, 31.745352],
        [117.201462, 31.727826],
        [117.258453, 31.727534],
        [117.27459, 31.757619],
        [117.270985, 31.775139],
        [117.26738, 31.774701],
        [117.259827, 31.783752],
        [117.21983, 31.780687]
      ]
    },
    {
      postions: [
        [117.490393, 31.88435],
        [117.596823, 31.895413],
        [117.626349, 31.784167],
        [117.449369, 31.775197],
        [117.490393, 31.88435]
      ]
    },
 
    {
      postions: [
        [117.250042, 31.954209],
        [117.273903, 31.970234],
        [117.306862, 31.957994],
        [117.303772, 31.912971],
        [117.293816, 31.901294],
        [117.269611, 31.900128],
        [117.250042, 31.954209]
      ]
    },
    {
      postions: [
        [117.168674, 31.964546],
        [117.202492, 31.926669],
        [117.132111, 31.923592],
        [117.125759, 31.95099],
        [117.168674, 31.964546]
      ]
    }
  ]
 
  for (let i = 0; i < postionArr.length; i++) {
    const polygonGraphic = new mars3d.graphic.PolygonEntity({
      positions: [postionArr[i].postions],
      style: {
        color: "#00ffff",
        opacity: 0.2,
        clampToGround: true
      }
    })
    polygonLayer.addGraphic(polygonGraphic)
  }
}
 
// 循环所有面,判断相交的去切割面
function clipAllPolygon(clipLine) {
  polygonLayer.eachGraphic(function (graphic) {
    try {
      const clippedPolygon = geoUtil.polygonClipByLine(graphic.toGeoJSON(), clipLine)
 
      graphicLayer.loadGeoJSON(clippedPolygon, {
        style: {
          randomColor: true,
          opacity: 0.5,
          outline: true,
          outlineWidth: 2,
          outlineColor: "#ffffff",
          clampToGround: true
        }
      })
    } catch (error) {
      // globalMsg('<div style="color:#ff0000;">' + error.state + ":</br>" + error.message + "</div>");S
    }
  })
}
 
/**
 * geoJson数据处理模块(需要引入turf.js)
 * 输入输出数据均为标准geoJson格式
 */
const geoUtil = {
  // 合并多边形
  unionPolygon: function (polygons) {
    let polygon = polygons[0]
    for (let i = 0; i < polygons.length; i++) {
      polygon = turf.union(polygon, polygons[i])
    }
    return polygon
  },
 
  /**
   * 线分割面
   * 面类型只能是polygon 但可以是环
   * 注:线与多边形必须有两个交点
   */
  polygonClipByLine: function (polygon, clipLine) {
    if (polygon.geometry.type === "Polygon") {
      const polyLine = turf.polygonToLine(polygon)
      if (polyLine.geometry.type === "LineString") {
        // 切割普通多边形
        return this._singlePolygonClip(polyLine, clipLine)
      } else if (polyLine.geometry.type === "MultiLineString") {
        // 切割环
        return this._multiPolygonClip(polyLine, clipLine)
      }
    } else if (polygon.geometry.type === "MultiPolygon") {
      // 若输入的多边形类型为Multipolygon则拆分成多个Polygon
      const polygons = this.multiPolygon2polygons(polygon)
      let clipPolygon = null
      let clipPolygonIndex = -1
      // 获取MultiPolygon中与切割线相交的多边形(有且只能有一个多边形相交2个交点)
      polygons.forEach(function (polygon, index) {
        const polyLine = turf.polygonToLine(polygon)
        if (turf.lineIntersect(polyLine, clipLine).features.length === 2) {
          if (!clipPolygon) {
            clipPolygon = polygon
            clipPolygonIndex = index
          } else {
            throw new Error({ state: "裁剪失败", message: "MultiPolygon只能有一个多边形与切割线存在交点" })
          }
        }
      })
      if (clipPolygonIndex !== -1) {
        polygons.splice(clipPolygonIndex, 1)
        return turf.featureCollection(polygons.concat(this.polygonClipByLine(clipPolygon, clipLine).features))
      } else {
        throw new Error({ state: "裁剪失败", message: "MultiPolygon与切割线无交点" })
      }
    } else {
      throw new Error({ state: "裁剪失败", message: "输入的多边形类型为错误" })
    }
  },
 
  _singlePolygonClip: function (polyLine, clipLine) {
    // 获得裁切点
    const intersects = turf.lineIntersect(polyLine, clipLine)
    if (intersects.features.length !== 2) {
      throw new Error({ state: "裁剪失败", message: "切割线与多边形交点应该为2个,当前交点个数为" + intersects.features.length })
    }
    // 检查切割线与多边形的位置关系 (切割线的起点和终点不能落在多边形内部)
    const clipLineLength = clipLine.geometry.coordinates.length
    const clipLineStartPoint = turf.point(clipLine.geometry.coordinates[0])
    const clipLineEndPoint = turf.point(clipLine.geometry.coordinates[clipLineLength - 1])
    const polygon = turf.polygon([polyLine.geometry.coordinates])
    if (turf.booleanPointInPolygon(clipLineStartPoint, polygon) || turf.booleanPointInPolygon(clipLineEndPoint, polygon)) {
      throw new Error({ state: "裁剪失败", message: "切割线起点或终点不能在 裁剪多边形内部" })
    }
    // 通过裁切点 分割多边形(只能获得多边形的一部分)
    const slicedPolyLine = turf.lineSlice(intersects.features[0], intersects.features[1], polyLine)
    // 裁剪线分割 保留多边形内部部分
    const slicedClipLine = turf.lineSlice(intersects.features[0], intersects.features[1], clipLine)
    // 重新拼接多边形 存在 对接的问题 所以先进行判断 如何对接裁剪的多边形和裁剪线
    const resultPolyline1 = this.connectLine(slicedPolyLine, slicedClipLine)
    // 闭合线 来构造多边形
    resultPolyline1.geometry.coordinates.push(resultPolyline1.geometry.coordinates[0])
    const resultPolygon1 = turf.lineToPolygon(resultPolyline1)
    // 构造切割的另一面多边形
    const firstPointOnLine = this.isOnLine(turf.point(polyLine.geometry.coordinates[0]), slicedPolyLine)
    const pointList = []
    if (firstPointOnLine) {
      for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
        const coordinate = polyLine.geometry.coordinates[i]
        if (!this.isOnLine(turf.point(coordinate), slicedPolyLine)) {
          pointList.push(coordinate)
        }
      }
    } else {
      let skipNum = 0 // 记录前面被跳过的点的个数
      let isStartPush = false
      for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
        const coordinate = polyLine.geometry.coordinates[i]
        if (!this.isOnLine(turf.point(coordinate), slicedPolyLine)) {
          if (isStartPush) {
            pointList.push(coordinate)
          } else {
            skipNum++
          }
        } else {
          isStartPush = true
        }
      }
      // 将前面跳过的点补充到 点数组中
      for (let i = 0; i < skipNum; i++) {
        pointList.push(polyLine.geometry.coordinates[i])
      }
    }
    const slicedPolyLine_2 = turf.lineString(pointList)
    const resultPolyline2 = this.connectLine(slicedPolyLine_2, slicedClipLine)
    // 闭合线 来构造多边形
    resultPolyline2.geometry.coordinates.push(resultPolyline2.geometry.coordinates[0])
    const resultPolygon2 = turf.lineToPolygon(resultPolyline2)
    // 返回面要素集
    return turf.featureCollection([resultPolygon1, resultPolygon2])
  },
 
  _multiPolygonClip: function (polyLine, clipLine) {
    // 将环 多边形分割成 内部逆时针多边形+外部多边形
    let outPolyline
    const insidePolylineList = []
    for (let i = 0; i < polyLine.geometry.coordinates.length; i++) {
      const splitPolyline = turf.lineString(polyLine.geometry.coordinates[i])
      if (turf.booleanClockwise(splitPolyline)) {
        if (outPolyline) {
          throw new Error({ state: "裁剪失败", message: "出现了两个外部多边形无法处理" })
        } else {
          outPolyline = splitPolyline
        }
      } else {
        const intersects = turf.lineIntersect(splitPolyline, clipLine)
        if (intersects.features.length > 0) {
          throw new Error({ state: "裁剪失败", message: "切割线不能与内环有交点" })
        }
        insidePolylineList.push(splitPolyline)
      }
    }
    const resultCollection = this._singlePolygonClip(outPolyline, clipLine)
 
    for (let i = 0; i < resultCollection.features.length; i++) {
      for (let j = 0; j < insidePolylineList.length; j++) {
        const startPoint = turf.point(insidePolylineList[j].geometry.coordinates[0])
        if (turf.booleanPointInPolygon(startPoint, resultCollection.features[i])) {
          resultCollection.features[i] = turf.mask(resultCollection.features[i], turf.lineToPolygon(insidePolylineList[j]))
        }
      }
    }
    return resultCollection
  },
 
  /**
   * 连接两条线
   * 方法会将两条线段最近的一段直接连接
   */
  connectLine: function (line1, line2) {
    const line2_length = line2.geometry.coordinates.length
    const line1_startPoint = line1.geometry.coordinates[0]
    const line2_startPoint = line2.geometry.coordinates[0]
    const line2_endPoint = line2.geometry.coordinates[line2_length - 1]
    const pointList = []
    // 获取line1 所有点坐标
    for (let i = 0; i < line1.geometry.coordinates.length; i++) {
      const coordinate = line1.geometry.coordinates[i]
      pointList.push(coordinate)
    }
 
    // 判断两条线的 起点是否接近,如果接近 逆转line2线 进行连接
    if (turf.distance(line1_startPoint, line2_startPoint) < turf.distance(line1_startPoint, line2_endPoint)) {
      line2.geometry.coordinates = line2.geometry.coordinates.reverse()
    }
    for (let i = 0; i < line2.geometry.coordinates.length; i++) {
      const coordinate = line2.geometry.coordinates[i]
      pointList.push(coordinate)
    }
    return turf.lineString(pointList)
  },
 
  /**
   * 判断点是否在线里面
   * 注:线组成的坐标对比
   */
  isOnLine: function (point, line) {
    for (let i = 0; i < line.geometry.coordinates.length; i++) {
      const coordinate = line.geometry.coordinates[i]
      if (point.geometry.coordinates[0] === coordinate[0] && point.geometry.coordinates[1] === coordinate[1]) {
        return true
      }
    }
    return false
  },
 
  /**
   * 获得两条线交点
   */
  getIntersectPoints: function (line1, line2) {
    return turf.lineIntersect(line1, line2)
  },
 
  // multiPolygon转polygons,不涉及属性
  multiPolygon2polygons: function (multiPolygon) {
    if (multiPolygon.geometry.type !== "MultiPolygon") {
      return
    }
    const polygons = []
    multiPolygon.geometry.coordinates.forEach((item) => {
      const polygon = {
        type: "Feature",
        properties: {},
        geometry: {
          type: "Polygon",
          coordinates: []
        }
      }
      polygon.geometry.coordinates = item
      polygons.push(polygon)
    })
    return polygons
  },
 
  /**
   * polygons转multiPolygon,不涉及属性,只输出属性为{}
   * 考虑polygons中就存在多面的情况
   */
  polygons2MultiPolygon: function (geoJson) {
    const newGeoJson = {
      type: "FeatureCollection",
      features: [{ geometry: { coordinates: [], type: "MultiPolygon" }, type: "Feature", properties: {} }]
    }
    geoJson.features.forEach((item) => {
      if (item.geometry.type === "Polygon") {
        newGeoJson.features[0].geometry.coordinates.push(item.geometry.coordinates)
      } else {
        item.geometry.coordinates.forEach((item) => {
          newGeoJson.features[0].geometry.coordinates.push(item)
        })
      }
    })
    return newGeoJson
  }
}