Source: RequestGet.js

  1. const Request = require('./Request')
  2. const defines = require('./defines')
  3. const BoundingBox = require('boundingbox')
  4. const overpassOutOptions = require('./overpassOutOptions')
  5. const RequestGetMembers = require('./RequestGetMembers')
  6. const isGeoJSON = require('./isGeoJSON')
  7. /**
  8. * A get request (request list of map features by id)
  9. * @extends Request
  10. */
  11. class RequestGet extends Request {
  12. /**
  13. * @param {OverpassFrontend} overpass
  14. * @param {data} data
  15. */
  16. constructor (overpass, data) {
  17. super(overpass, data)
  18. this.type = 'get'
  19. if (typeof this.ids === 'string') {
  20. this.ids = [this.ids]
  21. } else {
  22. this.ids = this.ids.concat()
  23. }
  24. if (typeof this.options.properties === 'undefined') {
  25. this.options.properties = defines.DEFAULT
  26. }
  27. for (let i = 0; i < this.ids.length; i++) {
  28. if (this.ids[i] in this.overpass.cacheElements && this.overpass.cacheElements[this.ids[i]] === false) {
  29. delete this.overpass.cacheElements[this.ids[i]]
  30. }
  31. }
  32. if (this.options.bounds) {
  33. if (isGeoJSON(this.options.bounds)) {
  34. this.geojsonBounds = this.options.bounds
  35. }
  36. this.options.bounds = new BoundingBox(this.options.bounds)
  37. } else if (this.options.bbox) {
  38. this.options.bounds = new BoundingBox(this.options.bbox)
  39. delete this.options.bbox
  40. console.error('OverpassFrontend.get(): option "bbox" is deprecated, use "bounds" instead')
  41. }
  42. // option 'split' not available for get requests -> use effort instead
  43. delete this.options.split
  44. this.done = {}
  45. if ('members' in this.options) {
  46. RequestGetMembers(this)
  47. }
  48. }
  49. _effortForId (id) {
  50. if (!id) {
  51. return null
  52. }
  53. const type = id.substr(0, 1)
  54. switch (type) {
  55. case 'n':
  56. return this.overpass.options.effortNode
  57. case 'w':
  58. return this.overpass.options.effortWay
  59. case 'r':
  60. return this.overpass.options.effortRelation
  61. }
  62. }
  63. /**
  64. * how much effort can a call to this request use
  65. * @return {Request#minMaxEffortResult} - minimum and maximum effort
  66. */
  67. minMaxEffort () {
  68. const todo = this.ids.filter(x => x)
  69. if (todo.length === 0) {
  70. return { minEffort: 0, maxEffort: 0 }
  71. }
  72. const minEffort = Math.min.apply(this, todo.map(id => this._effortForId(id)))
  73. const maxEffort = todo.map(id => this._effortForId(id)).reduce((a, b) => a + b)
  74. return { minEffort, maxEffort }
  75. }
  76. /**
  77. * check if there are any map features which can be returned right now
  78. */
  79. preprocess () {
  80. this.allFound = true
  81. for (let i = 0; i < this.ids.length; i++) {
  82. const id = this.ids[i]
  83. if (id === null) {
  84. continue
  85. }
  86. if (id in this.overpass.cacheElements) {
  87. const ob = this.overpass.cacheElements[id]
  88. let ready = true
  89. // Feature does not exists!
  90. if (ob.missingObject) {
  91. this.featureCallback(null, null, i)
  92. this.ids[i] = null
  93. continue
  94. }
  95. // for bounds option, if object is (partly) loaded, but outside call
  96. // featureCallback with 'false'
  97. if (this.options.bounds) {
  98. const intersects = this.geojsonBounds ? ob.intersects(this.geojsonBounds) : ob.intersects(this.options.bounds)
  99. if (intersects === 0 || (!ob.bounds && ob.properties | defines.BBOX)) {
  100. this.featureCallback(null, false, i)
  101. this.ids[i] = null
  102. continue
  103. }
  104. }
  105. // not fully loaded
  106. if ((ob !== false && ob !== null) && (this.options.properties & ob.properties) !== this.options.properties) {
  107. ready = false
  108. }
  109. // if sort is set in options maybe defer calling featureCallback
  110. if (ready) {
  111. this.receiveObject(ob)
  112. this.featureCallback(null, ob, i)
  113. this.ids[i] = null
  114. continue
  115. }
  116. } else {
  117. // Illegal ID
  118. if (id !== null && !id.match(/^[nwr][0-9]+$/)) {
  119. this.featureCallback(null, null, i)
  120. this.ids[i] = null
  121. continue
  122. }
  123. }
  124. this.allFound = false
  125. }
  126. }
  127. /**
  128. * compile the query
  129. * @param {OverpassFrontend#Context} context - Current context
  130. * @return {Request#SubRequest} - the compiled query
  131. */
  132. _compileQuery (context) {
  133. let query = ''
  134. let nodeQuery = ''
  135. let wayQuery = ''
  136. let relationQuery = ''
  137. let BBoxQuery = ''
  138. let effort = 0
  139. let outOptions
  140. if (this.options.bounds) {
  141. BBoxQuery = '(' + this.options.bounds.toLatLonString() + ')'
  142. }
  143. for (let i = 0; i < this.ids.length; i++) {
  144. const id = this.ids[i]
  145. outOptions = overpassOutOptions(this.options)
  146. if (effort > context.maxEffort) {
  147. break
  148. }
  149. if (id === null) {
  150. continue
  151. }
  152. // don't load objects multiple times in same context
  153. if (id in context.todo) {
  154. continue
  155. }
  156. if (this.options.bounds) {
  157. // check if we already know the bounds of the element; if yes, don't try
  158. // to load object if it does not intersect bounds
  159. if (id in this.overpass.cacheElements && (this.overpass.cacheElements[id].properties & defines.BBOX)) {
  160. if (!this.overpass.cacheElements[id].intersects(this.options.bounds)) {
  161. continue
  162. }
  163. }
  164. }
  165. switch (id.substr(0, 1)) {
  166. case 'n':
  167. nodeQuery += 'node(' + id.substr(1) + ');\n'
  168. effort += this.overpass.options.effortNode
  169. break
  170. case 'w':
  171. wayQuery += 'way(' + id.substr(1) + ');\n'
  172. effort += this.overpass.options.effortWay
  173. break
  174. case 'r':
  175. relationQuery += 'relation(' + id.substr(1) + ');\n'
  176. effort += this.overpass.options.effortRelation
  177. break
  178. }
  179. context.todo[id] = true
  180. }
  181. if (nodeQuery !== '') {
  182. query += '((' + nodeQuery + ');)->.n;\n'
  183. if (BBoxQuery) {
  184. query += '(node.n; - node.n' + BBoxQuery + '->.n;);\nout ids bb qt;\n'
  185. }
  186. }
  187. if (wayQuery !== '') {
  188. query += '((' + wayQuery + ');)->.w;\n'
  189. if (BBoxQuery) {
  190. query += '(way.w; - way.w' + BBoxQuery + '->.w;);\nout ids bb qt;\n'
  191. }
  192. }
  193. if (relationQuery !== '') {
  194. query += '((' + relationQuery + ');)->.r;\n'
  195. if (BBoxQuery) {
  196. query += '(relation.r; - relation.r' + BBoxQuery + '->.r;);\nout ids bb qt;\n'
  197. }
  198. }
  199. const requestParts = []
  200. if (BBoxQuery && (nodeQuery !== '' || wayQuery !== '' || relationQuery !== '')) {
  201. // additional separator to separate objects outside bbox from inside bbox
  202. query += 'out count;\n'
  203. requestParts.push({
  204. properties: defines.BBOX,
  205. bounds: this.options.bounds,
  206. boundsNoMatch: true
  207. })
  208. }
  209. if (nodeQuery !== '') {
  210. query += '.n out ' + outOptions + ';\n'
  211. }
  212. if (wayQuery !== '') {
  213. query += '.w out ' + outOptions + ';\n'
  214. }
  215. if (relationQuery !== '') {
  216. query += '.r out ' + outOptions + ';\n'
  217. }
  218. if (query) {
  219. requestParts.push({
  220. properties: this.options.properties,
  221. receiveObject: this.receiveObject.bind(this),
  222. checkFeatureCallback: this.checkFeatureCallback.bind(this),
  223. featureCallback: this._featureCallback.bind(this, this.featureCallback)
  224. })
  225. }
  226. const subRequest = {
  227. query,
  228. effort: effort,
  229. request: this,
  230. parts: requestParts
  231. }
  232. return subRequest
  233. }
  234. checkFeatureCallback (ob) {
  235. if (this.geojsonBounds && ob.intersects(this.geojsonBounds) === 0) {
  236. return false
  237. }
  238. return true
  239. }
  240. _featureCallback (fun, err, ob) {
  241. const indexes = []
  242. let p
  243. while ((p = this.ids.indexOf(ob.id)) !== -1) {
  244. this.ids[p] = null
  245. indexes.push(p)
  246. }
  247. if (this.options.bounds && !ob.intersects(this.options.bounds)) {
  248. indexes.forEach(p => fun(null, false, p))
  249. return
  250. }
  251. indexes.forEach(p => fun(null, ob, p))
  252. }
  253. needLoad () {
  254. this.preprocess()
  255. return this.allFound
  256. }
  257. mayFinish () {
  258. return this.allFound
  259. }
  260. }
  261. module.exports = RequestGet