KdTree.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import CoordinateList from '../../geom/CoordinateList'
  2. import hasInterface from '../../../../../hasInterface'
  3. import ArrayList from '../../../../../java/util/ArrayList'
  4. import KdNodeVisitor from './KdNodeVisitor'
  5. import Envelope from '../../geom/Envelope'
  6. import List from '../../../../../java/util/List'
  7. import KdNode from './KdNode'
  8. export default class KdTree {
  9. constructor() {
  10. KdTree.constructor_.apply(this, arguments)
  11. }
  12. static constructor_() {
  13. this._root = null
  14. this._numberOfNodes = null
  15. this._tolerance = null
  16. if (arguments.length === 0) {
  17. KdTree.constructor_.call(this, 0.0)
  18. } else if (arguments.length === 1) {
  19. const tolerance = arguments[0]
  20. this._tolerance = tolerance
  21. }
  22. }
  23. static toCoordinates() {
  24. if (arguments.length === 1) {
  25. const kdnodes = arguments[0]
  26. return KdTree.toCoordinates(kdnodes, false)
  27. } else if (arguments.length === 2) {
  28. const kdnodes = arguments[0], includeRepeated = arguments[1]
  29. const coord = new CoordinateList()
  30. for (let it = kdnodes.iterator(); it.hasNext(); ) {
  31. const node = it.next()
  32. const count = includeRepeated ? node.getCount() : 1
  33. for (let i = 0; i < count; i++)
  34. coord.add(node.getCoordinate(), true)
  35. }
  36. return coord.toCoordinateArray()
  37. }
  38. }
  39. insert() {
  40. if (arguments.length === 1) {
  41. const p = arguments[0]
  42. return this.insert(p, null)
  43. } else if (arguments.length === 2) {
  44. const p = arguments[0], data = arguments[1]
  45. if (this._root === null) {
  46. this._root = new KdNode(p, data)
  47. return this._root
  48. }
  49. if (this._tolerance > 0) {
  50. const matchNode = this.findBestMatchNode(p)
  51. if (matchNode !== null) {
  52. matchNode.increment()
  53. return matchNode
  54. }
  55. }
  56. return this.insertExact(p, data)
  57. }
  58. }
  59. query() {
  60. if (arguments.length === 1) {
  61. const queryEnv = arguments[0]
  62. const result = new ArrayList()
  63. this.query(queryEnv, result)
  64. return result
  65. } else if (arguments.length === 2) {
  66. if (arguments[0] instanceof Envelope && hasInterface(arguments[1], List)) {
  67. const queryEnv = arguments[0], result = arguments[1]
  68. this.queryNode(this._root, queryEnv, true, new (class {
  69. get interfaces_() {
  70. return [KdNodeVisitor]
  71. }
  72. visit(node) {
  73. result.add(node)
  74. }
  75. })())
  76. } else if (arguments[0] instanceof Envelope && hasInterface(arguments[1], KdNodeVisitor)) {
  77. const queryEnv = arguments[0], visitor = arguments[1]
  78. this.queryNode(this._root, queryEnv, true, visitor)
  79. }
  80. }
  81. }
  82. queryNode(currentNode, queryEnv, odd, visitor) {
  83. if (currentNode === null) return null
  84. let min = null
  85. let max = null
  86. let discriminant = null
  87. if (odd) {
  88. min = queryEnv.getMinX()
  89. max = queryEnv.getMaxX()
  90. discriminant = currentNode.getX()
  91. } else {
  92. min = queryEnv.getMinY()
  93. max = queryEnv.getMaxY()
  94. discriminant = currentNode.getY()
  95. }
  96. const searchLeft = min < discriminant
  97. const searchRight = discriminant <= max
  98. if (searchLeft)
  99. this.queryNode(currentNode.getLeft(), queryEnv, !odd, visitor)
  100. if (queryEnv.contains(currentNode.getCoordinate()))
  101. visitor.visit(currentNode)
  102. if (searchRight)
  103. this.queryNode(currentNode.getRight(), queryEnv, !odd, visitor)
  104. }
  105. findBestMatchNode(p) {
  106. const visitor = new BestMatchVisitor(p, this._tolerance)
  107. this.query(visitor.queryEnvelope(), visitor)
  108. return visitor.getNode()
  109. }
  110. isEmpty() {
  111. if (this._root === null) return true
  112. return false
  113. }
  114. insertExact(p, data) {
  115. let currentNode = this._root
  116. let leafNode = this._root
  117. let isOddLevel = true
  118. let isLessThan = true
  119. while (currentNode !== null) {
  120. if (currentNode !== null) {
  121. const isInTolerance = p.distance(currentNode.getCoordinate()) <= this._tolerance
  122. if (isInTolerance) {
  123. currentNode.increment()
  124. return currentNode
  125. }
  126. }
  127. if (isOddLevel)
  128. isLessThan = p.x < currentNode.getX()
  129. else
  130. isLessThan = p.y < currentNode.getY()
  131. leafNode = currentNode
  132. if (isLessThan)
  133. currentNode = currentNode.getLeft()
  134. else
  135. currentNode = currentNode.getRight()
  136. isOddLevel = !isOddLevel
  137. }
  138. this._numberOfNodes = this._numberOfNodes + 1
  139. const node = new KdNode(p, data)
  140. if (isLessThan)
  141. leafNode.setLeft(node)
  142. else
  143. leafNode.setRight(node)
  144. return node
  145. }
  146. }
  147. class BestMatchVisitor {
  148. constructor() {
  149. BestMatchVisitor.constructor_.apply(this, arguments)
  150. }
  151. static constructor_() {
  152. this._tolerance = null
  153. this._matchNode = null
  154. this._matchDist = 0.0
  155. this._p = null
  156. const p = arguments[0], tolerance = arguments[1]
  157. this._p = p
  158. this._tolerance = tolerance
  159. }
  160. visit(node) {
  161. const dist = this._p.distance(node.getCoordinate())
  162. const isInTolerance = dist <= this._tolerance
  163. if (!isInTolerance) return null
  164. let update = false
  165. if (this._matchNode === null || dist < this._matchDist || this._matchNode !== null && dist === this._matchDist && node.getCoordinate().compareTo(this._matchNode.getCoordinate()) < 1) update = true
  166. if (update) {
  167. this._matchNode = node
  168. this._matchDist = dist
  169. }
  170. }
  171. queryEnvelope() {
  172. const queryEnv = new Envelope(this._p)
  173. queryEnv.expandBy(this._tolerance)
  174. return queryEnv
  175. }
  176. getNode() {
  177. return this._matchNode
  178. }
  179. get interfaces_() {
  180. return [KdNodeVisitor]
  181. }
  182. }
  183. KdTree.BestMatchVisitor = BestMatchVisitor