Distance3DOp.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import LineString from '../../geom/LineString'
  2. import Geometry from '../../geom/Geometry'
  3. import Coordinate from '../../geom/Coordinate'
  4. import IllegalArgumentException from '../../../../../java/lang/IllegalArgumentException'
  5. import Point from '../../geom/Point'
  6. import PlanarPolygon3D from './PlanarPolygon3D'
  7. import Polygon from '../../geom/Polygon'
  8. import GeometryLocation from '../distance/GeometryLocation'
  9. import Double from '../../../../../java/lang/Double'
  10. import LineSegment from '../../geom/LineSegment'
  11. import GeometryCollection from '../../geom/GeometryCollection'
  12. import CGAlgorithms3D from '../../algorithm/CGAlgorithms3D'
  13. export default class Distance3DOp {
  14. constructor() {
  15. Distance3DOp.constructor_.apply(this, arguments)
  16. }
  17. static constructor_() {
  18. this._geom = null
  19. this._terminateDistance = 0.0
  20. this._minDistanceLocation = null
  21. this._minDistance = Double.MAX_VALUE
  22. this._isDone = false
  23. if (arguments.length === 2) {
  24. const g0 = arguments[0], g1 = arguments[1]
  25. Distance3DOp.constructor_.call(this, g0, g1, 0.0)
  26. } else if (arguments.length === 3) {
  27. const g0 = arguments[0], g1 = arguments[1], terminateDistance = arguments[2]
  28. this._geom = new Array(2).fill(null)
  29. this._geom[0] = g0
  30. this._geom[1] = g1
  31. this._terminateDistance = terminateDistance
  32. }
  33. }
  34. static segmentPoint(p0, p1, d0, d1) {
  35. if (d0 <= 0) return new Coordinate(p0)
  36. if (d1 <= 0) return new Coordinate(p1)
  37. const f = Math.abs(d0) / (Math.abs(d0) + Math.abs(d1))
  38. const intx = p0.x + f * (p1.x - p0.x)
  39. const inty = p0.y + f * (p1.y - p0.y)
  40. const intz = p0.getZ() + f * (p1.getZ() - p0.getZ())
  41. return new Coordinate(intx, inty, intz)
  42. }
  43. static nearestPoints(g0, g1) {
  44. const distOp = new Distance3DOp(g0, g1)
  45. return distOp.nearestPoints()
  46. }
  47. static polyPlane(poly) {
  48. return new PlanarPolygon3D(poly)
  49. }
  50. static isWithinDistance(g0, g1, distance) {
  51. const distOp = new Distance3DOp(g0, g1, distance)
  52. return distOp.distance() <= distance
  53. }
  54. static distance(g0, g1) {
  55. const distOp = new Distance3DOp(g0, g1)
  56. return distOp.distance()
  57. }
  58. computeMinDistancePolygonPoint(polyPlane, point, flip) {
  59. const pt = point.getCoordinate()
  60. const shell = polyPlane.getPolygon().getExteriorRing()
  61. if (polyPlane.intersects(pt, shell)) {
  62. const nHole = polyPlane.getPolygon().getNumInteriorRing()
  63. for (let i = 0; i < nHole; i++) {
  64. const hole = polyPlane.getPolygon().getInteriorRingN(i)
  65. if (polyPlane.intersects(pt, hole)) {
  66. this.computeMinDistanceLinePoint(hole, point, flip)
  67. return null
  68. }
  69. }
  70. const dist = Math.abs(polyPlane.getPlane().orientedDistance(pt))
  71. this.updateDistance(dist, new GeometryLocation(polyPlane.getPolygon(), 0, pt), new GeometryLocation(point, 0, pt), flip)
  72. }
  73. this.computeMinDistanceLinePoint(shell, point, flip)
  74. }
  75. intersection(poly, line) {
  76. const seq = line.getCoordinateSequence()
  77. if (seq.size() === 0) return null
  78. const p0 = new Coordinate()
  79. seq.getCoordinate(0, p0)
  80. let d0 = poly.getPlane().orientedDistance(p0)
  81. const p1 = new Coordinate()
  82. for (let i = 0; i < seq.size() - 1; i++) {
  83. seq.getCoordinate(i, p0)
  84. seq.getCoordinate(i + 1, p1)
  85. const d1 = poly.getPlane().orientedDistance(p1)
  86. if (d0 * d1 > 0) continue
  87. const intPt = Distance3DOp.segmentPoint(p0, p1, d0, d1)
  88. if (poly.intersects(intPt))
  89. return intPt
  90. d0 = d1
  91. }
  92. return null
  93. }
  94. computeMinDistancePolygonPolygon(poly0, poly1, flip) {
  95. this.computeMinDistancePolygonRings(poly0, poly1, flip)
  96. if (this._isDone) return null
  97. const polyPlane1 = new PlanarPolygon3D(poly1)
  98. this.computeMinDistancePolygonRings(polyPlane1, poly0.getPolygon(), flip)
  99. }
  100. computeMinDistancePointPoint(point0, point1, flip) {
  101. const dist = CGAlgorithms3D.distance(point0.getCoordinate(), point1.getCoordinate())
  102. if (dist < this._minDistance)
  103. this.updateDistance(dist, new GeometryLocation(point0, 0, point0.getCoordinate()), new GeometryLocation(point1, 0, point1.getCoordinate()), flip)
  104. }
  105. computeMinDistanceMultiMulti(g0, g1, flip) {
  106. if (g0 instanceof GeometryCollection) {
  107. const n = g0.getNumGeometries()
  108. for (let i = 0; i < n; i++) {
  109. const g = g0.getGeometryN(i)
  110. this.computeMinDistanceMultiMulti(g, g1, flip)
  111. if (this._isDone) return null
  112. }
  113. } else {
  114. if (g0.isEmpty()) return null
  115. if (g0 instanceof Polygon)
  116. this.computeMinDistanceOneMulti(Distance3DOp.polyPlane(g0), g1, flip)
  117. else this.computeMinDistanceOneMulti(g0, g1, flip)
  118. }
  119. }
  120. computeMinDistanceOneMulti() {
  121. if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && arguments[1] instanceof Geometry)) {
  122. const g0 = arguments[0], g1 = arguments[1], flip = arguments[2]
  123. if (g1 instanceof GeometryCollection) {
  124. const n = g1.getNumGeometries()
  125. for (let i = 0; i < n; i++) {
  126. const g = g1.getGeometryN(i)
  127. this.computeMinDistanceOneMulti(g0, g, flip)
  128. if (this._isDone) return null
  129. }
  130. } else {
  131. this.computeMinDistance(g0, g1, flip)
  132. }
  133. } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof PlanarPolygon3D && arguments[1] instanceof Geometry)) {
  134. const poly = arguments[0], geom = arguments[1], flip = arguments[2]
  135. if (geom instanceof GeometryCollection) {
  136. const n = geom.getNumGeometries()
  137. for (let i = 0; i < n; i++) {
  138. const g = geom.getGeometryN(i)
  139. this.computeMinDistanceOneMulti(poly, g, flip)
  140. if (this._isDone) return null
  141. }
  142. } else {
  143. if (geom instanceof Point) {
  144. this.computeMinDistancePolygonPoint(poly, geom, flip)
  145. return null
  146. }
  147. if (geom instanceof LineString) {
  148. this.computeMinDistancePolygonLine(poly, geom, flip)
  149. return null
  150. }
  151. if (geom instanceof Polygon) {
  152. this.computeMinDistancePolygonPolygon(poly, geom, flip)
  153. return null
  154. }
  155. }
  156. }
  157. }
  158. computeMinDistanceLinePoint(line, point, flip) {
  159. const lineCoord = line.getCoordinates()
  160. const coord = point.getCoordinate()
  161. for (let i = 0; i < lineCoord.length - 1; i++) {
  162. const dist = CGAlgorithms3D.distancePointSegment(coord, lineCoord[i], lineCoord[i + 1])
  163. if (dist < this._minDistance) {
  164. const seg = new LineSegment(lineCoord[i], lineCoord[i + 1])
  165. const segClosestPoint = seg.closestPoint(coord)
  166. this.updateDistance(dist, new GeometryLocation(line, i, segClosestPoint), new GeometryLocation(point, 0, coord), flip)
  167. }
  168. if (this._isDone) return null
  169. }
  170. }
  171. nearestLocations() {
  172. this.computeMinDistance()
  173. return this._minDistanceLocation
  174. }
  175. nearestPoints() {
  176. this.computeMinDistance()
  177. const nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()]
  178. return nearestPts
  179. }
  180. computeMinDistance() {
  181. if (arguments.length === 0) {
  182. if (this._minDistanceLocation !== null) return null
  183. this._minDistanceLocation = new Array(2).fill(null)
  184. const geomIndex = this.mostPolygonalIndex()
  185. const flip = geomIndex === 1
  186. this.computeMinDistanceMultiMulti(this._geom[geomIndex], this._geom[1 - geomIndex], flip)
  187. } else if (arguments.length === 3) {
  188. const g0 = arguments[0], g1 = arguments[1], flip = arguments[2]
  189. if (g0 instanceof Point) {
  190. if (g1 instanceof Point) {
  191. this.computeMinDistancePointPoint(g0, g1, flip)
  192. return null
  193. }
  194. if (g1 instanceof LineString) {
  195. this.computeMinDistanceLinePoint(g1, g0, !flip)
  196. return null
  197. }
  198. if (g1 instanceof Polygon) {
  199. this.computeMinDistancePolygonPoint(Distance3DOp.polyPlane(g1), g0, !flip)
  200. return null
  201. }
  202. }
  203. if (g0 instanceof LineString) {
  204. if (g1 instanceof Point) {
  205. this.computeMinDistanceLinePoint(g0, g1, flip)
  206. return null
  207. }
  208. if (g1 instanceof LineString) {
  209. this.computeMinDistanceLineLine(g0, g1, flip)
  210. return null
  211. }
  212. if (g1 instanceof Polygon) {
  213. this.computeMinDistancePolygonLine(Distance3DOp.polyPlane(g1), g0, !flip)
  214. return null
  215. }
  216. }
  217. if (g0 instanceof Polygon) {
  218. if (g1 instanceof Point) {
  219. this.computeMinDistancePolygonPoint(Distance3DOp.polyPlane(g0), g1, flip)
  220. return null
  221. }
  222. if (g1 instanceof LineString) {
  223. this.computeMinDistancePolygonLine(Distance3DOp.polyPlane(g0), g1, flip)
  224. return null
  225. }
  226. if (g1 instanceof Polygon) {
  227. this.computeMinDistancePolygonPolygon(Distance3DOp.polyPlane(g0), g1, flip)
  228. return null
  229. }
  230. }
  231. }
  232. }
  233. computeMinDistanceLineLine(line0, line1, flip) {
  234. const coord0 = line0.getCoordinates()
  235. const coord1 = line1.getCoordinates()
  236. for (let i = 0; i < coord0.length - 1; i++)
  237. for (let j = 0; j < coord1.length - 1; j++) {
  238. const dist = CGAlgorithms3D.distanceSegmentSegment(coord0[i], coord0[i + 1], coord1[j], coord1[j + 1])
  239. if (dist < this._minDistance) {
  240. this._minDistance = dist
  241. const seg0 = new LineSegment(coord0[i], coord0[i + 1])
  242. const seg1 = new LineSegment(coord1[j], coord1[j + 1])
  243. const closestPt = seg0.closestPoints(seg1)
  244. this.updateDistance(dist, new GeometryLocation(line0, i, closestPt[0]), new GeometryLocation(line1, j, closestPt[1]), flip)
  245. }
  246. if (this._isDone) return null
  247. }
  248. }
  249. computeMinDistancePolygonLine(poly, line, flip) {
  250. const intPt = this.intersection(poly, line)
  251. if (intPt !== null) {
  252. this.updateDistance(0, new GeometryLocation(poly.getPolygon(), 0, intPt), new GeometryLocation(line, 0, intPt), flip)
  253. return null
  254. }
  255. this.computeMinDistanceLineLine(poly.getPolygon().getExteriorRing(), line, flip)
  256. if (this._isDone) return null
  257. const nHole = poly.getPolygon().getNumInteriorRing()
  258. for (let i = 0; i < nHole; i++) {
  259. this.computeMinDistanceLineLine(poly.getPolygon().getInteriorRingN(i), line, flip)
  260. if (this._isDone) return null
  261. }
  262. }
  263. distance() {
  264. if (this._geom[0] === null || this._geom[1] === null) throw new IllegalArgumentException('null geometries are not supported')
  265. if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) return 0.0
  266. this.computeMinDistance()
  267. return this._minDistance
  268. }
  269. mostPolygonalIndex() {
  270. const dim0 = this._geom[0].getDimension()
  271. const dim1 = this._geom[1].getDimension()
  272. if (dim0 >= 2 && dim1 >= 2) {
  273. if (this._geom[0].getNumPoints() > this._geom[1].getNumPoints()) return 0
  274. return 1
  275. }
  276. if (dim0 >= 2) return 0
  277. if (dim1 >= 2) return 1
  278. return 0
  279. }
  280. computeMinDistancePolygonRings(poly, ringPoly, flip) {
  281. this.computeMinDistancePolygonLine(poly, ringPoly.getExteriorRing(), flip)
  282. if (this._isDone) return null
  283. const nHole = ringPoly.getNumInteriorRing()
  284. for (let i = 0; i < nHole; i++) {
  285. this.computeMinDistancePolygonLine(poly, ringPoly.getInteriorRingN(i), flip)
  286. if (this._isDone) return null
  287. }
  288. }
  289. updateDistance(dist, loc0, loc1, flip) {
  290. this._minDistance = dist
  291. const index = flip ? 1 : 0
  292. this._minDistanceLocation[index] = loc0
  293. this._minDistanceLocation[1 - index] = loc1
  294. if (this._minDistance < this._terminateDistance) this._isDone = true
  295. }
  296. }