OverlayOp.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import PointLocator from '../../algorithm/PointLocator'
  2. import Location from '../../geom/Location'
  3. import EdgeNodingValidator from '../../geomgraph/EdgeNodingValidator'
  4. import GeometryCollectionMapper from '../../geom/util/GeometryCollectionMapper'
  5. import PolygonBuilder from './PolygonBuilder'
  6. import Position from '../../geomgraph/Position'
  7. import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException'
  8. import LineBuilder from './LineBuilder'
  9. import PointBuilder from './PointBuilder'
  10. import SnapIfNeededOverlayOp from './snap/SnapIfNeededOverlayOp'
  11. import Label from '../../geomgraph/Label'
  12. import OverlayNodeFactory from './OverlayNodeFactory'
  13. import GeometryGraphOperation from '../GeometryGraphOperation'
  14. import EdgeList from '../../geomgraph/EdgeList'
  15. import ArrayList from '../../../../../java/util/ArrayList'
  16. import Assert from '../../util/Assert'
  17. import PlanarGraph from '../../geomgraph/PlanarGraph'
  18. export default class OverlayOp extends GeometryGraphOperation {
  19. constructor() {
  20. super()
  21. OverlayOp.constructor_.apply(this, arguments)
  22. }
  23. static constructor_() {
  24. this._ptLocator = new PointLocator()
  25. this._geomFact = null
  26. this._resultGeom = null
  27. this._graph = null
  28. this._edgeList = new EdgeList()
  29. this._resultPolyList = new ArrayList()
  30. this._resultLineList = new ArrayList()
  31. this._resultPointList = new ArrayList()
  32. const g0 = arguments[0], g1 = arguments[1]
  33. GeometryGraphOperation.constructor_.call(this, g0, g1)
  34. this._graph = new PlanarGraph(new OverlayNodeFactory())
  35. this._geomFact = g0.getFactory()
  36. }
  37. static overlayOp(geom0, geom1, opCode) {
  38. const gov = new OverlayOp(geom0, geom1)
  39. const geomOv = gov.getResultGeometry(opCode)
  40. return geomOv
  41. }
  42. static union(geom, other) {
  43. if (geom.isEmpty() || other.isEmpty()) {
  44. if (geom.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.UNION, geom, other, geom.getFactory())
  45. if (geom.isEmpty()) return other.copy()
  46. if (other.isEmpty()) return geom.copy()
  47. }
  48. if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
  49. return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.UNION)
  50. }
  51. static intersection(geom, other) {
  52. if (geom.isEmpty() || other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, geom, other, geom.getFactory())
  53. if (geom.isGeometryCollection()) {
  54. const g2 = other
  55. return GeometryCollectionMapper.map(geom, new (class {
  56. get interfaces_() {
  57. return [MapOp]
  58. }
  59. map(g) {
  60. return OverlayOp.intersection(g, g2)
  61. }
  62. })())
  63. }
  64. return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.INTERSECTION)
  65. }
  66. static symDifference(geom, other) {
  67. if (geom.isEmpty() || other.isEmpty()) {
  68. if (geom.isEmpty() && other.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, geom, other, geom.getFactory())
  69. if (geom.isEmpty()) return other.copy()
  70. if (other.isEmpty()) return geom.copy()
  71. }
  72. if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
  73. return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.SYMDIFFERENCE)
  74. }
  75. static resultDimension(opCode, g0, g1) {
  76. const dim0 = g0.getDimension()
  77. const dim1 = g1.getDimension()
  78. let resultDimension = -1
  79. switch (opCode) {
  80. case OverlayOp.INTERSECTION:
  81. resultDimension = Math.min(dim0, dim1)
  82. break
  83. case OverlayOp.UNION:
  84. resultDimension = Math.max(dim0, dim1)
  85. break
  86. case OverlayOp.DIFFERENCE:
  87. resultDimension = dim0
  88. break
  89. case OverlayOp.SYMDIFFERENCE:
  90. resultDimension = Math.max(dim0, dim1)
  91. break
  92. }
  93. return resultDimension
  94. }
  95. static createEmptyResult(overlayOpCode, a, b, geomFact) {
  96. let result = null
  97. const resultDim = OverlayOp.resultDimension(overlayOpCode, a, b)
  98. return result = geomFact.createEmpty(resultDim)
  99. }
  100. static difference(geom, other) {
  101. if (geom.isEmpty()) return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, geom, other, geom.getFactory())
  102. if (other.isEmpty()) return geom.copy()
  103. if (geom.isGeometryCollection() || other.isGeometryCollection()) throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
  104. return SnapIfNeededOverlayOp.overlayOp(geom, other, OverlayOp.DIFFERENCE)
  105. }
  106. static isResultOfOp() {
  107. if (arguments.length === 2) {
  108. const label = arguments[0], opCode = arguments[1]
  109. const loc0 = label.getLocation(0)
  110. const loc1 = label.getLocation(1)
  111. return OverlayOp.isResultOfOp(loc0, loc1, opCode)
  112. } else if (arguments.length === 3) {
  113. let loc0 = arguments[0], loc1 = arguments[1], overlayOpCode = arguments[2]
  114. if (loc0 === Location.BOUNDARY) loc0 = Location.INTERIOR
  115. if (loc1 === Location.BOUNDARY) loc1 = Location.INTERIOR
  116. switch (overlayOpCode) {
  117. case OverlayOp.INTERSECTION:
  118. return loc0 === Location.INTERIOR && loc1 === Location.INTERIOR
  119. case OverlayOp.UNION:
  120. return loc0 === Location.INTERIOR || loc1 === Location.INTERIOR
  121. case OverlayOp.DIFFERENCE:
  122. return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR
  123. case OverlayOp.SYMDIFFERENCE:
  124. return loc0 === Location.INTERIOR && loc1 !== Location.INTERIOR || loc0 !== Location.INTERIOR && loc1 === Location.INTERIOR
  125. }
  126. return false
  127. }
  128. }
  129. insertUniqueEdge(e) {
  130. const existingEdge = this._edgeList.findEqualEdge(e)
  131. if (existingEdge !== null) {
  132. const existingLabel = existingEdge.getLabel()
  133. let labelToMerge = e.getLabel()
  134. if (!existingEdge.isPointwiseEqual(e)) {
  135. labelToMerge = new Label(e.getLabel())
  136. labelToMerge.flip()
  137. }
  138. const depth = existingEdge.getDepth()
  139. if (depth.isNull())
  140. depth.add(existingLabel)
  141. depth.add(labelToMerge)
  142. existingLabel.merge(labelToMerge)
  143. } else {
  144. this._edgeList.add(e)
  145. }
  146. }
  147. getGraph() {
  148. return this._graph
  149. }
  150. cancelDuplicateResultEdges() {
  151. for (let it = this._graph.getEdgeEnds().iterator(); it.hasNext(); ) {
  152. const de = it.next()
  153. const sym = de.getSym()
  154. if (de.isInResult() && sym.isInResult()) {
  155. de.setInResult(false)
  156. sym.setInResult(false)
  157. }
  158. }
  159. }
  160. isCoveredByLA(coord) {
  161. if (this.isCovered(coord, this._resultLineList)) return true
  162. if (this.isCovered(coord, this._resultPolyList)) return true
  163. return false
  164. }
  165. computeGeometry(resultPointList, resultLineList, resultPolyList, opcode) {
  166. const geomList = new ArrayList()
  167. geomList.addAll(resultPointList)
  168. geomList.addAll(resultLineList)
  169. geomList.addAll(resultPolyList)
  170. if (geomList.isEmpty()) return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact)
  171. return this._geomFact.buildGeometry(geomList)
  172. }
  173. mergeSymLabels() {
  174. for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext(); ) {
  175. const node = nodeit.next()
  176. node.getEdges().mergeSymLabels()
  177. }
  178. }
  179. isCovered(coord, geomList) {
  180. for (let it = geomList.iterator(); it.hasNext(); ) {
  181. const geom = it.next()
  182. const loc = this._ptLocator.locate(coord, geom)
  183. if (loc !== Location.EXTERIOR) return true
  184. }
  185. return false
  186. }
  187. replaceCollapsedEdges() {
  188. const newEdges = new ArrayList()
  189. for (let it = this._edgeList.iterator(); it.hasNext(); ) {
  190. const e = it.next()
  191. if (e.isCollapsed()) {
  192. it.remove()
  193. newEdges.add(e.getCollapsedEdge())
  194. }
  195. }
  196. this._edgeList.addAll(newEdges)
  197. }
  198. updateNodeLabelling() {
  199. for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext(); ) {
  200. const node = nodeit.next()
  201. const lbl = node.getEdges().getLabel()
  202. node.getLabel().merge(lbl)
  203. }
  204. }
  205. getResultGeometry(overlayOpCode) {
  206. this.computeOverlay(overlayOpCode)
  207. return this._resultGeom
  208. }
  209. insertUniqueEdges(edges) {
  210. for (let i = edges.iterator(); i.hasNext(); ) {
  211. const e = i.next()
  212. this.insertUniqueEdge(e)
  213. }
  214. }
  215. computeOverlay(opCode) {
  216. this.copyPoints(0)
  217. this.copyPoints(1)
  218. this._arg[0].computeSelfNodes(this._li, false)
  219. this._arg[1].computeSelfNodes(this._li, false)
  220. this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true)
  221. const baseSplitEdges = new ArrayList()
  222. this._arg[0].computeSplitEdges(baseSplitEdges)
  223. this._arg[1].computeSplitEdges(baseSplitEdges)
  224. const splitEdges = baseSplitEdges
  225. this.insertUniqueEdges(baseSplitEdges)
  226. this.computeLabelsFromDepths()
  227. this.replaceCollapsedEdges()
  228. EdgeNodingValidator.checkValid(this._edgeList.getEdges())
  229. this._graph.addEdges(this._edgeList.getEdges())
  230. this.computeLabelling()
  231. this.labelIncompleteNodes()
  232. this.findResultAreaEdges(opCode)
  233. this.cancelDuplicateResultEdges()
  234. const polyBuilder = new PolygonBuilder(this._geomFact)
  235. polyBuilder.add(this._graph)
  236. this._resultPolyList = polyBuilder.getPolygons()
  237. const lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator)
  238. this._resultLineList = lineBuilder.build(opCode)
  239. const pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator)
  240. this._resultPointList = pointBuilder.build(opCode)
  241. this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode)
  242. }
  243. labelIncompleteNode(n, targetIndex) {
  244. const loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry())
  245. n.getLabel().setLocation(targetIndex, loc)
  246. }
  247. copyPoints(argIndex) {
  248. for (let i = this._arg[argIndex].getNodeIterator(); i.hasNext(); ) {
  249. const graphNode = i.next()
  250. const newNode = this._graph.addNode(graphNode.getCoordinate())
  251. newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex))
  252. }
  253. }
  254. findResultAreaEdges(opCode) {
  255. for (let it = this._graph.getEdgeEnds().iterator(); it.hasNext(); ) {
  256. const de = it.next()
  257. const label = de.getLabel()
  258. if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode))
  259. de.setInResult(true)
  260. }
  261. }
  262. computeLabelsFromDepths() {
  263. for (let it = this._edgeList.iterator(); it.hasNext(); ) {
  264. const e = it.next()
  265. const lbl = e.getLabel()
  266. const depth = e.getDepth()
  267. if (!depth.isNull()) {
  268. depth.normalize()
  269. for (let i = 0; i < 2; i++)
  270. if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i))
  271. if (depth.getDelta(i) === 0) {
  272. lbl.toLine(i)
  273. } else {
  274. Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized')
  275. lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT))
  276. Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized')
  277. lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT))
  278. }
  279. }
  280. }
  281. }
  282. computeLabelling() {
  283. for (let nodeit = this._graph.getNodes().iterator(); nodeit.hasNext(); ) {
  284. const node = nodeit.next()
  285. node.getEdges().computeLabelling(this._arg)
  286. }
  287. this.mergeSymLabels()
  288. this.updateNodeLabelling()
  289. }
  290. labelIncompleteNodes() {
  291. for (let ni = this._graph.getNodes().iterator(); ni.hasNext(); ) {
  292. const n = ni.next()
  293. const label = n.getLabel()
  294. if (n.isIsolated())
  295. if (label.isNull(0)) this.labelIncompleteNode(n, 0); else this.labelIncompleteNode(n, 1)
  296. n.getEdges().updateLabelling(label)
  297. }
  298. }
  299. isCoveredByA(coord) {
  300. if (this.isCovered(coord, this._resultPolyList)) return true
  301. return false
  302. }
  303. }
  304. OverlayOp.INTERSECTION = 1
  305. OverlayOp.UNION = 2
  306. OverlayOp.DIFFERENCE = 3
  307. OverlayOp.SYMDIFFERENCE = 4