Polygon.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import Area from '../algorithm/Area'
  2. import Geometry from './Geometry'
  3. import Arrays from '../../../../java/util/Arrays'
  4. import CoordinateFilter from './CoordinateFilter'
  5. import hasInterface from '../../../../hasInterface'
  6. import IllegalArgumentException from '../../../../java/lang/IllegalArgumentException'
  7. import Orientation from '../algorithm/Orientation'
  8. import CoordinateSequences from './CoordinateSequences'
  9. import GeometryComponentFilter from './GeometryComponentFilter'
  10. import Polygonal from './Polygonal'
  11. import GeometryFilter from './GeometryFilter'
  12. import CoordinateSequenceFilter from './CoordinateSequenceFilter'
  13. export default class Polygon extends Geometry {
  14. constructor() {
  15. super()
  16. Polygon.constructor_.apply(this, arguments)
  17. }
  18. static constructor_() {
  19. this._shell = null
  20. this._holes = null
  21. let shell = arguments[0], holes = arguments[1], factory = arguments[2]
  22. Geometry.constructor_.call(this, factory)
  23. if (shell === null)
  24. shell = this.getFactory().createLinearRing()
  25. if (holes === null)
  26. holes = []
  27. if (Geometry.hasNullElements(holes))
  28. throw new IllegalArgumentException('holes must not contain null elements')
  29. if (shell.isEmpty() && Geometry.hasNonEmptyElements(holes))
  30. throw new IllegalArgumentException('shell is empty but holes are not')
  31. this._shell = shell
  32. this._holes = holes
  33. }
  34. computeEnvelopeInternal() {
  35. return this._shell.getEnvelopeInternal()
  36. }
  37. getCoordinates() {
  38. if (this.isEmpty())
  39. return []
  40. const coordinates = new Array(this.getNumPoints()).fill(null)
  41. let k = -1
  42. const shellCoordinates = this._shell.getCoordinates()
  43. for (let x = 0; x < shellCoordinates.length; x++) {
  44. k++
  45. coordinates[k] = shellCoordinates[x]
  46. }
  47. for (let i = 0; i < this._holes.length; i++) {
  48. const childCoordinates = this._holes[i].getCoordinates()
  49. for (let j = 0; j < childCoordinates.length; j++) {
  50. k++
  51. coordinates[k] = childCoordinates[j]
  52. }
  53. }
  54. return coordinates
  55. }
  56. getArea() {
  57. let area = 0.0
  58. area += Area.ofRing(this._shell.getCoordinateSequence())
  59. for (let i = 0; i < this._holes.length; i++)
  60. area -= Area.ofRing(this._holes[i].getCoordinateSequence())
  61. return area
  62. }
  63. copyInternal() {
  64. const shellCopy = this._shell.copy()
  65. const holeCopies = new Array(this._holes.length).fill(null)
  66. for (let i = 0; i < this._holes.length; i++)
  67. holeCopies[i] = this._holes[i].copy()
  68. return new Polygon(shellCopy, holeCopies, this._factory)
  69. }
  70. isRectangle() {
  71. if (this.getNumInteriorRing() !== 0) return false
  72. if (this._shell === null) return false
  73. if (this._shell.getNumPoints() !== 5) return false
  74. const seq = this._shell.getCoordinateSequence()
  75. const env = this.getEnvelopeInternal()
  76. for (let i = 0; i < 5; i++) {
  77. const x = seq.getX(i)
  78. if (!(x === env.getMinX() || x === env.getMaxX())) return false
  79. const y = seq.getY(i)
  80. if (!(y === env.getMinY() || y === env.getMaxY())) return false
  81. }
  82. let prevX = seq.getX(0)
  83. let prevY = seq.getY(0)
  84. for (let i = 1; i <= 4; i++) {
  85. const x = seq.getX(i)
  86. const y = seq.getY(i)
  87. const xChanged = x !== prevX
  88. const yChanged = y !== prevY
  89. if (xChanged === yChanged) return false
  90. prevX = x
  91. prevY = y
  92. }
  93. return true
  94. }
  95. equalsExact() {
  96. if (arguments.length === 2 && (typeof arguments[1] === 'number' && arguments[0] instanceof Geometry)) {
  97. const other = arguments[0], tolerance = arguments[1]
  98. if (!this.isEquivalentClass(other))
  99. return false
  100. const otherPolygon = other
  101. const thisShell = this._shell
  102. const otherPolygonShell = otherPolygon._shell
  103. if (!thisShell.equalsExact(otherPolygonShell, tolerance))
  104. return false
  105. if (this._holes.length !== otherPolygon._holes.length)
  106. return false
  107. for (let i = 0; i < this._holes.length; i++)
  108. if (!this._holes[i].equalsExact(otherPolygon._holes[i], tolerance))
  109. return false
  110. return true
  111. } else {
  112. return super.equalsExact.apply(this, arguments)
  113. }
  114. }
  115. normalize() {
  116. if (arguments.length === 0) {
  117. this._shell = this.normalized(this._shell, true)
  118. for (let i = 0; i < this._holes.length; i++)
  119. this._holes[i] = this.normalized(this._holes[i], false)
  120. Arrays.sort(this._holes)
  121. } else if (arguments.length === 2) {
  122. const ring = arguments[0], clockwise = arguments[1]
  123. if (ring.isEmpty())
  124. return null
  125. const seq = ring.getCoordinateSequence()
  126. const minCoordinateIndex = CoordinateSequences.minCoordinateIndex(seq, 0, seq.size() - 2)
  127. CoordinateSequences.scroll(seq, minCoordinateIndex, true)
  128. if (Orientation.isCCW(seq) === clockwise) CoordinateSequences.reverse(seq)
  129. }
  130. }
  131. getCoordinate() {
  132. return this._shell.getCoordinate()
  133. }
  134. getNumInteriorRing() {
  135. return this._holes.length
  136. }
  137. getBoundaryDimension() {
  138. return 1
  139. }
  140. reverseInternal() {
  141. const shell = this.getExteriorRing().reverse()
  142. const holes = new Array(this.getNumInteriorRing()).fill(null)
  143. for (let i = 0; i < holes.length; i++)
  144. holes[i] = this.getInteriorRingN(i).reverse()
  145. return this.getFactory().createPolygon(shell, holes)
  146. }
  147. getTypeCode() {
  148. return Geometry.TYPECODE_POLYGON
  149. }
  150. getDimension() {
  151. return 2
  152. }
  153. getLength() {
  154. let len = 0.0
  155. len += this._shell.getLength()
  156. for (let i = 0; i < this._holes.length; i++)
  157. len += this._holes[i].getLength()
  158. return len
  159. }
  160. getNumPoints() {
  161. let numPoints = this._shell.getNumPoints()
  162. for (let i = 0; i < this._holes.length; i++)
  163. numPoints += this._holes[i].getNumPoints()
  164. return numPoints
  165. }
  166. convexHull() {
  167. return this.getExteriorRing().convexHull()
  168. }
  169. normalized(ring, clockwise) {
  170. const res = ring.copy()
  171. this.normalize(res, clockwise)
  172. return res
  173. }
  174. compareToSameClass() {
  175. if (arguments.length === 1) {
  176. const o = arguments[0]
  177. const thisShell = this._shell
  178. const otherShell = o._shell
  179. return thisShell.compareToSameClass(otherShell)
  180. } else if (arguments.length === 2) {
  181. const o = arguments[0], comp = arguments[1]
  182. const poly = o
  183. const thisShell = this._shell
  184. const otherShell = poly._shell
  185. const shellComp = thisShell.compareToSameClass(otherShell, comp)
  186. if (shellComp !== 0) return shellComp
  187. const nHole1 = this.getNumInteriorRing()
  188. const nHole2 = poly.getNumInteriorRing()
  189. let i = 0
  190. while (i < nHole1 && i < nHole2) {
  191. const thisHole = this.getInteriorRingN(i)
  192. const otherHole = poly.getInteriorRingN(i)
  193. const holeComp = thisHole.compareToSameClass(otherHole, comp)
  194. if (holeComp !== 0) return holeComp
  195. i++
  196. }
  197. if (i < nHole1) return 1
  198. if (i < nHole2) return -1
  199. return 0
  200. }
  201. }
  202. apply() {
  203. if (hasInterface(arguments[0], CoordinateFilter)) {
  204. const filter = arguments[0]
  205. this._shell.apply(filter)
  206. for (let i = 0; i < this._holes.length; i++)
  207. this._holes[i].apply(filter)
  208. } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
  209. const filter = arguments[0]
  210. this._shell.apply(filter)
  211. if (!filter.isDone())
  212. for (let i = 0; i < this._holes.length; i++) {
  213. this._holes[i].apply(filter)
  214. if (filter.isDone()) break
  215. }
  216. if (filter.isGeometryChanged()) this.geometryChanged()
  217. } else if (hasInterface(arguments[0], GeometryFilter)) {
  218. const filter = arguments[0]
  219. filter.filter(this)
  220. } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
  221. const filter = arguments[0]
  222. filter.filter(this)
  223. this._shell.apply(filter)
  224. for (let i = 0; i < this._holes.length; i++)
  225. this._holes[i].apply(filter)
  226. }
  227. }
  228. getBoundary() {
  229. if (this.isEmpty())
  230. return this.getFactory().createMultiLineString()
  231. const rings = new Array(this._holes.length + 1).fill(null)
  232. rings[0] = this._shell
  233. for (let i = 0; i < this._holes.length; i++)
  234. rings[i + 1] = this._holes[i]
  235. if (rings.length <= 1) return this.getFactory().createLinearRing(rings[0].getCoordinateSequence())
  236. return this.getFactory().createMultiLineString(rings)
  237. }
  238. getGeometryType() {
  239. return Geometry.TYPENAME_POLYGON
  240. }
  241. getExteriorRing() {
  242. return this._shell
  243. }
  244. isEmpty() {
  245. return this._shell.isEmpty()
  246. }
  247. getInteriorRingN(n) {
  248. return this._holes[n]
  249. }
  250. get interfaces_() {
  251. return [Polygonal]
  252. }
  253. }