HPRtree.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import GeometryFactory from '../../geom/GeometryFactory'
  2. import SpatialIndex from '../SpatialIndex'
  3. import Double from '../../../../../java/lang/Double'
  4. import Integer from '../../../../../java/lang/Integer'
  5. import HilbertEncoder from './HilbertEncoder'
  6. import Collections from '../../../../../java/util/Collections'
  7. import System from '../../../../../java/lang/System'
  8. import ArrayList from '../../../../../java/util/ArrayList'
  9. import Comparator from '../../../../../java/util/Comparator'
  10. import Item from './Item'
  11. import ArrayListVisitor from '../ArrayListVisitor'
  12. import Envelope from '../../geom/Envelope'
  13. export default class HPRtree {
  14. constructor() {
  15. HPRtree.constructor_.apply(this, arguments)
  16. }
  17. static constructor_() {
  18. this._items = new ArrayList()
  19. this._nodeCapacity = HPRtree.DEFAULT_NODE_CAPACITY
  20. this._totalExtent = new Envelope()
  21. this._layerStartIndex = null
  22. this._nodeBounds = null
  23. this._isBuilt = false
  24. if (arguments.length === 0) {
  25. HPRtree.constructor_.call(this, HPRtree.DEFAULT_NODE_CAPACITY)
  26. } else if (arguments.length === 1) {
  27. const nodeCapacity = arguments[0]
  28. this._nodeCapacity = nodeCapacity
  29. }
  30. }
  31. static computeLayerIndices(itemSize, nodeCapacity) {
  32. const layerIndexList = new ArrayList()
  33. let layerSize = itemSize
  34. let index = 0
  35. do {
  36. layerIndexList.add(index)
  37. layerSize = HPRtree.numNodesToCover(layerSize, nodeCapacity)
  38. index += HPRtree.ENV_SIZE * layerSize
  39. } while (layerSize > 1)
  40. return HPRtree.toIntArray(layerIndexList)
  41. }
  42. static createBoundsArray(size) {
  43. const a = new Array(4 * size).fill(null)
  44. for (let i = 0; i < size; i++) {
  45. const index = 4 * i
  46. a[index] = Double.MAX_VALUE
  47. a[index + 1] = Double.MAX_VALUE
  48. a[index + 2] = -Double.MAX_VALUE
  49. a[index + 3] = -Double.MAX_VALUE
  50. }
  51. return a
  52. }
  53. static intersects(env1, env2) {
  54. return !(env2.getMinX() > env1.getMaxX() || env2.getMaxX() < env1.getMinX() || env2.getMinY() > env1.getMaxY() || env2.getMaxY() < env1.getMinY())
  55. }
  56. static dumpItems(items) {
  57. const fact = new GeometryFactory()
  58. for (const item of items) {
  59. const env = item.getEnvelope()
  60. System.out.println(fact.toGeometry(env))
  61. }
  62. }
  63. static numNodesToCover(nChild, nodeCapacity) {
  64. const mult = Math.trunc(nChild / nodeCapacity)
  65. const total = mult * nodeCapacity
  66. if (total === nChild) return mult
  67. return mult + 1
  68. }
  69. static toIntArray(list) {
  70. const array = new Array(list.size()).fill(null)
  71. for (let i = 0; i < array.length; i++)
  72. array[i] = list.get(i)
  73. return array
  74. }
  75. computeLeafNodes(layerSize) {
  76. for (let i = 0; i < layerSize; i += HPRtree.ENV_SIZE)
  77. this.computeLeafNodeBounds(i, Math.trunc(this._nodeCapacity * i / 4))
  78. }
  79. size() {
  80. return this._items.size()
  81. }
  82. insert(itemEnv, item) {
  83. if (this._isBuilt)
  84. throw new IllegalStateException('Cannot insert items after tree is built.')
  85. this._items.add(new Item(itemEnv, item))
  86. this._totalExtent.expandToInclude(itemEnv)
  87. }
  88. intersects(nodeIndex, env) {
  89. const isBeyond = env.getMaxX() < this._nodeBounds[nodeIndex] || env.getMaxY() < this._nodeBounds[nodeIndex + 1] || env.getMinX() > this._nodeBounds[nodeIndex + 2] || env.getMinY() > this._nodeBounds[nodeIndex + 3]
  90. return !isBeyond
  91. }
  92. query() {
  93. if (arguments.length === 1) {
  94. const searchEnv = arguments[0]
  95. this.build()
  96. if (!this._totalExtent.intersects(searchEnv)) return new ArrayList()
  97. const visitor = new ArrayListVisitor()
  98. this.query(searchEnv, visitor)
  99. return visitor.getItems()
  100. } else if (arguments.length === 2) {
  101. const searchEnv = arguments[0], visitor = arguments[1]
  102. this.build()
  103. if (!this._totalExtent.intersects(searchEnv)) return null
  104. if (this._layerStartIndex === null)
  105. this.queryItems(0, searchEnv, visitor)
  106. else
  107. this.queryTopLayer(searchEnv, visitor)
  108. }
  109. }
  110. build() {
  111. if (this._isBuilt) return null
  112. this._isBuilt = true
  113. if (this._items.size() <= this._nodeCapacity) return null
  114. this.sortItems()
  115. this._layerStartIndex = HPRtree.computeLayerIndices(this._items.size(), this._nodeCapacity)
  116. const nodeCount = Math.trunc(this._layerStartIndex[this._layerStartIndex.length - 1] / 4)
  117. this._nodeBounds = HPRtree.createBoundsArray(nodeCount)
  118. this.computeLeafNodes(this._layerStartIndex[1])
  119. for (let i = 1; i < this._layerStartIndex.length - 1; i++)
  120. this.computeLayerNodes(i)
  121. }
  122. getNodeEnvelope(i) {
  123. return new Envelope(this._nodeBounds[i], this._nodeBounds[i + 1], this._nodeBounds[i + 2], this._nodeBounds[i + 3])
  124. }
  125. sortItems() {
  126. const comp = new ItemComparator(new HilbertEncoder(HPRtree.HILBERT_LEVEL, this._totalExtent))
  127. Collections.sort(this._items, comp)
  128. }
  129. queryNode(layerIndex, nodeOffset, searchEnv, visitor) {
  130. const layerStart = this._layerStartIndex[layerIndex]
  131. const nodeIndex = layerStart + nodeOffset
  132. if (!this.intersects(nodeIndex, searchEnv)) return null
  133. if (layerIndex === 0) {
  134. const childNodesOffset = Math.trunc(nodeOffset / HPRtree.ENV_SIZE) * this._nodeCapacity
  135. this.queryItems(childNodesOffset, searchEnv, visitor)
  136. } else {
  137. const childNodesOffset = nodeOffset * this._nodeCapacity
  138. this.queryNodeChildren(layerIndex - 1, childNodesOffset, searchEnv, visitor)
  139. }
  140. }
  141. updateNodeBounds(nodeIndex, minX, minY, maxX, maxY) {
  142. if (minX < this._nodeBounds[nodeIndex]) this._nodeBounds[nodeIndex] = minX
  143. if (minY < this._nodeBounds[nodeIndex + 1]) this._nodeBounds[nodeIndex + 1] = minY
  144. if (maxX > this._nodeBounds[nodeIndex + 2]) this._nodeBounds[nodeIndex + 2] = maxX
  145. if (maxY > this._nodeBounds[nodeIndex + 3]) this._nodeBounds[nodeIndex + 3] = maxY
  146. }
  147. remove(itemEnv, item) {
  148. return false
  149. }
  150. computeNodeBounds(nodeIndex, blockStart, nodeMaxIndex) {
  151. for (let i = 0; i <= this._nodeCapacity; i++) {
  152. const index = blockStart + 4 * i
  153. if (index >= nodeMaxIndex) break
  154. this.updateNodeBounds(nodeIndex, this._nodeBounds[index], this._nodeBounds[index + 1], this._nodeBounds[index + 2], this._nodeBounds[index + 3])
  155. }
  156. }
  157. queryTopLayer(searchEnv, visitor) {
  158. const layerIndex = this._layerStartIndex.length - 2
  159. const layerSize = this.layerSize(layerIndex)
  160. for (let i = 0; i < layerSize; i += HPRtree.ENV_SIZE)
  161. this.queryNode(layerIndex, i, searchEnv, visitor)
  162. }
  163. queryItems(blockStart, searchEnv, visitor) {
  164. for (let i = 0; i < this._nodeCapacity; i++) {
  165. const itemIndex = blockStart + i
  166. if (itemIndex >= this._items.size()) break
  167. const item = this._items.get(itemIndex)
  168. if (HPRtree.intersects(item.getEnvelope(), searchEnv))
  169. visitor.visitItem(item.getItem())
  170. }
  171. }
  172. queryNodeChildren(layerIndex, blockOffset, searchEnv, visitor) {
  173. const layerStart = this._layerStartIndex[layerIndex]
  174. const layerEnd = this._layerStartIndex[layerIndex + 1]
  175. for (let i = 0; i < this._nodeCapacity; i++) {
  176. const nodeOffset = blockOffset + HPRtree.ENV_SIZE * i
  177. if (layerStart + nodeOffset >= layerEnd) break
  178. this.queryNode(layerIndex, nodeOffset, searchEnv, visitor)
  179. }
  180. }
  181. computeLayerNodes(layerIndex) {
  182. const layerStart = this._layerStartIndex[layerIndex]
  183. const childLayerStart = this._layerStartIndex[layerIndex - 1]
  184. const layerSize = this.layerSize(layerIndex)
  185. const childLayerEnd = layerStart
  186. for (let i = 0; i < layerSize; i += HPRtree.ENV_SIZE) {
  187. const childStart = childLayerStart + this._nodeCapacity * i
  188. this.computeNodeBounds(layerStart + i, childStart, childLayerEnd)
  189. }
  190. }
  191. layerSize(layerIndex) {
  192. const layerStart = this._layerStartIndex[layerIndex]
  193. const layerEnd = this._layerStartIndex[layerIndex + 1]
  194. return layerEnd - layerStart
  195. }
  196. computeLeafNodeBounds(nodeIndex, blockStart) {
  197. for (let i = 0; i <= this._nodeCapacity; i++) {
  198. const itemIndex = blockStart + i
  199. if (itemIndex >= this._items.size()) break
  200. const env = this._items.get(itemIndex).getEnvelope()
  201. this.updateNodeBounds(nodeIndex, env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY())
  202. }
  203. }
  204. getBounds() {
  205. const numNodes = Math.trunc(this._nodeBounds.length / 4)
  206. const bounds = new Array(numNodes).fill(null)
  207. for (let i = numNodes - 1; i >= 0; i--) {
  208. const boundIndex = 4 * i
  209. bounds[i] = new Envelope(this._nodeBounds[boundIndex], this._nodeBounds[boundIndex + 2], this._nodeBounds[boundIndex + 1], this._nodeBounds[boundIndex + 3])
  210. }
  211. return bounds
  212. }
  213. get interfaces_() {
  214. return [SpatialIndex]
  215. }
  216. }
  217. class ItemComparator {
  218. constructor() {
  219. ItemComparator.constructor_.apply(this, arguments)
  220. }
  221. static constructor_() {
  222. this._encoder = null
  223. const encoder = arguments[0]
  224. this._encoder = encoder
  225. }
  226. compare(item1, item2) {
  227. const hcode1 = this._encoder.encode(item1.getEnvelope())
  228. const hcode2 = this._encoder.encode(item2.getEnvelope())
  229. return Integer.compare(hcode1, hcode2)
  230. }
  231. get interfaces_() {
  232. return [Comparator]
  233. }
  234. }
  235. HPRtree.ItemComparator = ItemComparator
  236. HPRtree.ENV_SIZE = 4
  237. HPRtree.HILBERT_LEVEL = 12
  238. HPRtree.DEFAULT_NODE_CAPACITY = 16