You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.3 KiB

  1. var OverpassLayer = require('overpass-layer')
  2. const Repository = require('./Repository')
  3. class OpenStreetBrowserLoader {
  4. constructor () {
  5. this.types = {}
  6. this.categories = {}
  7. this.repositories = {}
  8. this.templates = {}
  9. this._loadClash = {} // if a category is being loaded multiple times, collect callbacks
  10. }
  11. setMap (map) {
  12. this.map = map
  13. }
  14. /**
  15. * @param string id ID of the category
  16. * @param [object] options Options.
  17. * @waram {boolean} [options.force=false] Whether repository should be reload or not.
  18. * @param function callback Callback which will be called with (err, category)
  19. */
  20. getCategory (id, options, callback) {
  21. if (typeof options === 'function') {
  22. callback = options
  23. options = {}
  24. }
  25. var ids = this.getFullId(id, options)
  26. if (ids === null) {
  27. return callback(new Error('invalid id'), null)
  28. }
  29. if (options.force) {
  30. delete this.categories[ids.fullId]
  31. }
  32. if (ids.fullId in this.categories) {
  33. return callback(null, this.categories[ids.fullId])
  34. }
  35. var opt = JSON.parse(JSON.stringify(options))
  36. opt.categoryId = ids.entityId
  37. opt.repositoryId = ids.repositoryId
  38. this.getRepository(ids.repositoryId, opt, (err, repository) => {
  39. // maybe loaded in the meantime?
  40. if (ids.fullId in this.categories) {
  41. return callback(null, this.categories[ids.fullId])
  42. }
  43. if (err) {
  44. return callback(err, null)
  45. }
  46. repository.getCategory(ids.entityId, opt, (err, data) => {
  47. // maybe loaded in the meantime?
  48. if (ids.fullId in this.categories) {
  49. return callback(null, this.categories[ids.fullId])
  50. }
  51. if (err) { return callback(err) }
  52. this.getCategoryFromData(ids.id, opt, data, (err, category) =>{
  53. if (category) {
  54. category.setMap(this.map)
  55. }
  56. callback(err, category)
  57. })
  58. })
  59. })
  60. }
  61. /**
  62. * @param string repo ID of the repository
  63. * @param [object] options Options.
  64. * @param {boolean} [options.force=false] Whether repository should be reloaded or not.
  65. * @param function callback Callback which will be called with (err, repository)
  66. */
  67. getRepository (id, options, callback) {
  68. if (id in this.repositories) {
  69. const repository = this.repositories[id]
  70. if (repository.loadCallbacks) {
  71. return repository.loadCallbacks.push((err) => callback(err, repository))
  72. }
  73. if (options.force) {
  74. repository.clearCache()
  75. return repository.load((err) => {
  76. if (err) { return callback(err) }
  77. options.force = false
  78. callback(repository.err, repository)
  79. })
  80. }
  81. return callback(repository.err, repository)
  82. }
  83. this.repositories[id] = new Repository(id)
  84. this.repositories[id].load((err) => callback(err, this.repositories[id]))
  85. }
  86. /**
  87. * @param [object] options Options.
  88. * @param {boolean} [options.force=false] Whether repository should be reloaded or not.
  89. * @param function callback Callback which will be called with (err, list)
  90. */
  91. getRepositoryList (options, callback) {
  92. if (this.list) {
  93. return callback(null, this.list)
  94. }
  95. if (this.repositoryListCallbacks) {
  96. return this.repositoryListCallbacks.push(callback)
  97. }
  98. this.repositoryListCallbacks = [callback]
  99. var param = []
  100. param.push('lang=' + encodeURIComponent(ui_lang))
  101. param.push(config.categoriesRev)
  102. param = param.length ? '?' + param.join('&') : ''
  103. fetch('repo.php' + param)
  104. .then(res => res.json())
  105. .then(data => {
  106. this.list = data
  107. global.setTimeout(() => {
  108. const cbs = this.repositoryListCallbacks
  109. this.repositoryListCallbacks = null
  110. cbs.forEach(cb => cb(null, this.list))
  111. }, 0)
  112. })
  113. .catch(err => {
  114. global.setTimeout(() => {
  115. const cbs = this.repositoryListCallbacks
  116. this.repositoryListCallbacks = null
  117. cbs.forEach(cb => cb(err))
  118. }, 0)
  119. })
  120. }
  121. /**
  122. * @param string id ID of the template
  123. * @parapm [object] options Options.
  124. * @waram {boolean} [options.force=false] Whether repository should be reload or not.
  125. * @param function callback Callback which will be called with (err, template)
  126. */
  127. getTemplate (id, options, callback) {
  128. if (typeof options === 'function') {
  129. callback = options
  130. options = {}
  131. }
  132. var ids = this.getFullId(id, options)
  133. if (options.force) {
  134. delete this.templates[ids.fullId]
  135. }
  136. if (ids.fullId in this.templates) {
  137. return callback(null, this.templates[ids.fullId])
  138. }
  139. var opt = JSON.parse(JSON.stringify(options))
  140. opt.templateId = ids.entityId
  141. opt.repositoryId = ids.repositoryId
  142. this.getRepository(ids.repositoryId, opt, (err, repository) => {
  143. // maybe loaded in the meantime?
  144. if (ids.fullId in this.templates) {
  145. return callback(null, this.templates[ids.fullId])
  146. }
  147. if (err) {
  148. return callback(err, null)
  149. }
  150. repository.getTemplate(ids.entityId, opt, (err, data) => {
  151. // maybe loaded in the meantime?
  152. if (ids.fullId in this.templates) {
  153. return callback(null, this.templates[ids.fullId])
  154. }
  155. if (err) { return callback(err) }
  156. this.templates[ids.fullId] = OverpassLayer.twig.twig({ data, autoescape: true })
  157. callback(null, this.templates[ids.fullId])
  158. })
  159. })
  160. }
  161. getCategoryFromData (id, options, data, callback) {
  162. var ids = this.getFullId(id, options)
  163. if (ids.fullId in this.categories) {
  164. return callback(null, this.categories[ids.fullId])
  165. }
  166. if (!data.type) {
  167. data.type = 'overpass'
  168. }
  169. if (!(data.type in this.types)) {
  170. return callback(new Error('unknown type'), null)
  171. }
  172. let repository = this.repositories[ids.repositoryId]
  173. var opt = JSON.parse(JSON.stringify(options))
  174. opt.id = ids.id
  175. var layer = new this.types[data.type](opt, data, repository)
  176. layer.setMap(this.map)
  177. this.categories[ids.fullId] = layer
  178. if ('load' in layer) {
  179. layer.load(function (err) {
  180. callback(err, layer)
  181. })
  182. } else {
  183. callback(null, layer)
  184. }
  185. }
  186. getFullId (id, options) {
  187. var result = {}
  188. if (!id) {
  189. return null
  190. }
  191. let m = id.match(/^(.*)\/([^/]*)/)
  192. if (m) {
  193. result.id = id
  194. result.repositoryId = m[1]
  195. result.entityId = m[2]
  196. } else if (options.repositoryId && options.repositoryId !== 'default') {
  197. result.repositoryId = options.repositoryId
  198. result.entityId = id
  199. result.id = result.repositoryId + '/' + id
  200. } else {
  201. result.id = id
  202. result.repositoryId = 'default'
  203. result.entityId = id
  204. }
  205. result.sublayerId = null
  206. m = result.entityId.split(/:/)
  207. if (m.length > 1) {
  208. result.sublayerId = m[0]
  209. result.entityId = m[1]
  210. }
  211. result.fullId = result.repositoryId + '/' + (result.sublayerId ? result.sublayerId + ':' : '') + result.entityId
  212. return result
  213. }
  214. forget (id) {
  215. var ids = this.getFullId(id, options)
  216. this.categories[ids.fullId].remove()
  217. delete this.categories[ids.fullId]
  218. }
  219. registerType (type, classObject) {
  220. this.types[type] = classObject
  221. }
  222. }
  223. OpenStreetBrowserLoader.prototype.registerRepository = function (id, repository) {
  224. this.repositories[id] = repository
  225. }
  226. module.exports = new OpenStreetBrowserLoader()