@kalisio/kdk 2.5.1 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/core/api/hooks/hooks.authorisations.js +1 -2
  2. package/core/api/services/authorisations/authorisations.service.js +1 -3
  3. package/core/api/services/index.js +5 -1
  4. package/core/api/services/users/users.hooks.js +4 -3
  5. package/core/client/components/input/KShapePicker.vue +4 -4
  6. package/core/client/utils/index.js +1 -0
  7. package/coverage/core/api/application.js.html +344 -344
  8. package/coverage/core/api/authentication.js.html +79 -79
  9. package/coverage/core/api/db.js.html +167 -167
  10. package/coverage/core/api/hooks/hooks.authentication.js.html +12 -12
  11. package/coverage/core/api/hooks/hooks.authorisations.js.html +154 -157
  12. package/coverage/core/api/hooks/hooks.logger.js.html +18 -18
  13. package/coverage/core/api/hooks/hooks.model.js.html +275 -275
  14. package/coverage/core/api/hooks/hooks.push.js.html +34 -34
  15. package/coverage/core/api/hooks/hooks.query.js.html +89 -89
  16. package/coverage/core/api/hooks/hooks.schemas.js.html +13 -13
  17. package/coverage/core/api/hooks/hooks.service.js.html +28 -28
  18. package/coverage/core/api/hooks/hooks.storage.js.html +7 -7
  19. package/coverage/core/api/hooks/hooks.users.js.html +50 -50
  20. package/coverage/core/api/hooks/index.html +51 -51
  21. package/coverage/core/api/hooks/index.js.html +11 -11
  22. package/coverage/core/api/index.html +42 -42
  23. package/coverage/core/api/index.js.html +22 -22
  24. package/coverage/core/api/marshall.js.html +127 -127
  25. package/coverage/core/api/models/index.html +1 -1
  26. package/coverage/core/api/models/messages.model.mongodb.js.html +1 -1
  27. package/coverage/core/api/models/users.model.mongodb.js.html +11 -11
  28. package/coverage/core/api/services/account/account.hooks.js.html +42 -42
  29. package/coverage/core/api/services/account/account.service.js.html +34 -34
  30. package/coverage/core/api/services/account/index.html +1 -1
  31. package/coverage/core/api/services/authorisations/authorisations.hooks.js.html +34 -34
  32. package/coverage/core/api/services/authorisations/authorisations.service.js.html +87 -93
  33. package/coverage/core/api/services/authorisations/index.html +19 -19
  34. package/coverage/core/api/services/databases/databases.hooks.js.html +1 -1
  35. package/coverage/core/api/services/databases/databases.service.js.html +1 -1
  36. package/coverage/core/api/services/databases/index.html +1 -1
  37. package/coverage/core/api/services/import-export/import-export.hooks.js.html +1 -1
  38. package/coverage/core/api/services/import-export/import-export.service.js.html +1 -1
  39. package/coverage/core/api/services/import-export/index.html +1 -1
  40. package/coverage/core/api/services/index.html +15 -15
  41. package/coverage/core/api/services/index.js.html +97 -85
  42. package/coverage/core/api/services/mailer/index.html +1 -1
  43. package/coverage/core/api/services/mailer/mailer.hooks.js.html +1 -1
  44. package/coverage/core/api/services/mailer/mailer.service.js.html +1 -1
  45. package/coverage/core/api/services/messages/index.html +1 -1
  46. package/coverage/core/api/services/messages/messages.hooks.js.html +1 -1
  47. package/coverage/core/api/services/push/index.html +1 -1
  48. package/coverage/core/api/services/push/push.hooks.js.html +1 -1
  49. package/coverage/core/api/services/push/push.service.js.html +1 -1
  50. package/coverage/core/api/services/storage/index.html +1 -1
  51. package/coverage/core/api/services/storage/storage.hooks.js.html +36 -36
  52. package/coverage/core/api/services/storage/storage.service.js.html +30 -30
  53. package/coverage/core/api/services/users/index.html +7 -7
  54. package/coverage/core/api/services/users/users.hooks.js.html +83 -80
  55. package/coverage/core/api/services/users/users.service.js.html +4 -4
  56. package/coverage/core/common/errors.js.html +2 -2
  57. package/coverage/core/common/index.html +30 -30
  58. package/coverage/core/common/index.js.html +11 -11
  59. package/coverage/core/common/permissions.js.html +187 -187
  60. package/coverage/core/common/schema.js.html +26 -26
  61. package/coverage/core/common/utils.js.html +61 -61
  62. package/coverage/core/common/utils.offline.js.html +6 -6
  63. package/coverage/index.html +173 -173
  64. package/coverage/lcov-report/core/api/application.js.html +344 -344
  65. package/coverage/lcov-report/core/api/authentication.js.html +79 -79
  66. package/coverage/lcov-report/core/api/db.js.html +167 -167
  67. package/coverage/lcov-report/core/api/hooks/hooks.authentication.js.html +12 -12
  68. package/coverage/lcov-report/core/api/hooks/hooks.authorisations.js.html +154 -157
  69. package/coverage/lcov-report/core/api/hooks/hooks.logger.js.html +18 -18
  70. package/coverage/lcov-report/core/api/hooks/hooks.model.js.html +275 -275
  71. package/coverage/lcov-report/core/api/hooks/hooks.push.js.html +34 -34
  72. package/coverage/lcov-report/core/api/hooks/hooks.query.js.html +89 -89
  73. package/coverage/lcov-report/core/api/hooks/hooks.schemas.js.html +13 -13
  74. package/coverage/lcov-report/core/api/hooks/hooks.service.js.html +28 -28
  75. package/coverage/lcov-report/core/api/hooks/hooks.storage.js.html +7 -7
  76. package/coverage/lcov-report/core/api/hooks/hooks.users.js.html +50 -50
  77. package/coverage/lcov-report/core/api/hooks/index.html +51 -51
  78. package/coverage/lcov-report/core/api/hooks/index.js.html +11 -11
  79. package/coverage/lcov-report/core/api/index.html +42 -42
  80. package/coverage/lcov-report/core/api/index.js.html +22 -22
  81. package/coverage/lcov-report/core/api/marshall.js.html +127 -127
  82. package/coverage/lcov-report/core/api/models/index.html +1 -1
  83. package/coverage/lcov-report/core/api/models/messages.model.mongodb.js.html +1 -1
  84. package/coverage/lcov-report/core/api/models/users.model.mongodb.js.html +11 -11
  85. package/coverage/lcov-report/core/api/services/account/account.hooks.js.html +42 -42
  86. package/coverage/lcov-report/core/api/services/account/account.service.js.html +34 -34
  87. package/coverage/lcov-report/core/api/services/account/index.html +1 -1
  88. package/coverage/lcov-report/core/api/services/authorisations/authorisations.hooks.js.html +34 -34
  89. package/coverage/lcov-report/core/api/services/authorisations/authorisations.service.js.html +87 -93
  90. package/coverage/lcov-report/core/api/services/authorisations/index.html +19 -19
  91. package/coverage/lcov-report/core/api/services/databases/databases.hooks.js.html +1 -1
  92. package/coverage/lcov-report/core/api/services/databases/databases.service.js.html +1 -1
  93. package/coverage/lcov-report/core/api/services/databases/index.html +1 -1
  94. package/coverage/lcov-report/core/api/services/import-export/import-export.hooks.js.html +1 -1
  95. package/coverage/lcov-report/core/api/services/import-export/import-export.service.js.html +1 -1
  96. package/coverage/lcov-report/core/api/services/import-export/index.html +1 -1
  97. package/coverage/lcov-report/core/api/services/index.html +15 -15
  98. package/coverage/lcov-report/core/api/services/index.js.html +97 -85
  99. package/coverage/lcov-report/core/api/services/mailer/index.html +1 -1
  100. package/coverage/lcov-report/core/api/services/mailer/mailer.hooks.js.html +1 -1
  101. package/coverage/lcov-report/core/api/services/mailer/mailer.service.js.html +1 -1
  102. package/coverage/lcov-report/core/api/services/messages/index.html +1 -1
  103. package/coverage/lcov-report/core/api/services/messages/messages.hooks.js.html +1 -1
  104. package/coverage/lcov-report/core/api/services/push/index.html +1 -1
  105. package/coverage/lcov-report/core/api/services/push/push.hooks.js.html +1 -1
  106. package/coverage/lcov-report/core/api/services/push/push.service.js.html +1 -1
  107. package/coverage/lcov-report/core/api/services/storage/index.html +1 -1
  108. package/coverage/lcov-report/core/api/services/storage/storage.hooks.js.html +36 -36
  109. package/coverage/lcov-report/core/api/services/storage/storage.service.js.html +30 -30
  110. package/coverage/lcov-report/core/api/services/users/index.html +7 -7
  111. package/coverage/lcov-report/core/api/services/users/users.hooks.js.html +83 -80
  112. package/coverage/lcov-report/core/api/services/users/users.service.js.html +4 -4
  113. package/coverage/lcov-report/core/common/errors.js.html +2 -2
  114. package/coverage/lcov-report/core/common/index.html +30 -30
  115. package/coverage/lcov-report/core/common/index.js.html +11 -11
  116. package/coverage/lcov-report/core/common/permissions.js.html +187 -187
  117. package/coverage/lcov-report/core/common/schema.js.html +26 -26
  118. package/coverage/lcov-report/core/common/utils.js.html +61 -61
  119. package/coverage/lcov-report/core/common/utils.offline.js.html +6 -6
  120. package/coverage/lcov-report/index.html +173 -173
  121. package/coverage/lcov-report/map/api/hooks/hooks.catalog.js.html +316 -316
  122. package/coverage/lcov-report/map/api/hooks/hooks.features.js.html +218 -218
  123. package/coverage/lcov-report/map/api/hooks/hooks.query.js.html +798 -789
  124. package/coverage/lcov-report/map/api/hooks/index.html +54 -54
  125. package/coverage/lcov-report/map/api/hooks/index.js.html +16 -16
  126. package/coverage/lcov-report/map/api/index.html +32 -32
  127. package/coverage/lcov-report/map/api/index.js.html +46 -46
  128. package/coverage/lcov-report/map/api/marshall.js.html +72 -72
  129. package/coverage/lcov-report/map/api/models/alerts.model.mongodb.js.html +24 -24
  130. package/coverage/lcov-report/map/api/models/catalog.model.mongodb.js.html +44 -44
  131. package/coverage/lcov-report/map/api/models/features.model.mongodb.js.html +80 -80
  132. package/coverage/lcov-report/map/api/models/index.html +65 -65
  133. package/coverage/lcov-report/map/api/models/projects.model.mongodb.js.html +26 -26
  134. package/coverage/lcov-report/map/api/models/styles.model.mongodb.js.html +28 -28
  135. package/coverage/lcov-report/map/api/services/alerts/alerts.hooks.js.html +136 -136
  136. package/coverage/lcov-report/map/api/services/alerts/alerts.service.js.html +343 -343
  137. package/coverage/lcov-report/map/api/services/alerts/index.html +32 -32
  138. package/coverage/lcov-report/map/api/services/catalog/catalog.hooks.js.html +172 -172
  139. package/coverage/lcov-report/map/api/services/catalog/index.html +21 -21
  140. package/coverage/lcov-report/map/api/services/daptiles/daptiles.service.js.html +1 -1
  141. package/coverage/lcov-report/map/api/services/daptiles/index.html +1 -1
  142. package/coverage/lcov-report/map/api/services/features/features.hooks.js.html +160 -160
  143. package/coverage/lcov-report/map/api/services/features/features.service.js.html +117 -117
  144. package/coverage/lcov-report/map/api/services/features/index.html +31 -31
  145. package/coverage/lcov-report/map/api/services/index.html +21 -21
  146. package/coverage/lcov-report/map/api/services/index.js.html +334 -334
  147. package/coverage/lcov-report/map/api/services/projects/index.html +21 -21
  148. package/coverage/lcov-report/map/api/services/projects/projects.hooks.js.html +237 -237
  149. package/coverage/lcov-report/map/api/services/styles/index.html +21 -21
  150. package/coverage/lcov-report/map/api/services/styles/styles.hooks.js.html +84 -84
  151. package/coverage/lcov-report/map/common/dynamic-grid-source.js.html +68 -68
  152. package/coverage/lcov-report/map/common/errors.js.html +16 -16
  153. package/coverage/lcov-report/map/common/geotiff-grid-source.js.html +267 -267
  154. package/coverage/lcov-report/map/common/grid.js.html +554 -554
  155. package/coverage/lcov-report/map/common/index.html +159 -159
  156. package/coverage/lcov-report/map/common/index.js.html +68 -68
  157. package/coverage/lcov-report/map/common/meteo-model-grid-source.js.html +73 -73
  158. package/coverage/lcov-report/map/common/moment-utils.js.html +18 -18
  159. package/coverage/lcov-report/map/common/opendap-grid-source.js.html +484 -484
  160. package/coverage/lcov-report/map/common/opendap-utils.js.html +353 -353
  161. package/coverage/lcov-report/map/common/permissions.js.html +40 -40
  162. package/coverage/lcov-report/map/common/time-based-grid-source.js.html +59 -59
  163. package/coverage/lcov-report/map/common/tms-utils.js.html +1 -1
  164. package/coverage/lcov-report/map/common/wcs-grid-source.js.html +190 -190
  165. package/coverage/lcov-report/map/common/wcs-utils.js.html +338 -338
  166. package/coverage/lcov-report/map/common/weacast-grid-source.js.html +345 -345
  167. package/coverage/lcov-report/map/common/wfs-utils.js.html +1 -1
  168. package/coverage/lcov-report/map/common/wms-utils.js.html +1 -1
  169. package/coverage/lcov-report/map/common/wmts-utils.js.html +1 -1
  170. package/coverage/lcov.info +6880 -5908
  171. package/coverage/map/api/hooks/hooks.catalog.js.html +316 -316
  172. package/coverage/map/api/hooks/hooks.features.js.html +218 -218
  173. package/coverage/map/api/hooks/hooks.query.js.html +798 -789
  174. package/coverage/map/api/hooks/index.html +54 -54
  175. package/coverage/map/api/hooks/index.js.html +16 -16
  176. package/coverage/map/api/index.html +32 -32
  177. package/coverage/map/api/index.js.html +46 -46
  178. package/coverage/map/api/marshall.js.html +72 -72
  179. package/coverage/map/api/models/alerts.model.mongodb.js.html +24 -24
  180. package/coverage/map/api/models/catalog.model.mongodb.js.html +44 -44
  181. package/coverage/map/api/models/features.model.mongodb.js.html +80 -80
  182. package/coverage/map/api/models/index.html +65 -65
  183. package/coverage/map/api/models/projects.model.mongodb.js.html +26 -26
  184. package/coverage/map/api/models/styles.model.mongodb.js.html +28 -28
  185. package/coverage/map/api/services/alerts/alerts.hooks.js.html +136 -136
  186. package/coverage/map/api/services/alerts/alerts.service.js.html +343 -343
  187. package/coverage/map/api/services/alerts/index.html +32 -32
  188. package/coverage/map/api/services/catalog/catalog.hooks.js.html +172 -172
  189. package/coverage/map/api/services/catalog/index.html +21 -21
  190. package/coverage/map/api/services/daptiles/daptiles.service.js.html +1 -1
  191. package/coverage/map/api/services/daptiles/index.html +1 -1
  192. package/coverage/map/api/services/features/features.hooks.js.html +160 -160
  193. package/coverage/map/api/services/features/features.service.js.html +117 -117
  194. package/coverage/map/api/services/features/index.html +31 -31
  195. package/coverage/map/api/services/index.html +21 -21
  196. package/coverage/map/api/services/index.js.html +334 -334
  197. package/coverage/map/api/services/projects/index.html +21 -21
  198. package/coverage/map/api/services/projects/projects.hooks.js.html +237 -237
  199. package/coverage/map/api/services/styles/index.html +21 -21
  200. package/coverage/map/api/services/styles/styles.hooks.js.html +84 -84
  201. package/coverage/map/common/dynamic-grid-source.js.html +68 -68
  202. package/coverage/map/common/errors.js.html +16 -16
  203. package/coverage/map/common/geotiff-grid-source.js.html +267 -267
  204. package/coverage/map/common/grid.js.html +554 -554
  205. package/coverage/map/common/index.html +159 -159
  206. package/coverage/map/common/index.js.html +68 -68
  207. package/coverage/map/common/meteo-model-grid-source.js.html +73 -73
  208. package/coverage/map/common/moment-utils.js.html +18 -18
  209. package/coverage/map/common/opendap-grid-source.js.html +484 -484
  210. package/coverage/map/common/opendap-utils.js.html +353 -353
  211. package/coverage/map/common/permissions.js.html +40 -40
  212. package/coverage/map/common/time-based-grid-source.js.html +59 -59
  213. package/coverage/map/common/tms-utils.js.html +1 -1
  214. package/coverage/map/common/wcs-grid-source.js.html +190 -190
  215. package/coverage/map/common/wcs-utils.js.html +338 -338
  216. package/coverage/map/common/weacast-grid-source.js.html +345 -345
  217. package/coverage/map/common/wfs-utils.js.html +1 -1
  218. package/coverage/map/common/wms-utils.js.html +1 -1
  219. package/coverage/map/common/wmts-utils.js.html +1 -1
  220. package/coverage/tmp/coverage-151198-1753351220086-0.json +1 -0
  221. package/coverage/tmp/coverage-151210-1753351220070-0.json +1 -0
  222. package/coverage/tmp/coverage-151221-1753351129816-0.json +1 -0
  223. package/coverage/tmp/coverage-151233-1753351129803-0.json +1 -0
  224. package/coverage/tmp/coverage-151240-1753351129770-0.json +1 -0
  225. package/coverage/tmp/coverage-151307-1753351220058-0.json +1 -0
  226. package/coverage/tmp/coverage-151319-1753351220044-0.json +1 -0
  227. package/coverage/tmp/coverage-151326-1753351220010-0.json +1 -0
  228. package/map/api/hooks/hooks.query.js +7 -4
  229. package/map/api/services/styles/styles.hooks.js +1 -1
  230. package/map/client/components/KLayerEditor.vue +4 -0
  231. package/map/client/components/catalog/KLayerCategories.vue +2 -0
  232. package/map/client/components/stickies/KAttribution.vue +8 -7
  233. package/map/client/components/stickies/KLevelSlider.vue +10 -8
  234. package/map/client/components/styles/KStyleEditor.vue +1 -1
  235. package/map/client/components/styles/KStyleManager.vue +7 -1
  236. package/map/client/composables/highlight.js +5 -1
  237. package/map/client/i18n/map_en.json +4 -1
  238. package/map/client/i18n/map_fr.json +5 -2
  239. package/map/client/leaflet/utils/utils.geojson.js +5 -4
  240. package/map/client/mixins/globe/mixin.geojson-layers.js +11 -5
  241. package/map/client/mixins/mixin.activity.js +9 -0
  242. package/map/client/utils/utils.layers.js +0 -2
  243. package/package.json +1 -1
  244. package/test/api/core/index.test.js +49 -60
  245. package/test/api/core/push.test.js +1 -1
  246. package/test/api/core/test-log-2025-06-25.log +9 -0
  247. package/test/api/core/test-log-2025-07-24.log +44 -0
  248. package/test/api/map/test-log-2025-05-27.log +13 -0
  249. package/test/api/map/test-log-2025-06-23.log +7 -0
  250. package/test/api/map/test-log-2025-07-24.log +11 -0
  251. package/coverage/tmp/coverage-323534-1747828879483-0.json +0 -1
  252. package/coverage/tmp/coverage-323546-1747828879453-0.json +0 -1
  253. package/coverage/tmp/coverage-323557-1747828879439-0.json +0 -1
  254. package/coverage/tmp/coverage-323569-1747828879416-0.json +0 -1
  255. package/coverage/tmp/coverage-323576-1747828879390-0.json +0 -1
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">0% </span>
26
+ <span class="strong">94.29% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>0/400</span>
28
+ <span class='fraction'>380/403</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">0% </span>
33
+ <span class="strong">79.67% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>0/1</span>
35
+ <span class='fraction'>98/123</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">0% </span>
40
+ <span class="strong">100% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>0/1</span>
42
+ <span class='fraction'>6/6</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">0% </span>
47
+ <span class="strong">94.29% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>0/400</span>
49
+ <span class='fraction'>380/403</span>
50
50
  </div>
51
51
 
52
52
 
@@ -61,7 +61,7 @@
61
61
  </div>
62
62
  </template>
63
63
  </div>
64
- <div class='status-line low'></div>
64
+ <div class='status-line high'></div>
65
65
  <pre><table class="coverage">
66
66
  <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
67
  <a name='L2'></a><a href='#L2'>2</a>
@@ -463,806 +463,815 @@
463
463
  <a name='L398'></a><a href='#L398'>398</a>
464
464
  <a name='L399'></a><a href='#L399'>399</a>
465
465
  <a name='L400'></a><a href='#L400'>400</a>
466
- <a name='L401'></a><a href='#L401'>401</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
467
- <span class="cline-any cline-no">&nbsp;</span>
468
- <span class="cline-any cline-no">&nbsp;</span>
469
- <span class="cline-any cline-no">&nbsp;</span>
470
- <span class="cline-any cline-no">&nbsp;</span>
471
- <span class="cline-any cline-no">&nbsp;</span>
472
- <span class="cline-any cline-no">&nbsp;</span>
473
- <span class="cline-any cline-no">&nbsp;</span>
474
- <span class="cline-any cline-no">&nbsp;</span>
475
- <span class="cline-any cline-no">&nbsp;</span>
476
- <span class="cline-any cline-no">&nbsp;</span>
477
- <span class="cline-any cline-no">&nbsp;</span>
478
- <span class="cline-any cline-no">&nbsp;</span>
479
- <span class="cline-any cline-no">&nbsp;</span>
480
- <span class="cline-any cline-no">&nbsp;</span>
481
- <span class="cline-any cline-no">&nbsp;</span>
482
- <span class="cline-any cline-no">&nbsp;</span>
483
- <span class="cline-any cline-no">&nbsp;</span>
484
- <span class="cline-any cline-no">&nbsp;</span>
485
- <span class="cline-any cline-no">&nbsp;</span>
486
- <span class="cline-any cline-no">&nbsp;</span>
487
- <span class="cline-any cline-no">&nbsp;</span>
488
- <span class="cline-any cline-no">&nbsp;</span>
489
- <span class="cline-any cline-no">&nbsp;</span>
490
- <span class="cline-any cline-no">&nbsp;</span>
491
- <span class="cline-any cline-no">&nbsp;</span>
492
- <span class="cline-any cline-no">&nbsp;</span>
493
- <span class="cline-any cline-no">&nbsp;</span>
494
- <span class="cline-any cline-no">&nbsp;</span>
495
- <span class="cline-any cline-no">&nbsp;</span>
496
- <span class="cline-any cline-no">&nbsp;</span>
497
- <span class="cline-any cline-no">&nbsp;</span>
498
- <span class="cline-any cline-no">&nbsp;</span>
499
- <span class="cline-any cline-no">&nbsp;</span>
500
- <span class="cline-any cline-no">&nbsp;</span>
501
- <span class="cline-any cline-no">&nbsp;</span>
502
- <span class="cline-any cline-no">&nbsp;</span>
503
- <span class="cline-any cline-no">&nbsp;</span>
504
- <span class="cline-any cline-no">&nbsp;</span>
505
- <span class="cline-any cline-no">&nbsp;</span>
506
- <span class="cline-any cline-no">&nbsp;</span>
507
- <span class="cline-any cline-no">&nbsp;</span>
508
- <span class="cline-any cline-no">&nbsp;</span>
509
- <span class="cline-any cline-no">&nbsp;</span>
510
- <span class="cline-any cline-no">&nbsp;</span>
511
- <span class="cline-any cline-no">&nbsp;</span>
512
- <span class="cline-any cline-no">&nbsp;</span>
513
- <span class="cline-any cline-no">&nbsp;</span>
514
- <span class="cline-any cline-no">&nbsp;</span>
515
- <span class="cline-any cline-no">&nbsp;</span>
516
- <span class="cline-any cline-no">&nbsp;</span>
517
- <span class="cline-any cline-no">&nbsp;</span>
518
- <span class="cline-any cline-no">&nbsp;</span>
519
- <span class="cline-any cline-no">&nbsp;</span>
520
- <span class="cline-any cline-no">&nbsp;</span>
521
- <span class="cline-any cline-no">&nbsp;</span>
522
- <span class="cline-any cline-no">&nbsp;</span>
523
- <span class="cline-any cline-no">&nbsp;</span>
524
- <span class="cline-any cline-no">&nbsp;</span>
525
- <span class="cline-any cline-no">&nbsp;</span>
526
- <span class="cline-any cline-no">&nbsp;</span>
527
- <span class="cline-any cline-no">&nbsp;</span>
528
- <span class="cline-any cline-no">&nbsp;</span>
529
- <span class="cline-any cline-no">&nbsp;</span>
530
- <span class="cline-any cline-no">&nbsp;</span>
531
- <span class="cline-any cline-no">&nbsp;</span>
532
- <span class="cline-any cline-no">&nbsp;</span>
533
- <span class="cline-any cline-no">&nbsp;</span>
534
- <span class="cline-any cline-no">&nbsp;</span>
535
- <span class="cline-any cline-no">&nbsp;</span>
536
- <span class="cline-any cline-no">&nbsp;</span>
537
- <span class="cline-any cline-no">&nbsp;</span>
538
- <span class="cline-any cline-no">&nbsp;</span>
539
- <span class="cline-any cline-no">&nbsp;</span>
540
- <span class="cline-any cline-no">&nbsp;</span>
541
- <span class="cline-any cline-no">&nbsp;</span>
542
- <span class="cline-any cline-no">&nbsp;</span>
543
- <span class="cline-any cline-no">&nbsp;</span>
544
- <span class="cline-any cline-no">&nbsp;</span>
545
- <span class="cline-any cline-no">&nbsp;</span>
546
- <span class="cline-any cline-no">&nbsp;</span>
547
- <span class="cline-any cline-no">&nbsp;</span>
548
- <span class="cline-any cline-no">&nbsp;</span>
549
- <span class="cline-any cline-no">&nbsp;</span>
550
- <span class="cline-any cline-no">&nbsp;</span>
551
- <span class="cline-any cline-no">&nbsp;</span>
552
- <span class="cline-any cline-no">&nbsp;</span>
553
- <span class="cline-any cline-no">&nbsp;</span>
554
- <span class="cline-any cline-no">&nbsp;</span>
555
- <span class="cline-any cline-no">&nbsp;</span>
556
- <span class="cline-any cline-no">&nbsp;</span>
557
- <span class="cline-any cline-no">&nbsp;</span>
558
- <span class="cline-any cline-no">&nbsp;</span>
559
- <span class="cline-any cline-no">&nbsp;</span>
560
- <span class="cline-any cline-no">&nbsp;</span>
561
- <span class="cline-any cline-no">&nbsp;</span>
562
- <span class="cline-any cline-no">&nbsp;</span>
563
- <span class="cline-any cline-no">&nbsp;</span>
564
- <span class="cline-any cline-no">&nbsp;</span>
565
- <span class="cline-any cline-no">&nbsp;</span>
566
- <span class="cline-any cline-no">&nbsp;</span>
567
- <span class="cline-any cline-no">&nbsp;</span>
568
- <span class="cline-any cline-no">&nbsp;</span>
569
- <span class="cline-any cline-no">&nbsp;</span>
570
- <span class="cline-any cline-no">&nbsp;</span>
571
- <span class="cline-any cline-no">&nbsp;</span>
572
- <span class="cline-any cline-no">&nbsp;</span>
573
- <span class="cline-any cline-no">&nbsp;</span>
574
- <span class="cline-any cline-no">&nbsp;</span>
575
- <span class="cline-any cline-no">&nbsp;</span>
576
- <span class="cline-any cline-no">&nbsp;</span>
577
- <span class="cline-any cline-no">&nbsp;</span>
578
- <span class="cline-any cline-no">&nbsp;</span>
579
- <span class="cline-any cline-no">&nbsp;</span>
580
- <span class="cline-any cline-no">&nbsp;</span>
581
- <span class="cline-any cline-no">&nbsp;</span>
582
- <span class="cline-any cline-no">&nbsp;</span>
583
- <span class="cline-any cline-no">&nbsp;</span>
584
- <span class="cline-any cline-no">&nbsp;</span>
585
- <span class="cline-any cline-no">&nbsp;</span>
586
- <span class="cline-any cline-no">&nbsp;</span>
587
- <span class="cline-any cline-no">&nbsp;</span>
588
- <span class="cline-any cline-no">&nbsp;</span>
589
- <span class="cline-any cline-no">&nbsp;</span>
590
- <span class="cline-any cline-no">&nbsp;</span>
591
- <span class="cline-any cline-no">&nbsp;</span>
592
- <span class="cline-any cline-no">&nbsp;</span>
593
- <span class="cline-any cline-no">&nbsp;</span>
594
- <span class="cline-any cline-no">&nbsp;</span>
595
- <span class="cline-any cline-no">&nbsp;</span>
596
- <span class="cline-any cline-no">&nbsp;</span>
597
- <span class="cline-any cline-no">&nbsp;</span>
598
- <span class="cline-any cline-no">&nbsp;</span>
599
- <span class="cline-any cline-no">&nbsp;</span>
600
- <span class="cline-any cline-no">&nbsp;</span>
601
- <span class="cline-any cline-no">&nbsp;</span>
602
- <span class="cline-any cline-no">&nbsp;</span>
603
- <span class="cline-any cline-no">&nbsp;</span>
604
- <span class="cline-any cline-no">&nbsp;</span>
605
- <span class="cline-any cline-no">&nbsp;</span>
606
- <span class="cline-any cline-no">&nbsp;</span>
607
- <span class="cline-any cline-no">&nbsp;</span>
608
- <span class="cline-any cline-no">&nbsp;</span>
609
- <span class="cline-any cline-no">&nbsp;</span>
610
- <span class="cline-any cline-no">&nbsp;</span>
611
- <span class="cline-any cline-no">&nbsp;</span>
612
- <span class="cline-any cline-no">&nbsp;</span>
613
- <span class="cline-any cline-no">&nbsp;</span>
614
- <span class="cline-any cline-no">&nbsp;</span>
615
- <span class="cline-any cline-no">&nbsp;</span>
616
- <span class="cline-any cline-no">&nbsp;</span>
617
- <span class="cline-any cline-no">&nbsp;</span>
618
- <span class="cline-any cline-no">&nbsp;</span>
619
- <span class="cline-any cline-no">&nbsp;</span>
620
- <span class="cline-any cline-no">&nbsp;</span>
621
- <span class="cline-any cline-no">&nbsp;</span>
622
- <span class="cline-any cline-no">&nbsp;</span>
623
- <span class="cline-any cline-no">&nbsp;</span>
624
- <span class="cline-any cline-no">&nbsp;</span>
625
- <span class="cline-any cline-no">&nbsp;</span>
626
- <span class="cline-any cline-no">&nbsp;</span>
627
- <span class="cline-any cline-no">&nbsp;</span>
628
- <span class="cline-any cline-no">&nbsp;</span>
629
- <span class="cline-any cline-no">&nbsp;</span>
630
- <span class="cline-any cline-no">&nbsp;</span>
631
- <span class="cline-any cline-no">&nbsp;</span>
632
- <span class="cline-any cline-no">&nbsp;</span>
633
- <span class="cline-any cline-no">&nbsp;</span>
634
- <span class="cline-any cline-no">&nbsp;</span>
635
- <span class="cline-any cline-no">&nbsp;</span>
636
- <span class="cline-any cline-no">&nbsp;</span>
637
- <span class="cline-any cline-no">&nbsp;</span>
638
- <span class="cline-any cline-no">&nbsp;</span>
639
- <span class="cline-any cline-no">&nbsp;</span>
640
- <span class="cline-any cline-no">&nbsp;</span>
641
- <span class="cline-any cline-no">&nbsp;</span>
642
- <span class="cline-any cline-no">&nbsp;</span>
643
- <span class="cline-any cline-no">&nbsp;</span>
644
- <span class="cline-any cline-no">&nbsp;</span>
645
- <span class="cline-any cline-no">&nbsp;</span>
646
- <span class="cline-any cline-no">&nbsp;</span>
647
- <span class="cline-any cline-no">&nbsp;</span>
648
- <span class="cline-any cline-no">&nbsp;</span>
649
- <span class="cline-any cline-no">&nbsp;</span>
650
- <span class="cline-any cline-no">&nbsp;</span>
651
- <span class="cline-any cline-no">&nbsp;</span>
652
- <span class="cline-any cline-no">&nbsp;</span>
653
- <span class="cline-any cline-no">&nbsp;</span>
654
- <span class="cline-any cline-no">&nbsp;</span>
655
- <span class="cline-any cline-no">&nbsp;</span>
656
- <span class="cline-any cline-no">&nbsp;</span>
657
- <span class="cline-any cline-no">&nbsp;</span>
658
- <span class="cline-any cline-no">&nbsp;</span>
659
- <span class="cline-any cline-no">&nbsp;</span>
660
- <span class="cline-any cline-no">&nbsp;</span>
661
- <span class="cline-any cline-no">&nbsp;</span>
662
- <span class="cline-any cline-no">&nbsp;</span>
663
- <span class="cline-any cline-no">&nbsp;</span>
664
- <span class="cline-any cline-no">&nbsp;</span>
665
- <span class="cline-any cline-no">&nbsp;</span>
666
- <span class="cline-any cline-no">&nbsp;</span>
667
- <span class="cline-any cline-no">&nbsp;</span>
668
- <span class="cline-any cline-no">&nbsp;</span>
669
- <span class="cline-any cline-no">&nbsp;</span>
670
- <span class="cline-any cline-no">&nbsp;</span>
671
- <span class="cline-any cline-no">&nbsp;</span>
672
- <span class="cline-any cline-no">&nbsp;</span>
673
- <span class="cline-any cline-no">&nbsp;</span>
674
- <span class="cline-any cline-no">&nbsp;</span>
675
- <span class="cline-any cline-no">&nbsp;</span>
676
- <span class="cline-any cline-no">&nbsp;</span>
677
- <span class="cline-any cline-no">&nbsp;</span>
678
- <span class="cline-any cline-no">&nbsp;</span>
679
- <span class="cline-any cline-no">&nbsp;</span>
680
- <span class="cline-any cline-no">&nbsp;</span>
681
- <span class="cline-any cline-no">&nbsp;</span>
682
- <span class="cline-any cline-no">&nbsp;</span>
683
- <span class="cline-any cline-no">&nbsp;</span>
684
- <span class="cline-any cline-no">&nbsp;</span>
685
- <span class="cline-any cline-no">&nbsp;</span>
686
- <span class="cline-any cline-no">&nbsp;</span>
687
- <span class="cline-any cline-no">&nbsp;</span>
688
- <span class="cline-any cline-no">&nbsp;</span>
689
- <span class="cline-any cline-no">&nbsp;</span>
690
- <span class="cline-any cline-no">&nbsp;</span>
691
- <span class="cline-any cline-no">&nbsp;</span>
692
- <span class="cline-any cline-no">&nbsp;</span>
693
- <span class="cline-any cline-no">&nbsp;</span>
694
- <span class="cline-any cline-no">&nbsp;</span>
695
- <span class="cline-any cline-no">&nbsp;</span>
696
- <span class="cline-any cline-no">&nbsp;</span>
697
- <span class="cline-any cline-no">&nbsp;</span>
698
- <span class="cline-any cline-no">&nbsp;</span>
699
- <span class="cline-any cline-no">&nbsp;</span>
700
- <span class="cline-any cline-no">&nbsp;</span>
701
- <span class="cline-any cline-no">&nbsp;</span>
702
- <span class="cline-any cline-no">&nbsp;</span>
703
- <span class="cline-any cline-no">&nbsp;</span>
704
- <span class="cline-any cline-no">&nbsp;</span>
705
- <span class="cline-any cline-no">&nbsp;</span>
706
- <span class="cline-any cline-no">&nbsp;</span>
707
- <span class="cline-any cline-no">&nbsp;</span>
708
- <span class="cline-any cline-no">&nbsp;</span>
709
- <span class="cline-any cline-no">&nbsp;</span>
710
- <span class="cline-any cline-no">&nbsp;</span>
711
- <span class="cline-any cline-no">&nbsp;</span>
712
- <span class="cline-any cline-no">&nbsp;</span>
713
- <span class="cline-any cline-no">&nbsp;</span>
714
- <span class="cline-any cline-no">&nbsp;</span>
715
- <span class="cline-any cline-no">&nbsp;</span>
716
- <span class="cline-any cline-no">&nbsp;</span>
717
- <span class="cline-any cline-no">&nbsp;</span>
718
- <span class="cline-any cline-no">&nbsp;</span>
719
- <span class="cline-any cline-no">&nbsp;</span>
720
- <span class="cline-any cline-no">&nbsp;</span>
721
- <span class="cline-any cline-no">&nbsp;</span>
722
- <span class="cline-any cline-no">&nbsp;</span>
723
- <span class="cline-any cline-no">&nbsp;</span>
724
- <span class="cline-any cline-no">&nbsp;</span>
725
- <span class="cline-any cline-no">&nbsp;</span>
726
- <span class="cline-any cline-no">&nbsp;</span>
727
- <span class="cline-any cline-no">&nbsp;</span>
728
- <span class="cline-any cline-no">&nbsp;</span>
729
- <span class="cline-any cline-no">&nbsp;</span>
730
- <span class="cline-any cline-no">&nbsp;</span>
731
- <span class="cline-any cline-no">&nbsp;</span>
732
- <span class="cline-any cline-no">&nbsp;</span>
733
- <span class="cline-any cline-no">&nbsp;</span>
734
- <span class="cline-any cline-no">&nbsp;</span>
735
- <span class="cline-any cline-no">&nbsp;</span>
736
- <span class="cline-any cline-no">&nbsp;</span>
737
- <span class="cline-any cline-no">&nbsp;</span>
738
- <span class="cline-any cline-no">&nbsp;</span>
739
- <span class="cline-any cline-no">&nbsp;</span>
740
- <span class="cline-any cline-no">&nbsp;</span>
741
- <span class="cline-any cline-no">&nbsp;</span>
742
- <span class="cline-any cline-no">&nbsp;</span>
743
- <span class="cline-any cline-no">&nbsp;</span>
744
- <span class="cline-any cline-no">&nbsp;</span>
745
- <span class="cline-any cline-no">&nbsp;</span>
746
- <span class="cline-any cline-no">&nbsp;</span>
747
- <span class="cline-any cline-no">&nbsp;</span>
748
- <span class="cline-any cline-no">&nbsp;</span>
749
- <span class="cline-any cline-no">&nbsp;</span>
750
- <span class="cline-any cline-no">&nbsp;</span>
751
- <span class="cline-any cline-no">&nbsp;</span>
752
- <span class="cline-any cline-no">&nbsp;</span>
753
- <span class="cline-any cline-no">&nbsp;</span>
754
- <span class="cline-any cline-no">&nbsp;</span>
755
- <span class="cline-any cline-no">&nbsp;</span>
756
- <span class="cline-any cline-no">&nbsp;</span>
757
- <span class="cline-any cline-no">&nbsp;</span>
758
- <span class="cline-any cline-no">&nbsp;</span>
759
- <span class="cline-any cline-no">&nbsp;</span>
760
- <span class="cline-any cline-no">&nbsp;</span>
761
- <span class="cline-any cline-no">&nbsp;</span>
762
- <span class="cline-any cline-no">&nbsp;</span>
763
- <span class="cline-any cline-no">&nbsp;</span>
764
- <span class="cline-any cline-no">&nbsp;</span>
765
- <span class="cline-any cline-no">&nbsp;</span>
766
- <span class="cline-any cline-no">&nbsp;</span>
767
- <span class="cline-any cline-no">&nbsp;</span>
768
- <span class="cline-any cline-no">&nbsp;</span>
769
- <span class="cline-any cline-no">&nbsp;</span>
770
- <span class="cline-any cline-no">&nbsp;</span>
771
- <span class="cline-any cline-no">&nbsp;</span>
772
- <span class="cline-any cline-no">&nbsp;</span>
773
- <span class="cline-any cline-no">&nbsp;</span>
774
- <span class="cline-any cline-no">&nbsp;</span>
775
- <span class="cline-any cline-no">&nbsp;</span>
776
- <span class="cline-any cline-no">&nbsp;</span>
777
- <span class="cline-any cline-no">&nbsp;</span>
778
- <span class="cline-any cline-no">&nbsp;</span>
779
- <span class="cline-any cline-no">&nbsp;</span>
780
- <span class="cline-any cline-no">&nbsp;</span>
781
- <span class="cline-any cline-no">&nbsp;</span>
782
- <span class="cline-any cline-no">&nbsp;</span>
783
- <span class="cline-any cline-no">&nbsp;</span>
784
- <span class="cline-any cline-no">&nbsp;</span>
785
- <span class="cline-any cline-no">&nbsp;</span>
786
- <span class="cline-any cline-no">&nbsp;</span>
787
- <span class="cline-any cline-no">&nbsp;</span>
788
- <span class="cline-any cline-no">&nbsp;</span>
789
- <span class="cline-any cline-no">&nbsp;</span>
790
- <span class="cline-any cline-no">&nbsp;</span>
791
- <span class="cline-any cline-no">&nbsp;</span>
792
- <span class="cline-any cline-no">&nbsp;</span>
793
- <span class="cline-any cline-no">&nbsp;</span>
794
- <span class="cline-any cline-no">&nbsp;</span>
795
- <span class="cline-any cline-no">&nbsp;</span>
796
- <span class="cline-any cline-no">&nbsp;</span>
797
- <span class="cline-any cline-no">&nbsp;</span>
798
- <span class="cline-any cline-no">&nbsp;</span>
799
- <span class="cline-any cline-no">&nbsp;</span>
800
- <span class="cline-any cline-no">&nbsp;</span>
801
- <span class="cline-any cline-no">&nbsp;</span>
802
- <span class="cline-any cline-no">&nbsp;</span>
803
- <span class="cline-any cline-no">&nbsp;</span>
804
- <span class="cline-any cline-no">&nbsp;</span>
805
- <span class="cline-any cline-no">&nbsp;</span>
806
- <span class="cline-any cline-no">&nbsp;</span>
807
- <span class="cline-any cline-no">&nbsp;</span>
808
- <span class="cline-any cline-no">&nbsp;</span>
809
- <span class="cline-any cline-no">&nbsp;</span>
810
- <span class="cline-any cline-no">&nbsp;</span>
811
- <span class="cline-any cline-no">&nbsp;</span>
812
- <span class="cline-any cline-no">&nbsp;</span>
813
- <span class="cline-any cline-no">&nbsp;</span>
814
- <span class="cline-any cline-no">&nbsp;</span>
815
- <span class="cline-any cline-no">&nbsp;</span>
816
- <span class="cline-any cline-no">&nbsp;</span>
817
- <span class="cline-any cline-no">&nbsp;</span>
818
- <span class="cline-any cline-no">&nbsp;</span>
819
- <span class="cline-any cline-no">&nbsp;</span>
820
- <span class="cline-any cline-no">&nbsp;</span>
821
- <span class="cline-any cline-no">&nbsp;</span>
822
- <span class="cline-any cline-no">&nbsp;</span>
823
- <span class="cline-any cline-no">&nbsp;</span>
824
- <span class="cline-any cline-no">&nbsp;</span>
825
- <span class="cline-any cline-no">&nbsp;</span>
826
- <span class="cline-any cline-no">&nbsp;</span>
827
- <span class="cline-any cline-no">&nbsp;</span>
828
- <span class="cline-any cline-no">&nbsp;</span>
829
- <span class="cline-any cline-no">&nbsp;</span>
830
- <span class="cline-any cline-no">&nbsp;</span>
831
- <span class="cline-any cline-no">&nbsp;</span>
832
- <span class="cline-any cline-no">&nbsp;</span>
833
- <span class="cline-any cline-no">&nbsp;</span>
834
- <span class="cline-any cline-no">&nbsp;</span>
835
- <span class="cline-any cline-no">&nbsp;</span>
836
- <span class="cline-any cline-no">&nbsp;</span>
837
- <span class="cline-any cline-no">&nbsp;</span>
838
- <span class="cline-any cline-no">&nbsp;</span>
839
- <span class="cline-any cline-no">&nbsp;</span>
840
- <span class="cline-any cline-no">&nbsp;</span>
841
- <span class="cline-any cline-no">&nbsp;</span>
842
- <span class="cline-any cline-no">&nbsp;</span>
843
- <span class="cline-any cline-no">&nbsp;</span>
844
- <span class="cline-any cline-no">&nbsp;</span>
845
- <span class="cline-any cline-no">&nbsp;</span>
846
- <span class="cline-any cline-no">&nbsp;</span>
847
- <span class="cline-any cline-no">&nbsp;</span>
848
- <span class="cline-any cline-no">&nbsp;</span>
849
- <span class="cline-any cline-no">&nbsp;</span>
850
- <span class="cline-any cline-no">&nbsp;</span>
851
- <span class="cline-any cline-no">&nbsp;</span>
852
- <span class="cline-any cline-no">&nbsp;</span>
853
- <span class="cline-any cline-no">&nbsp;</span>
854
- <span class="cline-any cline-no">&nbsp;</span>
855
- <span class="cline-any cline-no">&nbsp;</span>
856
- <span class="cline-any cline-no">&nbsp;</span>
857
- <span class="cline-any cline-no">&nbsp;</span>
858
- <span class="cline-any cline-no">&nbsp;</span>
859
- <span class="cline-any cline-no">&nbsp;</span>
860
- <span class="cline-any cline-no">&nbsp;</span>
861
- <span class="cline-any cline-no">&nbsp;</span>
862
- <span class="cline-any cline-no">&nbsp;</span>
863
- <span class="cline-any cline-no">&nbsp;</span>
864
- <span class="cline-any cline-no">&nbsp;</span>
865
- <span class="cline-any cline-no">&nbsp;</span>
866
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" >import _ from 'lodash'</span></span></span>
867
- <span class="cstat-no" title="statement not covered" >import { marshallGeometry } from '../marshall.js'</span>
868
- <span class="cstat-no" title="statement not covered" >import makeDebug from 'debug'</span>
869
- <span class="cstat-no" title="statement not covered" ></span>
870
- <span class="cstat-no" title="statement not covered" >const debug = makeDebug('kdk:map:query:hooks')</span>
871
- <span class="cstat-no" title="statement not covered" ></span>
872
- <span class="cstat-no" title="statement not covered" >export function marshallGeometryQuery (hook) {</span>
873
- <span class="cstat-no" title="statement not covered" > const query = hook.params.query</span>
874
- <span class="cstat-no" title="statement not covered" > if (_.isNil(query)) return</span>
875
- <span class="cstat-no" title="statement not covered" > const geometry = query.geometry</span>
876
- <span class="cstat-no" title="statement not covered" > if (_.isNil(geometry)) return</span>
877
- <span class="cstat-no" title="statement not covered" > marshallGeometry(geometry)</span>
878
- <span class="cstat-no" title="statement not covered" >}</span>
879
- <span class="cstat-no" title="statement not covered" ></span>
880
- <span class="cstat-no" title="statement not covered" >function getGeometryQueryForBBox (bbox) {</span>
881
- <span class="cstat-no" title="statement not covered" > // Here we use the custom MongoDB CRS that enforces counter-clockwise winding order</span>
882
- <span class="cstat-no" title="statement not covered" > // and allows to support queries with a single-ringed GeoJSON polygon</span>
883
- <span class="cstat-no" title="statement not covered" > // whose area is greater than or equal to a single hemisphere.</span>
884
- <span class="cstat-no" title="statement not covered" > // Otherwise $geoIntersects queries for the complementary geometry.</span>
885
- <span class="cstat-no" title="statement not covered" > return {</span>
886
- <span class="cstat-no" title="statement not covered" > $geoIntersects: {</span>
887
- <span class="cstat-no" title="statement not covered" > $geometry: {</span>
888
- <span class="cstat-no" title="statement not covered" > type: 'Polygon',</span>
889
- <span class="cstat-no" title="statement not covered" > coordinates: [bbox],</span>
890
- <span class="cstat-no" title="statement not covered" > crs: {</span>
891
- <span class="cstat-no" title="statement not covered" > type: 'name',</span>
892
- <span class="cstat-no" title="statement not covered" > properties: { name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' }</span>
893
- <span class="cstat-no" title="statement not covered" > }</span>
894
- <span class="cstat-no" title="statement not covered" > }</span>
895
- <span class="cstat-no" title="statement not covered" > }</span>
896
- <span class="cstat-no" title="statement not covered" > }</span>
897
- <span class="cstat-no" title="statement not covered" >}</span>
898
- <span class="cstat-no" title="statement not covered" ></span>
899
- <span class="cstat-no" title="statement not covered" >export function marshallGeoJsonQuery (hook) {</span>
900
- <span class="cstat-no" title="statement not covered" > const query = hook.params.query</span>
901
- <span class="cstat-no" title="statement not covered" > if (query) {</span>
902
- <span class="cstat-no" title="statement not covered" > if (query.geoJson) {</span>
903
- <span class="cstat-no" title="statement not covered" > delete query.geoJson</span>
904
- <span class="cstat-no" title="statement not covered" > hook.params.asGeoJson = true</span>
905
- <span class="cstat-no" title="statement not covered" > }</span>
906
- <span class="cstat-no" title="statement not covered" > }</span>
907
- <span class="cstat-no" title="statement not covered" >}</span>
908
- <span class="cstat-no" title="statement not covered" ></span>
909
- <span class="cstat-no" title="statement not covered" >export function marshallSpatialQuery (hook) {</span>
910
- <span class="cstat-no" title="statement not covered" > const query = hook.params.query</span>
911
- <span class="cstat-no" title="statement not covered" > if (query) {</span>
912
- <span class="cstat-no" title="statement not covered" > if (!_.isNil(query.geometry)) marshallGeometry(query.geometry)</span>
913
- <span class="cstat-no" title="statement not covered" > // Shortcut for proximity query</span>
914
- <span class="cstat-no" title="statement not covered" > if ((!_.isNil(query.centerLon) || !_.isNil(query.longitude)) &amp;&amp;</span>
915
- <span class="cstat-no" title="statement not covered" > (!_.isNil(query.centerLat) || !_.isNil(query.latitude)) &amp;&amp; !_.isNil(query.distance)) {</span>
916
- <span class="cstat-no" title="statement not covered" > const longitude = (_.isNil(query.centerLon) ? _.toNumber(query.longitude) : _.toNumber(query.centerLon))</span>
917
- <span class="cstat-no" title="statement not covered" > const latitude = (_.isNil(query.centerLat) ? _.toNumber(query.latitude) : _.toNumber(query.centerLat))</span>
918
- <span class="cstat-no" title="statement not covered" > const distance = _.toNumber(query.distance)</span>
919
- <span class="cstat-no" title="statement not covered" > // Transform to MongoDB spatial request</span>
920
- <span class="cstat-no" title="statement not covered" > delete query.centerLon</span>
921
- <span class="cstat-no" title="statement not covered" > delete query.longitude</span>
922
- <span class="cstat-no" title="statement not covered" > delete query.centerLat</span>
923
- <span class="cstat-no" title="statement not covered" > delete query.latitude</span>
924
- <span class="cstat-no" title="statement not covered" > delete query.distance</span>
925
- <span class="cstat-no" title="statement not covered" > // Aggregation requires a specific operator</span>
926
- <span class="cstat-no" title="statement not covered" > if (query.$aggregate) {</span>
466
+ <a name='L401'></a><a href='#L401'>401</a>
467
+ <a name='L402'></a><a href='#L402'>402</a>
468
+ <a name='L403'></a><a href='#L403'>403</a>
469
+ <a name='L404'></a><a href='#L404'>404</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
470
+ <span class="cline-any cline-yes">1x</span>
471
+ <span class="cline-any cline-yes">1x</span>
472
+ <span class="cline-any cline-yes">1x</span>
473
+ <span class="cline-any cline-yes">1x</span>
474
+ <span class="cline-any cline-yes">1x</span>
475
+ <span class="cline-any cline-yes">1x</span>
476
+ <span class="cline-any cline-yes">2x</span>
477
+ <span class="cline-any cline-yes">2x</span>
478
+ <span class="cline-any cline-yes">2x</span>
479
+ <span class="cline-any cline-yes">2x</span>
480
+ <span class="cline-any cline-yes">2x</span>
481
+ <span class="cline-any cline-yes">2x</span>
482
+ <span class="cline-any cline-yes">1x</span>
483
+ <span class="cline-any cline-yes">10x</span>
484
+ <span class="cline-any cline-yes">10x</span>
485
+ <span class="cline-any cline-yes">10x</span>
486
+ <span class="cline-any cline-yes">10x</span>
487
+ <span class="cline-any cline-yes">10x</span>
488
+ <span class="cline-any cline-yes">10x</span>
489
+ <span class="cline-any cline-yes">10x</span>
490
+ <span class="cline-any cline-yes">10x</span>
491
+ <span class="cline-any cline-yes">10x</span>
492
+ <span class="cline-any cline-yes">10x</span>
493
+ <span class="cline-any cline-yes">10x</span>
494
+ <span class="cline-any cline-yes">10x</span>
495
+ <span class="cline-any cline-yes">10x</span>
496
+ <span class="cline-any cline-yes">10x</span>
497
+ <span class="cline-any cline-yes">10x</span>
498
+ <span class="cline-any cline-yes">10x</span>
499
+ <span class="cline-any cline-yes">10x</span>
500
+ <span class="cline-any cline-yes">10x</span>
501
+ <span class="cline-any cline-yes">1x</span>
502
+ <span class="cline-any cline-yes">1x</span>
503
+ <span class="cline-any cline-yes">55x</span>
504
+ <span class="cline-any cline-yes">55x</span>
505
+ <span class="cline-any cline-yes">54x</span>
506
+ <span class="cline-any cline-yes">1x</span>
507
+ <span class="cline-any cline-yes">1x</span>
508
+ <span class="cline-any cline-yes">1x</span>
509
+ <span class="cline-any cline-yes">54x</span>
510
+ <span class="cline-any cline-yes">55x</span>
511
+ <span class="cline-any cline-yes">1x</span>
512
+ <span class="cline-any cline-yes">1x</span>
513
+ <span class="cline-any cline-yes">55x</span>
514
+ <span class="cline-any cline-yes">55x</span>
515
+ <span class="cline-any cline-yes">54x</span>
516
+ <span class="cline-any cline-yes">54x</span>
517
+ <span class="cline-any cline-yes">54x</span>
518
+ <span class="cline-any cline-yes">54x</span>
519
+ <span class="cline-any cline-yes">1x</span>
520
+ <span class="cline-any cline-yes">1x</span>
521
+ <span class="cline-any cline-yes">1x</span>
522
+ <span class="cline-any cline-yes">1x</span>
523
+ <span class="cline-any cline-yes">1x</span>
524
+ <span class="cline-any cline-yes">1x</span>
525
+ <span class="cline-any cline-yes">1x</span>
526
+ <span class="cline-any cline-yes">1x</span>
527
+ <span class="cline-any cline-yes">1x</span>
528
+ <span class="cline-any cline-yes">1x</span>
529
+ <span class="cline-any cline-yes">1x</span>
530
+ <span class="cline-any cline-no">&nbsp;</span>
531
+ <span class="cline-any cline-no">&nbsp;</span>
532
+ <span class="cline-any cline-no">&nbsp;</span>
533
+ <span class="cline-any cline-no">&nbsp;</span>
534
+ <span class="cline-any cline-no">&nbsp;</span>
535
+ <span class="cline-any cline-no">&nbsp;</span>
536
+ <span class="cline-any cline-yes">1x</span>
537
+ <span class="cline-any cline-yes">1x</span>
538
+ <span class="cline-any cline-yes">1x</span>
539
+ <span class="cline-any cline-yes">1x</span>
540
+ <span class="cline-any cline-yes">1x</span>
541
+ <span class="cline-any cline-yes">1x</span>
542
+ <span class="cline-any cline-yes">1x</span>
543
+ <span class="cline-any cline-yes">1x</span>
544
+ <span class="cline-any cline-yes">1x</span>
545
+ <span class="cline-any cline-yes">1x</span>
546
+ <span class="cline-any cline-yes">1x</span>
547
+ <span class="cline-any cline-yes">1x</span>
548
+ <span class="cline-any cline-yes">1x</span>
549
+ <span class="cline-any cline-yes">1x</span>
550
+ <span class="cline-any cline-yes">1x</span>
551
+ <span class="cline-any cline-yes">1x</span>
552
+ <span class="cline-any cline-yes">1x</span>
553
+ <span class="cline-any cline-yes">1x</span>
554
+ <span class="cline-any cline-yes">1x</span>
555
+ <span class="cline-any cline-yes">54x</span>
556
+ <span class="cline-any cline-yes">54x</span>
557
+ <span class="cline-any cline-yes">6x</span>
558
+ <span class="cline-any cline-yes">6x</span>
559
+ <span class="cline-any cline-yes">6x</span>
560
+ <span class="cline-any cline-yes">6x</span>
561
+ <span class="cline-any cline-yes">6x</span>
562
+ <span class="cline-any cline-yes">6x</span>
563
+ <span class="cline-any cline-yes">6x</span>
564
+ <span class="cline-any cline-yes">6x</span>
565
+ <span class="cline-any cline-yes">6x</span>
566
+ <span class="cline-any cline-yes">6x</span>
567
+ <span class="cline-any cline-yes">6x</span>
568
+ <span class="cline-any cline-yes">6x</span>
569
+ <span class="cline-any cline-yes">6x</span>
570
+ <span class="cline-any cline-yes">6x</span>
571
+ <span class="cline-any cline-yes">2x</span>
572
+ <span class="cline-any cline-yes">2x</span>
573
+ <span class="cline-any cline-yes">2x</span>
574
+ <span class="cline-any cline-yes">6x</span>
575
+ <span class="cline-any cline-yes">4x</span>
576
+ <span class="cline-any cline-yes">4x</span>
577
+ <span class="cline-any cline-yes">4x</span>
578
+ <span class="cline-any cline-yes">4x</span>
579
+ <span class="cline-any cline-yes">4x</span>
580
+ <span class="cline-any cline-yes">6x</span>
581
+ <span class="cline-any cline-yes">54x</span>
582
+ <span class="cline-any cline-yes">54x</span>
583
+ <span class="cline-any cline-yes">3x</span>
584
+ <span class="cline-any cline-yes">3x</span>
585
+ <span class="cline-any cline-yes">3x</span>
586
+ <span class="cline-any cline-yes">3x</span>
587
+ <span class="cline-any cline-yes">3x</span>
588
+ <span class="cline-any cline-yes">3x</span>
589
+ <span class="cline-any cline-yes">3x</span>
590
+ <span class="cline-any cline-yes">3x</span>
591
+ <span class="cline-any cline-yes">3x</span>
592
+ <span class="cline-any cline-yes">3x</span>
593
+ <span class="cline-any cline-yes">3x</span>
594
+ <span class="cline-any cline-yes">3x</span>
595
+ <span class="cline-any cline-yes">3x</span>
596
+ <span class="cline-any cline-yes">3x</span>
597
+ <span class="cline-any cline-yes">3x</span>
598
+ <span class="cline-any cline-yes">54x</span>
599
+ <span class="cline-any cline-yes">55x</span>
600
+ <span class="cline-any cline-yes">55x</span>
601
+ <span class="cline-any cline-yes">55x</span>
602
+ <span class="cline-any cline-yes">1x</span>
603
+ <span class="cline-any cline-yes">1x</span>
604
+ <span class="cline-any cline-yes">3x</span>
605
+ <span class="cline-any cline-yes">38x</span>
606
+ <span class="cline-any cline-yes">38x</span>
607
+ <span class="cline-any cline-yes">38x</span>
608
+ <span class="cline-any cline-yes">38x</span>
609
+ <span class="cline-any cline-yes">38x</span>
610
+ <span class="cline-any cline-yes">38x</span>
611
+ <span class="cline-any cline-yes">38x</span>
612
+ <span class="cline-any cline-yes">38x</span>
613
+ <span class="cline-any cline-yes">38x</span>
614
+ <span class="cline-any cline-yes">38x</span>
615
+ <span class="cline-any cline-yes">38x</span>
616
+ <span class="cline-any cline-yes">38x</span>
617
+ <span class="cline-any cline-yes">37x</span>
618
+ <span class="cline-any cline-yes">38x</span>
619
+ <span class="cline-any cline-yes">38x</span>
620
+ <span class="cline-any cline-yes">38x</span>
621
+ <span class="cline-any cline-yes">38x</span>
622
+ <span class="cline-any cline-yes">38x</span>
623
+ <span class="cline-any cline-yes">37x</span>
624
+ <span class="cline-any cline-yes">37x</span>
625
+ <span class="cline-any cline-yes">620x</span>
626
+ <span class="cline-any cline-yes">620x</span>
627
+ <span class="cline-any cline-yes">616x</span>
628
+ <span class="cline-any cline-yes">620x</span>
629
+ <span class="cline-any cline-yes">613x</span>
630
+ <span class="cline-any cline-yes">620x</span>
631
+ <span class="cline-any cline-yes">620x</span>
632
+ <span class="cline-any cline-yes">2x</span>
633
+ <span class="cline-any cline-yes">2x</span>
634
+ <span class="cline-any cline-yes">37x</span>
635
+ <span class="cline-any cline-yes">37x</span>
636
+ <span class="cline-any cline-yes">619x</span>
637
+ <span class="cline-any cline-yes">619x</span>
638
+ <span class="cline-any cline-yes">619x</span>
639
+ <span class="cline-any cline-yes">4x</span>
640
+ <span class="cline-any cline-yes">4x</span>
641
+ <span class="cline-any cline-no">&nbsp;</span>
642
+ <span class="cline-any cline-no">&nbsp;</span>
643
+ <span class="cline-any cline-yes">4x</span>
644
+ <span class="cline-any cline-yes">619x</span>
645
+ <span class="cline-any cline-yes">2x</span>
646
+ <span class="cline-any cline-yes">2x</span>
647
+ <span class="cline-any cline-yes">619x</span>
648
+ <span class="cline-any cline-yes">3x</span>
649
+ <span class="cline-any cline-yes">3x</span>
650
+ <span class="cline-any cline-yes">619x</span>
651
+ <span class="cline-any cline-yes">619x</span>
652
+ <span class="cline-any cline-yes">619x</span>
653
+ <span class="cline-any cline-yes">611x</span>
654
+ <span class="cline-any cline-yes">611x</span>
655
+ <span class="cline-any cline-yes">611x</span>
656
+ <span class="cline-any cline-yes">611x</span>
657
+ <span class="cline-any cline-yes">611x</span>
658
+ <span class="cline-any cline-yes">619x</span>
659
+ <span class="cline-any cline-yes">3x</span>
660
+ <span class="cline-any cline-yes">3x</span>
661
+ <span class="cline-any cline-yes">3x</span>
662
+ <span class="cline-any cline-yes">3x</span>
663
+ <span class="cline-any cline-yes">3x</span>
664
+ <span class="cline-any cline-yes">8x</span>
665
+ <span class="cline-any cline-yes">5x</span>
666
+ <span class="cline-any cline-yes">5x</span>
667
+ <span class="cline-any cline-yes">5x</span>
668
+ <span class="cline-any cline-yes">5x</span>
669
+ <span class="cline-any cline-yes">5x</span>
670
+ <span class="cline-any cline-yes">5x</span>
671
+ <span class="cline-any cline-yes">5x</span>
672
+ <span class="cline-any cline-yes">37x</span>
673
+ <span class="cline-any cline-yes">37x</span>
674
+ <span class="cline-any cline-yes">38x</span>
675
+ <span class="cline-any cline-yes">2x</span>
676
+ <span class="cline-any cline-yes">5x</span>
677
+ <span class="cline-any cline-yes">5x</span>
678
+ <span class="cline-any cline-yes">5x</span>
679
+ <span class="cline-any cline-yes">20x</span>
680
+ <span class="cline-any cline-yes">10x</span>
681
+ <span class="cline-any cline-yes">10x</span>
682
+ <span class="cline-any cline-yes">5x</span>
683
+ <span class="cline-any cline-yes">5x</span>
684
+ <span class="cline-any cline-no">&nbsp;</span>
685
+ <span class="cline-any cline-no">&nbsp;</span>
686
+ <span class="cline-any cline-no">&nbsp;</span>
687
+ <span class="cline-any cline-no">&nbsp;</span>
688
+ <span class="cline-any cline-no">&nbsp;</span>
689
+ <span class="cline-any cline-yes">2x</span>
690
+ <span class="cline-any cline-yes">2x</span>
691
+ <span class="cline-any cline-yes">37x</span>
692
+ <span class="cline-any cline-yes">38x</span>
693
+ <span class="cline-any cline-yes">36x</span>
694
+ <span class="cline-any cline-yes">36x</span>
695
+ <span class="cline-any cline-yes">36x</span>
696
+ <span class="cline-any cline-yes">36x</span>
697
+ <span class="cline-any cline-yes">36x</span>
698
+ <span class="cline-any cline-yes">38x</span>
699
+ <span class="cline-any cline-no">&nbsp;</span>
700
+ <span class="cline-any cline-no">&nbsp;</span>
701
+ <span class="cline-any cline-no">&nbsp;</span>
702
+ <span class="cline-any cline-no">&nbsp;</span>
703
+ <span class="cline-any cline-yes">1x</span>
704
+ <span class="cline-any cline-yes">1x</span>
705
+ <span class="cline-any cline-yes">1x</span>
706
+ <span class="cline-any cline-yes">38x</span>
707
+ <span class="cline-any cline-yes">3x</span>
708
+ <span class="cline-any cline-yes">1x</span>
709
+ <span class="cline-any cline-yes">1x</span>
710
+ <span class="cline-any cline-yes">36x</span>
711
+ <span class="cline-any cline-yes">36x</span>
712
+ <span class="cline-any cline-yes">36x</span>
713
+ <span class="cline-any cline-yes">36x</span>
714
+ <span class="cline-any cline-yes">36x</span>
715
+ <span class="cline-any cline-yes">8x</span>
716
+ <span class="cline-any cline-yes">8x</span>
717
+ <span class="cline-any cline-yes">8x</span>
718
+ <span class="cline-any cline-yes">8x</span>
719
+ <span class="cline-any cline-yes">8x</span>
720
+ <span class="cline-any cline-yes">8x</span>
721
+ <span class="cline-any cline-yes">8x</span>
722
+ <span class="cline-any cline-yes">8x</span>
723
+ <span class="cline-any cline-yes">8x</span>
724
+ <span class="cline-any cline-yes">8x</span>
725
+ <span class="cline-any cline-yes">8x</span>
726
+ <span class="cline-any cline-yes">8x</span>
727
+ <span class="cline-any cline-yes">8x</span>
728
+ <span class="cline-any cline-yes">8x</span>
729
+ <span class="cline-any cline-yes">8x</span>
730
+ <span class="cline-any cline-yes">8x</span>
731
+ <span class="cline-any cline-yes">3x</span>
732
+ <span class="cline-any cline-yes">3x</span>
733
+ <span class="cline-any cline-yes">3x</span>
734
+ <span class="cline-any cline-yes">3x</span>
735
+ <span class="cline-any cline-yes">3x</span>
736
+ <span class="cline-any cline-yes">3x</span>
737
+ <span class="cline-any cline-yes">3x</span>
738
+ <span class="cline-any cline-yes">8x</span>
739
+ <span class="cline-any cline-yes">5x</span>
740
+ <span class="cline-any cline-yes">5x</span>
741
+ <span class="cline-any cline-yes">5x</span>
742
+ <span class="cline-any cline-yes">5x</span>
743
+ <span class="cline-any cline-yes">5x</span>
744
+ <span class="cline-any cline-yes">5x</span>
745
+ <span class="cline-any cline-yes">5x</span>
746
+ <span class="cline-any cline-yes">5x</span>
747
+ <span class="cline-any cline-yes">5x</span>
748
+ <span class="cline-any cline-yes">5x</span>
749
+ <span class="cline-any cline-yes">5x</span>
750
+ <span class="cline-any cline-yes">5x</span>
751
+ <span class="cline-any cline-yes">5x</span>
752
+ <span class="cline-any cline-yes">5x</span>
753
+ <span class="cline-any cline-yes">3x</span>
754
+ <span class="cline-any cline-yes">3x</span>
755
+ <span class="cline-any cline-yes">3x</span>
756
+ <span class="cline-any cline-yes">3x</span>
757
+ <span class="cline-any cline-yes">5x</span>
758
+ <span class="cline-any cline-yes">8x</span>
759
+ <span class="cline-any cline-yes">8x</span>
760
+ <span class="cline-any cline-yes">8x</span>
761
+ <span class="cline-any cline-yes">8x</span>
762
+ <span class="cline-any cline-yes">8x</span>
763
+ <span class="cline-any cline-yes">8x</span>
764
+ <span class="cline-any cline-yes">8x</span>
765
+ <span class="cline-any cline-yes">8x</span>
766
+ <span class="cline-any cline-yes">8x</span>
767
+ <span class="cline-any cline-yes">8x</span>
768
+ <span class="cline-any cline-yes">7x</span>
769
+ <span class="cline-any cline-yes">7x</span>
770
+ <span class="cline-any cline-yes">8x</span>
771
+ <span class="cline-any cline-yes">8x</span>
772
+ <span class="cline-any cline-yes">8x</span>
773
+ <span class="cline-any cline-yes">8x</span>
774
+ <span class="cline-any cline-yes">8x</span>
775
+ <span class="cline-any cline-yes">8x</span>
776
+ <span class="cline-any cline-yes">8x</span>
777
+ <span class="cline-any cline-yes">8x</span>
778
+ <span class="cline-any cline-yes">8x</span>
779
+ <span class="cline-any cline-yes">8x</span>
780
+ <span class="cline-any cline-yes">11x</span>
781
+ <span class="cline-any cline-yes">11x</span>
782
+ <span class="cline-any cline-yes">11x</span>
783
+ <span class="cline-any cline-yes">11x</span>
784
+ <span class="cline-any cline-yes">11x</span>
785
+ <span class="cline-any cline-yes">11x</span>
786
+ <span class="cline-any cline-no">&nbsp;</span>
787
+ <span class="cline-any cline-no">&nbsp;</span>
788
+ <span class="cline-any cline-yes">11x</span>
789
+ <span class="cline-any cline-yes">11x</span>
790
+ <span class="cline-any cline-yes">11x</span>
791
+ <span class="cline-any cline-yes">11x</span>
792
+ <span class="cline-any cline-yes">11x</span>
793
+ <span class="cline-any cline-yes">11x</span>
794
+ <span class="cline-any cline-yes">11x</span>
795
+ <span class="cline-any cline-yes">4x</span>
796
+ <span class="cline-any cline-yes">4x</span>
797
+ <span class="cline-any cline-yes">4x</span>
798
+ <span class="cline-any cline-yes">4x</span>
799
+ <span class="cline-any cline-yes">1x</span>
800
+ <span class="cline-any cline-yes">1x</span>
801
+ <span class="cline-any cline-yes">1x</span>
802
+ <span class="cline-any cline-yes">1x</span>
803
+ <span class="cline-any cline-yes">4x</span>
804
+ <span class="cline-any cline-yes">11x</span>
805
+ <span class="cline-any cline-yes">7x</span>
806
+ <span class="cline-any cline-yes">7x</span>
807
+ <span class="cline-any cline-yes">11x</span>
808
+ <span class="cline-any cline-yes">11x</span>
809
+ <span class="cline-any cline-yes">38x</span>
810
+ <span class="cline-any cline-yes">11x</span>
811
+ <span class="cline-any cline-yes">11x</span>
812
+ <span class="cline-any cline-yes">11x</span>
813
+ <span class="cline-any cline-yes">11x</span>
814
+ <span class="cline-any cline-yes">11x</span>
815
+ <span class="cline-any cline-yes">11x</span>
816
+ <span class="cline-any cline-yes">11x</span>
817
+ <span class="cline-any cline-yes">13x</span>
818
+ <span class="cline-any cline-yes">13x</span>
819
+ <span class="cline-any cline-yes">13x</span>
820
+ <span class="cline-any cline-yes">6x</span>
821
+ <span class="cline-any cline-yes">6x</span>
822
+ <span class="cline-any cline-yes">6x</span>
823
+ <span class="cline-any cline-yes">6x</span>
824
+ <span class="cline-any cline-yes">6x</span>
825
+ <span class="cline-any cline-yes">6x</span>
826
+ <span class="cline-any cline-yes">11x</span>
827
+ <span class="cline-any cline-yes">11x</span>
828
+ <span class="cline-any cline-yes">11x</span>
829
+ <span class="cline-any cline-yes">8x</span>
830
+ <span class="cline-any cline-yes">8x</span>
831
+ <span class="cline-any cline-yes">9x</span>
832
+ <span class="cline-any cline-yes">9x</span>
833
+ <span class="cline-any cline-yes">9x</span>
834
+ <span class="cline-any cline-yes">9x</span>
835
+ <span class="cline-any cline-yes">8x</span>
836
+ <span class="cline-any cline-yes">11x</span>
837
+ <span class="cline-any cline-yes">3x</span>
838
+ <span class="cline-any cline-yes">4x</span>
839
+ <span class="cline-any cline-yes">4x</span>
840
+ <span class="cline-any cline-yes">4x</span>
841
+ <span class="cline-any cline-yes">4x</span>
842
+ <span class="cline-any cline-yes">4x</span>
843
+ <span class="cline-any cline-yes">4x</span>
844
+ <span class="cline-any cline-yes">4x</span>
845
+ <span class="cline-any cline-yes">4x</span>
846
+ <span class="cline-any cline-yes">4x</span>
847
+ <span class="cline-any cline-yes">4x</span>
848
+ <span class="cline-any cline-yes">4x</span>
849
+ <span class="cline-any cline-yes">4x</span>
850
+ <span class="cline-any cline-yes">4x</span>
851
+ <span class="cline-any cline-yes">4x</span>
852
+ <span class="cline-any cline-yes">3x</span>
853
+ <span class="cline-any cline-no">&nbsp;</span>
854
+ <span class="cline-any cline-yes">3x</span>
855
+ <span class="cline-any cline-yes">4x</span>
856
+ <span class="cline-any cline-yes">4x</span>
857
+ <span class="cline-any cline-no">&nbsp;</span>
858
+ <span class="cline-any cline-no">&nbsp;</span>
859
+ <span class="cline-any cline-no">&nbsp;</span>
860
+ <span class="cline-any cline-yes">3x</span>
861
+ <span class="cline-any cline-yes">3x</span>
862
+ <span class="cline-any cline-yes">8x</span>
863
+ <span class="cline-any cline-yes">8x</span>
864
+ <span class="cline-any cline-yes">8x</span>
865
+ <span class="cline-any cline-yes">8x</span>
866
+ <span class="cline-any cline-yes">8x</span>
867
+ <span class="cline-any cline-yes">8x</span>
868
+ <span class="cline-any cline-yes">8x</span>
869
+ <span class="cline-any cline-yes">8x</span>
870
+ <span class="cline-any cline-yes">36x</span>
871
+ <span class="cline-any cline-yes">36x</span>
872
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import _ from 'lodash'
873
+ import { marshallGeometry } from '../marshall.js'
874
+ import makeDebug from 'debug'
875
+ &nbsp;
876
+ const debug = makeDebug('kdk:map:query:hooks')
877
+ &nbsp;
878
+ export function marshallGeometryQuery (hook) {
879
+ const query = hook.params.query
880
+ if (_.isNil(query)) <span class="branch-0 cbranch-no" title="branch not covered" >return</span>
881
+ const geometry = query.geometry
882
+ if (_.isNil(geometry)) <span class="branch-0 cbranch-no" title="branch not covered" >return</span>
883
+ marshallGeometry(geometry)
884
+ }
885
+ &nbsp;
886
+ function getGeometryQueryForBBox (bbox) {
887
+ // Here we use the custom MongoDB CRS that enforces counter-clockwise winding order
888
+ // and allows to support queries with a single-ringed GeoJSON polygon
889
+ // whose area is greater than or equal to a single hemisphere.
890
+ // Otherwise $geoIntersects queries for the complementary geometry.
891
+ return {
892
+ $geoIntersects: {
893
+ $geometry: {
894
+ type: 'Polygon',
895
+ coordinates: [bbox],
896
+ crs: {
897
+ type: 'name',
898
+ properties: { name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' }
899
+ }
900
+ }
901
+ }
902
+ }
903
+ }
904
+ &nbsp;
905
+ export function marshallGeoJsonQuery (hook) {
906
+ const query = hook.params.query
907
+ if (query) {
908
+ if (query.geoJson) {
909
+ delete query.geoJson
910
+ hook.params.asGeoJson = true
911
+ }
912
+ }
913
+ }
914
+ &nbsp;
915
+ export function marshallSpatialQuery (hook) {
916
+ const query = hook.params.query
917
+ if (query) {
918
+ if (!_.isNil(query.geometry)) marshallGeometry(query.geometry)
919
+ // Shortcut for proximity query
920
+ if ((!_.isNil(query.centerLon) || !_.isNil(query.longitude)) &amp;&amp;
921
+ (!_.isNil(query.centerLat) || !_.isNil(query.latitude)) &amp;&amp; !_.isNil(query.distance)) {
922
+ const longitude = (_.isNil(query.centerLon) ? _.toNumber(query.longitude) <span class="branch-0 cbranch-no" title="branch not covered" >: _.toNumber(query.centerLon))</span>
923
+ const latitude = (_.isNil(query.centerLat) ? _.toNumber(query.latitude) <span class="branch-0 cbranch-no" title="branch not covered" >: _.toNumber(query.centerLat))</span>
924
+ const distance = _.toNumber(query.distance)
925
+ // Transform to MongoDB spatial request
926
+ delete query.centerLon
927
+ delete query.longitude
928
+ delete query.centerLat
929
+ delete query.latitude
930
+ delete query.distance
931
+ // Aggregation requires a specific operator
932
+ if (query.$aggregate) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
927
933
  <span class="cstat-no" title="statement not covered" > query.$geoNear = {</span>
928
934
  <span class="cstat-no" title="statement not covered" > near: { type: 'Point', coordinates: [longitude, latitude] },</span>
929
935
  <span class="cstat-no" title="statement not covered" > maxDistance: distance,</span>
930
936
  <span class="cstat-no" title="statement not covered" > distanceField: 'distance',</span>
931
937
  <span class="cstat-no" title="statement not covered" > spherical: true</span>
932
938
  <span class="cstat-no" title="statement not covered" > }</span>
933
- <span class="cstat-no" title="statement not covered" > } else {</span>
934
- <span class="cstat-no" title="statement not covered" > /* We switched from $near to $geoWithin due to https://github.com/kalisio/kdk/issues/345</span>
935
- <span class="cstat-no" title="statement not covered" > query.geometry = {</span>
936
- <span class="cstat-no" title="statement not covered" > $near: {</span>
937
- <span class="cstat-no" title="statement not covered" > $geometry: {</span>
938
- <span class="cstat-no" title="statement not covered" > type: 'Point',</span>
939
- <span class="cstat-no" title="statement not covered" > coordinates: [longitude, latitude]</span>
940
- <span class="cstat-no" title="statement not covered" > },</span>
941
- <span class="cstat-no" title="statement not covered" > $maxDistance: distance</span>
942
- <span class="cstat-no" title="statement not covered" > }</span>
943
- <span class="cstat-no" title="statement not covered" > }</span>
944
- <span class="cstat-no" title="statement not covered" > */</span>
945
- <span class="cstat-no" title="statement not covered" > query.geometry = {</span>
946
- <span class="cstat-no" title="statement not covered" > $geoWithin: {</span>
947
- <span class="cstat-no" title="statement not covered" > $centerSphere: [[longitude, latitude], distance / 6378137.0] // Earth radius as in radians</span>
948
- <span class="cstat-no" title="statement not covered" > }</span>
949
- <span class="cstat-no" title="statement not covered" > }</span>
950
- <span class="cstat-no" title="statement not covered" > }</span>
951
- <span class="cstat-no" title="statement not covered" > }</span>
952
- <span class="cstat-no" title="statement not covered" > // Shortcut for bbox query</span>
953
- <span class="cstat-no" title="statement not covered" > if (!_.isNil(query.south) &amp;&amp; !_.isNil(query.north) &amp;&amp; !_.isNil(query.west) &amp;&amp; !_.isNil(query.east)) {</span>
954
- <span class="cstat-no" title="statement not covered" > const south = _.toNumber(query.south)</span>
955
- <span class="cstat-no" title="statement not covered" > const north = _.toNumber(query.north)</span>
956
- <span class="cstat-no" title="statement not covered" > const west = _.toNumber(query.west)</span>
957
- <span class="cstat-no" title="statement not covered" > const east = _.toNumber(query.east)</span>
958
- <span class="cstat-no" title="statement not covered" > // Transform to MongoDB spatial request</span>
959
- <span class="cstat-no" title="statement not covered" > delete query.south</span>
960
- <span class="cstat-no" title="statement not covered" > delete query.north</span>
961
- <span class="cstat-no" title="statement not covered" > delete query.west</span>
962
- <span class="cstat-no" title="statement not covered" > delete query.east</span>
963
- <span class="cstat-no" title="statement not covered" > </span>
964
- <span class="cstat-no" title="statement not covered" > // FIXME: MongoDB should allow to support queries with a single-ringed GeoJSON polygon</span>
965
- <span class="cstat-no" title="statement not covered" > // whose area is greater than or equal to a single hemisphere.</span>
966
- <span class="cstat-no" title="statement not covered" > // However, we did not succeeed in making it work as expected, for now on we split large polygon into two halfs.</span>
967
- <span class="cstat-no" title="statement not covered" > if ((east-west) &lt;= 180) {</span>
968
- <span class="cstat-no" title="statement not covered" > // BBox as a polygon also requires the closing point.</span>
969
- <span class="cstat-no" title="statement not covered" > const bbox = [[west, south], [east, south], [east, north], [west, north], [west, south]]</span>
970
- <span class="cstat-no" title="statement not covered" > query.geometry = getGeometryQueryForBBox(bbox)</span>
971
- <span class="cstat-no" title="statement not covered" > } else {</span>
972
- <span class="cstat-no" title="statement not covered" > // BBox as a polygon also requires the closing point.</span>
973
- <span class="cstat-no" title="statement not covered" > const leftHalfBbox = [[west, south], [0.5*(west+east), south], [0.5*(west+east), north], [west, north], [west, south]]</span>
974
- <span class="cstat-no" title="statement not covered" > const rightHalfBbox = [[0.5*(west+east), south], [east, south], [east, north], [0.5*(west+east), north], [0.5*(west+east), south]]</span>
975
- <span class="cstat-no" title="statement not covered" > query.$or = [{ geometry: getGeometryQueryForBBox(leftHalfBbox) }, { geometry: getGeometryQueryForBBox(rightHalfBbox) }]</span>
976
- <span class="cstat-no" title="statement not covered" > }</span>
977
- <span class="cstat-no" title="statement not covered" > }</span>
978
- <span class="cstat-no" title="statement not covered" > // Shortcut for location query</span>
979
- <span class="cstat-no" title="statement not covered" > if (!_.isNil(query.longitude) &amp;&amp; !_.isNil(query.latitude)) {</span>
980
- <span class="cstat-no" title="statement not covered" > const longitude = _.toNumber(query.longitude)</span>
981
- <span class="cstat-no" title="statement not covered" > const latitude = _.toNumber(query.latitude)</span>
982
- <span class="cstat-no" title="statement not covered" > // Transform to MongoDB spatial request</span>
983
- <span class="cstat-no" title="statement not covered" > delete query.longitude</span>
984
- <span class="cstat-no" title="statement not covered" > delete query.latitude</span>
985
- <span class="cstat-no" title="statement not covered" > const geometryQuery = {</span>
986
- <span class="cstat-no" title="statement not covered" > $geoIntersects: {</span>
987
- <span class="cstat-no" title="statement not covered" > $geometry: {</span>
988
- <span class="cstat-no" title="statement not covered" > type: 'Point',</span>
989
- <span class="cstat-no" title="statement not covered" > coordinates: [longitude, latitude]</span>
990
- <span class="cstat-no" title="statement not covered" > }</span>
991
- <span class="cstat-no" title="statement not covered" > }</span>
992
- <span class="cstat-no" title="statement not covered" > }</span>
993
- <span class="cstat-no" title="statement not covered" > query.geometry = geometryQuery</span>
994
- <span class="cstat-no" title="statement not covered" > }</span>
995
- <span class="cstat-no" title="statement not covered" > }</span>
996
- <span class="cstat-no" title="statement not covered" > // Include GeoJson query by default</span>
997
- <span class="cstat-no" title="statement not covered" > marshallGeoJsonQuery(hook)</span>
998
- <span class="cstat-no" title="statement not covered" >}</span>
999
- <span class="cstat-no" title="statement not covered" ></span>
1000
- <span class="cstat-no" title="statement not covered" >export function asGeoJson (options = {}) {</span>
1001
- <span class="cstat-no" title="statement not covered" > return function (hook) {</span>
1002
- <span class="cstat-no" title="statement not covered" > const params = hook.params</span>
1003
- <span class="cstat-no" title="statement not covered" > const query = params.query</span>
1004
- <span class="cstat-no" title="statement not covered" > if (!options.force &amp;&amp; !params.asGeoJson) return</span>
1005
- <span class="cstat-no" title="statement not covered" > if (_.has(query, '$distinct') || _.has(query, '$aggregation')) return // Not applicable in this case</span>
1006
- <span class="cstat-no" title="statement not covered" > const longitudeProperty = (options.longitudeProperty || 'longitude')</span>
1007
- <span class="cstat-no" title="statement not covered" > const latitudeProperty = (options.latitudeProperty || 'latitude')</span>
1008
- <span class="cstat-no" title="statement not covered" > const altitudeProperty = (options.altitudeProperty || 'altitude')</span>
1009
- <span class="cstat-no" title="statement not covered" > const geometryProperty = (options.geometryProperty || 'geometry')</span>
1010
- <span class="cstat-no" title="statement not covered" > const allowNullGeometries = _.get(options, 'allowNullGeometries', false)</span>
1011
- <span class="cstat-no" title="statement not covered" > let results = _.get(hook, options.dataPath || 'result')</span>
1012
- <span class="cstat-no" title="statement not covered" > // Already as GeoJson ?</span>
1013
- <span class="cstat-no" title="statement not covered" > if (results.type === 'FeatureCollection') return</span>
1014
- <span class="cstat-no" title="statement not covered" > const isPaginated = !_.isNil(results.data)</span>
1015
- <span class="cstat-no" title="statement not covered" > const pagination = (isPaginated ? _.pick(results, ['total', 'skip', 'limit']) : {})</span>
1016
- <span class="cstat-no" title="statement not covered" > results = (isPaginated ? results.data : results)</span>
1017
- <span class="cstat-no" title="statement not covered" > // Single item case, i.e. GET ?</span>
1018
- <span class="cstat-no" title="statement not covered" > const isFeatureCollection = Array.isArray(results)</span>
1019
- <span class="cstat-no" title="statement not covered" > if (!isFeatureCollection) results = [results]</span>
1020
- <span class="cstat-no" title="statement not covered" > results = results</span>
1021
- <span class="cstat-no" title="statement not covered" > .filter(item =&gt; {</span>
1022
- <span class="cstat-no" title="statement not covered" > // Check if item is not already in GeoJson feature format and we can convert if required</span>
1023
- <span class="cstat-no" title="statement not covered" > return (_.has(item, longitudeProperty) &amp;&amp; _.has(item, latitudeProperty)) ||</span>
1024
- <span class="cstat-no" title="statement not covered" > // When performing feature aggregation on geometries the result can be an array</span>
1025
- <span class="cstat-no" title="statement not covered" > Array.isArray(_.get(item, geometryProperty)) ||</span>
1026
- <span class="cstat-no" title="statement not covered" > // Check for a geometry property (previously provided or already transformed item)</span>
1027
- <span class="cstat-no" title="statement not covered" > (_.has(item, geometryProperty + '.type') &amp;&amp; _.has(item, geometryProperty + '.coordinates')) ||</span>
1028
- <span class="cstat-no" title="statement not covered" > (_.has(item, geometryProperty + '.geometry.type') &amp;&amp; _.has(item, geometryProperty + '.geometry.coordinates')) ||</span>
1029
- <span class="cstat-no" title="statement not covered" > // Check for null geometries when allowed</span>
1030
- <span class="cstat-no" title="statement not covered" > allowNullGeometries</span>
1031
- <span class="cstat-no" title="statement not covered" > })</span>
1032
- <span class="cstat-no" title="statement not covered" > .map(item =&gt; {</span>
1033
- <span class="cstat-no" title="statement not covered" > let coordinates</span>
1034
- <span class="cstat-no" title="statement not covered" > // Keep track of coordinates before picking properties</span>
1035
- <span class="cstat-no" title="statement not covered" > if (_.has(item, longitudeProperty) &amp;&amp; _.has(item, latitudeProperty)) {</span>
1036
- <span class="cstat-no" title="statement not covered" > coordinates = [_.get(item, longitudeProperty), _.get(item, latitudeProperty)]</span>
1037
- <span class="cstat-no" title="statement not covered" > if (_.has(item, altitudeProperty)) {</span>
939
+ } else {
940
+ /* We switched from $near to $geoWithin due to https://github.com/kalisio/kdk/issues/345
941
+ query.geometry = {
942
+ $near: {
943
+ $geometry: {
944
+ type: 'Point',
945
+ coordinates: [longitude, latitude]
946
+ },
947
+ $maxDistance: distance
948
+ }
949
+ }
950
+ */
951
+ query.geometry = {
952
+ $geoWithin: {
953
+ $centerSphere: [[longitude, latitude], distance / 6378137.0] // Earth radius as in radians
954
+ }
955
+ }
956
+ }
957
+ }
958
+ // Shortcut for bbox query
959
+ if (!_.isNil(query.south) &amp;&amp; !_.isNil(query.north) &amp;&amp; !_.isNil(query.west) &amp;&amp; !_.isNil(query.east)) {
960
+ const south = _.toNumber(query.south)
961
+ const north = _.toNumber(query.north)
962
+ const west = _.toNumber(query.west)
963
+ const east = _.toNumber(query.east)
964
+ // Transform to MongoDB spatial request
965
+ delete query.south
966
+ delete query.north
967
+ delete query.west
968
+ delete query.east
969
+
970
+ // FIXME: MongoDB should allow to support queries with a single-ringed GeoJSON polygon
971
+ // whose area is greater than or equal to a single hemisphere.
972
+ // However, we did not succeeed in making it work as expected, for now on we split large polygon into two halfs.
973
+ if ((east-west) &lt;= 180) {
974
+ // BBox as a polygon also requires the closing point.
975
+ const bbox = [[west, south], [east, south], [east, north], [west, north], [west, south]]
976
+ query.geometry = getGeometryQueryForBBox(bbox)
977
+ } else {
978
+ // BBox as a polygon also requires the closing point.
979
+ const leftHalfBbox = [[west, south], [0.5*(west+east), south], [0.5*(west+east), north], [west, north], [west, south]]
980
+ const rightHalfBbox = [[0.5*(west+east), south], [east, south], [east, north], [0.5*(west+east), north], [0.5*(west+east), south]]
981
+ query.$or = [{ geometry: getGeometryQueryForBBox(leftHalfBbox) }, { geometry: getGeometryQueryForBBox(rightHalfBbox) }]
982
+ }
983
+ }
984
+ // Shortcut for location query
985
+ if (!_.isNil(query.longitude) &amp;&amp; !_.isNil(query.latitude)) {
986
+ const longitude = _.toNumber(query.longitude)
987
+ const latitude = _.toNumber(query.latitude)
988
+ // Transform to MongoDB spatial request
989
+ delete query.longitude
990
+ delete query.latitude
991
+ const geometryQuery = {
992
+ $geoIntersects: {
993
+ $geometry: {
994
+ type: 'Point',
995
+ coordinates: [longitude, latitude]
996
+ }
997
+ }
998
+ }
999
+ query.geometry = geometryQuery
1000
+ }
1001
+ }
1002
+ // Include GeoJson query by default
1003
+ marshallGeoJsonQuery(hook)
1004
+ }
1005
+ &nbsp;
1006
+ export function asGeoJson (options = {}) {
1007
+ return function (hook) {
1008
+ const params = hook.params
1009
+ const query = params.query
1010
+ if (!options.force &amp;&amp; !params.asGeoJson) <span class="branch-0 cbranch-no" title="branch not covered" >return</span>
1011
+ if (_.has(query, '$distinct') || _.has(query, '$aggregation')) return // Not applicable in this case
1012
+ const longitudeProperty = (options.longitudeProperty || 'longitude')
1013
+ const latitudeProperty = (options.latitudeProperty || 'latitude')
1014
+ const altitudeProperty = (options.altitudeProperty || 'altitude')
1015
+ const geometryProperty = (options.geometryProperty || 'geometry')
1016
+ const allowNullGeometries = _.get(options, 'allowNullGeometries', false)
1017
+ let results = _.get(hook, options.dataPath || 'result')
1018
+ // Already as GeoJson ?
1019
+ if (results.type === 'FeatureCollection') <span class="branch-0 cbranch-no" title="branch not covered" >return</span>
1020
+ const isPaginated = !_.isNil(results.data)
1021
+ const pagination = (isPaginated ? _.pick(results, ['total', 'skip', 'limit']) : {})
1022
+ results = (isPaginated ? results.data : results)
1023
+ // Single item case, i.e. GET ?
1024
+ const isFeatureCollection = Array.isArray(results)
1025
+ if (!isFeatureCollection) <span class="branch-0 cbranch-no" title="branch not covered" >results = [results]</span>
1026
+ results = results
1027
+ .filter(item =&gt; {
1028
+ // Check if item is not already in GeoJson feature format and we can convert if required
1029
+ return (_.has(item, longitudeProperty) &amp;&amp; _.has(item, latitudeProperty)) ||
1030
+ // When performing feature aggregation on geometries the result can be an array
1031
+ Array.isArray(_.get(item, geometryProperty)) ||
1032
+ // Check for a geometry property (previously provided or already transformed item)
1033
+ (_.has(item, geometryProperty + '.type') &amp;&amp; _.has(item, geometryProperty + '.coordinates')) ||
1034
+ (_.has(item, geometryProperty + '.geometry.type') <span class="branch-0 cbranch-no" title="branch not covered" >&amp;&amp; _.has(item, geometryProperty + '.geometry.coordinates'))</span> ||
1035
+ // Check for null geometries when allowed
1036
+ allowNullGeometries
1037
+ })
1038
+ .map(item =&gt; {
1039
+ let coordinates
1040
+ // Keep track of coordinates before picking properties
1041
+ if (_.has(item, longitudeProperty) &amp;&amp; _.has(item, latitudeProperty)) {
1042
+ coordinates = [_.get(item, longitudeProperty), _.get(item, latitudeProperty)]
1043
+ if (_.has(item, altitudeProperty)) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1038
1044
  <span class="cstat-no" title="statement not covered" > coordinates.push(_.get(item, altitudeProperty))</span>
1039
1045
  <span class="cstat-no" title="statement not covered" > }</span>
1040
- <span class="cstat-no" title="statement not covered" > }</span>
1041
- <span class="cstat-no" title="statement not covered" > if (options.pick) {</span>
1042
- <span class="cstat-no" title="statement not covered" > item = _.pick(item, options.pick)</span>
1043
- <span class="cstat-no" title="statement not covered" > }</span>
1044
- <span class="cstat-no" title="statement not covered" > if (options.omit) {</span>
1045
- <span class="cstat-no" title="statement not covered" > item = _.omit(item, options.omit)</span>
1046
- <span class="cstat-no" title="statement not covered" > }</span>
1047
- <span class="cstat-no" title="statement not covered" > // Item locations are already in GeoJson format</span>
1048
- <span class="cstat-no" title="statement not covered" > if ((_.has(item, geometryProperty + '.type') &amp;&amp; _.has(item, geometryProperty + '.coordinates')) ||</span>
1049
- <span class="cstat-no" title="statement not covered" > (_.has(item, geometryProperty + '.geometry.type') &amp;&amp; _.has(item, geometryProperty + '.geometry.coordinates'))) {</span>
1050
- <span class="cstat-no" title="statement not covered" > return Object.assign({</span>
1051
- <span class="cstat-no" title="statement not covered" > type: 'Feature',</span>
1052
- <span class="cstat-no" title="statement not covered" > geometry: _.get(item, geometryProperty + '.geometry', _.get(item, geometryProperty)),</span>
1053
- <span class="cstat-no" title="statement not covered" > properties: {}</span>
1054
- <span class="cstat-no" title="statement not covered" > }, _.omit(item, [geometryProperty]))</span>
1055
- <span class="cstat-no" title="statement not covered" > } else if (Array.isArray(_.get(item, geometryProperty))) {</span>
1056
- <span class="cstat-no" title="statement not covered" > return Object.assign({</span>
1057
- <span class="cstat-no" title="statement not covered" > type: 'Feature',</span>
1058
- <span class="cstat-no" title="statement not covered" > geometry: { type: 'GeometryCollection', geometries: _.get(item, geometryProperty) },</span>
1059
- <span class="cstat-no" title="statement not covered" > properties: {}</span>
1060
- <span class="cstat-no" title="statement not covered" > }, _.omit(item, [geometryProperty]))</span>
1061
- <span class="cstat-no" title="statement not covered" > } else {</span>
1062
- <span class="cstat-no" title="statement not covered" > // Item locations are not already in GeoJson feature format so we need to convert</span>
1063
- <span class="cstat-no" title="statement not covered" > return Object.assign({</span>
1064
- <span class="cstat-no" title="statement not covered" > type: 'Feature',</span>
1065
- <span class="cstat-no" title="statement not covered" > geometry: (coordinates ? { type: 'Point', coordinates } : null),</span>
1066
- <span class="cstat-no" title="statement not covered" > properties: {}</span>
1067
- <span class="cstat-no" title="statement not covered" > }, _.omit(item, [longitudeProperty, latitudeProperty]))</span>
1068
- <span class="cstat-no" title="statement not covered" > }</span>
1069
- <span class="cstat-no" title="statement not covered" > })</span>
1070
- <span class="cstat-no" title="statement not covered" > // Move some data to properties ?</span>
1071
- <span class="cstat-no" title="statement not covered" > if (options.properties) {</span>
1072
- <span class="cstat-no" title="statement not covered" > results.forEach(item =&gt; {</span>
1073
- <span class="cstat-no" title="statement not covered" > // True indicates to move all fields to properties</span>
1074
- <span class="cstat-no" title="statement not covered" > if (options.properties === true) {</span>
1075
- <span class="cstat-no" title="statement not covered" > _.forOwn(item, (value, key) =&gt; {</span>
1076
- <span class="cstat-no" title="statement not covered" > if ((key === 'geometry') || (key === 'type') || (key === '_id')) return</span>
1077
- <span class="cstat-no" title="statement not covered" > _.set(item, `properties.${key}`, _.get(item, key))</span>
1078
- <span class="cstat-no" title="statement not covered" > _.unset(item, key)</span>
1079
- <span class="cstat-no" title="statement not covered" > })</span>
1080
- <span class="cstat-no" title="statement not covered" > } else { // Else we expect a specific mapping</span>
1046
+ }
1047
+ if (options.pick) {
1048
+ item = _.pick(item, options.pick)
1049
+ }
1050
+ if (options.omit) {
1051
+ item = _.omit(item, options.omit)
1052
+ }
1053
+ // Item locations are already in GeoJson format
1054
+ if ((_.has(item, geometryProperty + '.type') &amp;&amp; _.has(item, geometryProperty + '.coordinates')) ||
1055
+ (_.has(item, geometryProperty + '.geometry.type') <span class="branch-0 cbranch-no" title="branch not covered" >&amp;&amp; _.has(item, geometryProperty + '.geometry.coordinates'))</span>) {
1056
+ return Object.assign({
1057
+ type: 'Feature',
1058
+ geometry: _.get(item, geometryProperty + '.geometry', _.get(item, geometryProperty)),
1059
+ properties: {}
1060
+ }, _.omit(item, [geometryProperty]))
1061
+ } else if (Array.isArray(_.get(item, geometryProperty))) {
1062
+ return Object.assign({
1063
+ type: 'Feature',
1064
+ geometry: { type: 'GeometryCollection', geometries: _.get(item, geometryProperty) },
1065
+ properties: {}
1066
+ }, _.omit(item, [geometryProperty]))
1067
+ } else {
1068
+ // Item locations are not already in GeoJson feature format so we need to convert
1069
+ return Object.assign({
1070
+ type: 'Feature',
1071
+ geometry: (coordinates ? { type: 'Point', coordinates } : null),
1072
+ properties: {}
1073
+ }, _.omit(item, [longitudeProperty, latitudeProperty]))
1074
+ }
1075
+ })
1076
+ // Move some data to properties ?
1077
+ if (options.properties) {
1078
+ results.forEach(item =&gt; {
1079
+ // True indicates to move all fields to properties
1080
+ if (options.properties === true) {
1081
+ _.forOwn(item, (value, key) =&gt; {
1082
+ if ((key === 'geometry') || (key === 'type') || (key === '_id')) return
1083
+ _.set(item, `properties.${key}`, _.get(item, key))
1084
+ _.unset(item, key)
1085
+ })
1086
+ }<span class="branch-0 cbranch-no" title="branch not covered" > else { // Else we expect a specific mapping</span>
1081
1087
  <span class="cstat-no" title="statement not covered" > options.properties.forEach(mapping =&gt; {</span>
1082
1088
  <span class="cstat-no" title="statement not covered" > if (mapping.from) _.set(item, `properties.${mapping.to || mapping.from}`, _.get(item, `${mapping.from}`))</span>
1083
1089
  <span class="cstat-no" title="statement not covered" > if (mapping.delete) _.unset(item, `${mapping.from}`)</span>
1084
1090
  <span class="cstat-no" title="statement not covered" > })</span>
1085
1091
  <span class="cstat-no" title="statement not covered" > }</span>
1086
- <span class="cstat-no" title="statement not covered" > })</span>
1087
- <span class="cstat-no" title="statement not covered" > }</span>
1088
- <span class="cstat-no" title="statement not covered" > // If we should make it available as a GeoJson feature collection create it</span>
1089
- <span class="cstat-no" title="statement not covered" > if (isFeatureCollection &amp;&amp; _.get(options, 'asFeatureCollection', true)) {</span>
1090
- <span class="cstat-no" title="statement not covered" > // Copy pagination information if any so that client can use it anyway</span>
1091
- <span class="cstat-no" title="statement not covered" > _.set(hook, options.dataPath || 'result', Object.assign({</span>
1092
- <span class="cstat-no" title="statement not covered" > type: 'FeatureCollection',</span>
1093
- <span class="cstat-no" title="statement not covered" > features: results</span>
1094
- <span class="cstat-no" title="statement not covered" > }, pagination)) // If no pagination this merged object will be empty</span>
1095
- <span class="cstat-no" title="statement not covered" > } else if (isPaginated) {</span>
1092
+ })
1093
+ }
1094
+ // If we should make it available as a GeoJson feature collection create it
1095
+ if (isFeatureCollection &amp;&amp; _.get(options, 'asFeatureCollection', true)) {
1096
+ // Copy pagination information if any so that client can use it anyway
1097
+ _.set(hook, options.dataPath || 'result', Object.assign({
1098
+ type: 'FeatureCollection',
1099
+ features: results
1100
+ }, pagination)) // If no pagination this merged object will be empty
1101
+ } else if (isPaginated) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1096
1102
  <span class="cstat-no" title="statement not covered" > // Copy pagination information if any so that client can use it anyway</span>
1097
1103
  <span class="cstat-no" title="statement not covered" > _.set(hook, options.dataPath || 'result', Object.assign({</span>
1098
1104
  <span class="cstat-no" title="statement not covered" > data: results</span>
1099
1105
  <span class="cstat-no" title="statement not covered" > }, pagination))</span>
1100
- <span class="cstat-no" title="statement not covered" > } else {</span>
1101
- <span class="cstat-no" title="statement not covered" > _.set(hook, options.dataPath || 'result', isFeatureCollection ? results : results[0])</span>
1102
- <span class="cstat-no" title="statement not covered" > }</span>
1103
- <span class="cstat-no" title="statement not covered" > }</span>
1104
- <span class="cstat-no" title="statement not covered" >}</span>
1105
- <span class="cstat-no" title="statement not covered" ></span>
1106
- <span class="cstat-no" title="statement not covered" >export async function aggregateFeaturesQuery (hook) {</span>
1107
- <span class="cstat-no" title="statement not covered" > const query = hook.params.query</span>
1108
- <span class="cstat-no" title="statement not covered" > const service = hook.service</span>
1109
- <span class="cstat-no" title="statement not covered" > if (!query) return</span>
1110
- <span class="cstat-no" title="statement not covered" > // Perform aggregation</span>
1111
- <span class="cstat-no" title="statement not covered" > if (query.$aggregate) {</span>
1112
- <span class="cstat-no" title="statement not covered" > const collection = service.Model</span>
1113
- <span class="cstat-no" title="statement not covered" > const indexes = await collection.indexes()</span>
1114
- <span class="cstat-no" title="statement not covered" > let featureId = (service.options ? service.options.featureId : [])</span>
1115
- <span class="cstat-no" title="statement not covered" > // Support compound ID</span>
1116
- <span class="cstat-no" title="statement not covered" > featureId = (Array.isArray(featureId) ? featureId : [featureId])</span>
1117
- <span class="cstat-no" title="statement not covered" > let featureIdType = (service.options ? service.options.featureIdType : [])</span>
1118
- <span class="cstat-no" title="statement not covered" > featureIdType = (Array.isArray(featureIdType) ? featureIdType : [featureIdType])</span>
1119
- <span class="cstat-no" title="statement not covered" > const ids = typeof query.$groupBy === 'string' // Group by matching ID(s), ie single ID or array of field to create a compound ID</span>
1120
- <span class="cstat-no" title="statement not covered" > ? { [query.$groupBy]: '$properties.' + query.$groupBy }</span>
1121
- <span class="cstat-no" title="statement not covered" > // Aggregated in an accumulator to avoid conflict with feature properties</span>
1122
- <span class="cstat-no" title="statement not covered" > : query.$groupBy.reduce((object, id) =&gt; Object.assign(object, { [id]: '$properties.' + id }), {})</span>
1123
- <span class="cstat-no" title="statement not covered" > const groupBy = { _id: ids }</span>
1124
- <span class="cstat-no" title="statement not covered" > let keys = _.keys(ids)</span>
1125
- <span class="cstat-no" title="statement not covered" > // Do we only keep first or last available time ?</span>
1126
- <span class="cstat-no" title="statement not covered" > const singleTime = (_.toNumber(query.$limit) === 1)</span>
1127
- <span class="cstat-no" title="statement not covered" > if (singleTime) {</span>
1128
- <span class="cstat-no" title="statement not covered" > // When single time no aggregation is performed at all so we only have raw features</span>
1129
- <span class="cstat-no" title="statement not covered" > keys = keys.map(key =&gt; 'properties.' + key)</span>
1130
- <span class="cstat-no" title="statement not covered" > // In this case no need to aggregate on each element we simply keep the first feature</span>
1131
- <span class="cstat-no" title="statement not covered" > // BUG: according to https://jira.mongodb.org/browse/SERVER-9507 MongoDB is not yet</span>
1132
- <span class="cstat-no" title="statement not covered" > // able to optimize this kind of operations to avoid full index scan</span>
1133
- <span class="cstat-no" title="statement not covered" > // For now we should restrict it to short time range</span>
1134
- <span class="cstat-no" title="statement not covered" > Object.assign(groupBy, { feature: { $first: '$$ROOT' } })</span>
1135
- <span class="cstat-no" title="statement not covered" > } else {</span>
1136
- <span class="cstat-no" title="statement not covered" > Object.assign(groupBy, {</span>
1137
- <span class="cstat-no" title="statement not covered" > time: { $push: '$time' }, // Keep track of all times</span>
1138
- <span class="cstat-no" title="statement not covered" > runTime: { $push: '$runTime' }, // Keep track of all run times</span>
1139
- <span class="cstat-no" title="statement not covered" > type: { $last: '$type' }, // type is assumed similar for all results, keep last</span>
1140
- <span class="cstat-no" title="statement not covered" > properties: { $last: '$properties' } // non-aggregated properties are assumed similar for all results, keep last</span>
1141
- <span class="cstat-no" title="statement not covered" > })</span>
1142
- <span class="cstat-no" title="statement not covered" > // Keep track of all levels as well if not targetting a specific one</span>
1143
- <span class="cstat-no" title="statement not covered" > if (!_.has(query, 'level')) {</span>
1144
- <span class="cstat-no" title="statement not covered" > Object.assign(groupBy, {</span>
1145
- <span class="cstat-no" title="statement not covered" > level: { $push: '$level' }</span>
1146
- <span class="cstat-no" title="statement not covered" > })</span>
1147
- <span class="cstat-no" title="statement not covered" > }</span>
1148
- <span class="cstat-no" title="statement not covered" > // Check if we aggregate geometry or simply properties</span>
1149
- <span class="cstat-no" title="statement not covered" > if (!query.$aggregate.includes('geometry')) {</span>
1150
- <span class="cstat-no" title="statement not covered" > Object.assign(groupBy, {</span>
1151
- <span class="cstat-no" title="statement not covered" > geometry: { $last: '$geometry' } // geometry is assumed similar for all results, keep last</span>
1152
- <span class="cstat-no" title="statement not covered" > })</span>
1153
- <span class="cstat-no" title="statement not covered" > }</span>
1154
- <span class="cstat-no" title="statement not covered" > }</span>
1155
- <span class="cstat-no" title="statement not covered" > // Merge with any additional group expression</span>
1156
- <span class="cstat-no" title="statement not covered" > const group = _.get(query, '$group', {})</span>
1157
- <span class="cstat-no" title="statement not covered" > Object.assign(groupBy, group)</span>
1158
- <span class="cstat-no" title="statement not covered" > // The query contains the match stage except options relevent to the aggregation pipeline</span>
1159
- <span class="cstat-no" title="statement not covered" > const match = _.omit(query, ['$group', '$groupBy', '$aggregate', '$geoNear', '$sort', '$limit', '$skip'])</span>
1160
- <span class="cstat-no" title="statement not covered" > // Check for any required type conversion (eg HTTP requests)</span>
1161
- <span class="cstat-no" title="statement not covered" > for (let i = 0; i &lt; featureId.length; i++) {</span>
1162
- <span class="cstat-no" title="statement not covered" > const id = featureId[i]</span>
1163
- <span class="cstat-no" title="statement not covered" > const idType = featureIdType[i]</span>
1164
- <span class="cstat-no" title="statement not covered" > if (_.has(match, 'properties.' + id)) {</span>
1165
- <span class="cstat-no" title="statement not covered" > if (idType === 'number') _.set(match, 'properties.' + id, _.toNumber(_.get(match, 'properties.' + id)))</span>
1166
- <span class="cstat-no" title="statement not covered" > }</span>
1167
- <span class="cstat-no" title="statement not covered" > }</span>
1168
- <span class="cstat-no" title="statement not covered" > // Ensure we do not mix results with/without relevant element values</span>
1169
- <span class="cstat-no" title="statement not covered" > // by separately querying each element then merging</span>
1170
- <span class="cstat-no" title="statement not covered" > let aggregatedResults</span>
1171
- <span class="cstat-no" title="statement not covered" > // Associative map used to optimize merging between aggregated elements</span>
1172
- <span class="cstat-no" title="statement not covered" > const aggregatedResultsMap = new Map()</span>
1173
- <span class="cstat-no" title="statement not covered" > const aggregateOptions = {</span>
1174
- <span class="cstat-no" title="statement not covered" > allowDiskUse: true</span>
1175
- <span class="cstat-no" title="statement not covered" > }</span>
1176
- <span class="cstat-no" title="statement not covered" > await Promise.all(query.$aggregate.map(async element =&gt; {</span>
1177
- <span class="cstat-no" title="statement not covered" > const isGeometry = (element === 'geometry')</span>
1178
- <span class="cstat-no" title="statement not covered" > // Geometry is a root property while others are feature properties</span>
1179
- <span class="cstat-no" title="statement not covered" > const prefix = (isGeometry ? '' : 'properties.')</span>
1180
- <span class="cstat-no" title="statement not covered" > const pipeline = []</span>
1181
- <span class="cstat-no" title="statement not covered" > // Check for geometry stage</span>
1182
- <span class="cstat-no" title="statement not covered" > if (query.$geoNear) {</span>
1106
+ } else {
1107
+ _.set(hook, options.dataPath || 'result', isFeatureCollection ? results <span class="branch-0 cbranch-no" title="branch not covered" >: results[0])</span>
1108
+ }
1109
+ }
1110
+ }
1111
+ &nbsp;
1112
+ export async function aggregateFeaturesQuery (hook) {
1113
+ const query = hook.params.query
1114
+ const service = hook.service
1115
+ if (!query) <span class="branch-0 cbranch-no" title="branch not covered" >return</span>
1116
+ // Perform aggregation
1117
+ if (query.$aggregate) {
1118
+ const collection = service.Model
1119
+ const indexes = await collection.indexes()
1120
+ let featureId = (service.options ? service.options.featureId <span class="branch-0 cbranch-no" title="branch not covered" >: [])</span>
1121
+ // Support compound ID
1122
+ featureId = (Array.isArray(featureId) <span class="branch-0 cbranch-no" title="branch not covered" >? featureId </span>: [featureId])
1123
+ let featureIdType = (service.options ? service.options.featureIdType <span class="branch-0 cbranch-no" title="branch not covered" >: [])</span>
1124
+ featureIdType = (Array.isArray(featureIdType) <span class="branch-0 cbranch-no" title="branch not covered" >? featureIdType </span>: [featureIdType])
1125
+ const ids = typeof query.$groupBy === 'string' // Group by matching ID(s), ie single ID or array of field to create a compound ID
1126
+ ? { [query.$groupBy]: '$properties.' + query.$groupBy }
1127
+ // Aggregated in an accumulator to avoid conflict with feature properties
1128
+ <span class="branch-0 cbranch-no" title="branch not covered" > : query.$groupBy.reduce((object, id) =&gt; Object.assign(object, { [id]: '$properties.' + id }), {})</span>
1129
+ const groupBy = { _id: ids }
1130
+ let keys = _.keys(ids)
1131
+ // Do we only keep first or last available time ?
1132
+ const singleTime = (_.toNumber(query.$limit) === 1)
1133
+ if (singleTime) {
1134
+ // When single time no aggregation is performed at all so we only have raw features
1135
+ keys = keys.map(key =&gt; 'properties.' + key)
1136
+ // In this case no need to aggregate on each element we simply keep the first feature
1137
+ // BUG: according to https://jira.mongodb.org/browse/SERVER-9507 MongoDB is not yet
1138
+ // able to optimize this kind of operations to avoid full index scan
1139
+ // For now we should restrict it to short time range
1140
+ Object.assign(groupBy, { feature: { $first: '$$ROOT' } })
1141
+ } else {
1142
+ Object.assign(groupBy, {
1143
+ time: { $push: '$time' }, // Keep track of all times
1144
+ runTime: { $push: '$runTime' }, // Keep track of all run times
1145
+ type: { $last: '$type' }, // type is assumed similar for all results, keep last
1146
+ properties: { $last: '$properties' } // non-aggregated properties are assumed similar for all results, keep last
1147
+ })
1148
+ // Keep track of all levels as well if not targetting a specific one
1149
+ if (!_.has(query, 'level')) {
1150
+ Object.assign(groupBy, {
1151
+ level: { $push: '$level' }
1152
+ })
1153
+ }
1154
+ // Check if we aggregate geometry or simply properties
1155
+ if (!query.$aggregate.includes('geometry')) {
1156
+ Object.assign(groupBy, {
1157
+ geometry: { $last: '$geometry' } // geometry is assumed similar for all results, keep last
1158
+ })
1159
+ }
1160
+ }
1161
+ // Merge with any additional group expression
1162
+ const group = _.get(query, '$group', {})
1163
+ Object.assign(groupBy, group)
1164
+ // The query contains the match stage except options relevent to the aggregation pipeline
1165
+ const match = _.omit(query, ['$group', '$groupBy', '$aggregate', '$geoNear', '$sort', '$limit', '$skip'])
1166
+ // Check for any required type conversion (eg HTTP requests)
1167
+ for (let i = 0; i &lt; featureId.length; i++) {
1168
+ const id = featureId[i]
1169
+ const idType = featureIdType[i]
1170
+ if (_.has(match, 'properties.' + id)) {
1171
+ if (idType === 'number') <span class="branch-0 cbranch-no" title="branch not covered" >_.set(match, 'properties.' + id, _.toNumber(_.get(match, 'properties.' + id)))</span>
1172
+ }
1173
+ }
1174
+ // Ensure we do not mix results with/without relevant element values
1175
+ // by separately querying each element then merging
1176
+ let aggregatedResults
1177
+ // Associative map used to optimize merging between aggregated elements
1178
+ const aggregatedResultsMap = new Map()
1179
+ const aggregateOptions = {
1180
+ allowDiskUse: true
1181
+ }
1182
+ await Promise.all(query.$aggregate.map(async element =&gt; {
1183
+ const isGeometry = (element === 'geometry')
1184
+ // Geometry is a root property while others are feature properties
1185
+ const prefix = (isGeometry ? '' : 'properties.')
1186
+ const pipeline = []
1187
+ // Check for geometry stage
1188
+ if (query.$geoNear) <span class="branch-0 cbranch-no" title="branch not covered" >{</span>
1183
1189
  <span class="cstat-no" title="statement not covered" > pipeline.push({ $geoNear: query.$geoNear })</span>
1184
1190
  <span class="cstat-no" title="statement not covered" > }</span>
1185
- <span class="cstat-no" title="statement not covered" > // Find matching features only</span>
1186
- <span class="cstat-no" title="statement not covered" > pipeline.push({ $match: Object.assign({ [prefix + element]: { $exists: true } }, match) })</span>
1187
- <span class="cstat-no" title="statement not covered" > // Ensure they are ordered by increasing time by default,</span>
1188
- <span class="cstat-no" title="statement not covered" > // most recent forecast and lower level first</span>
1189
- <span class="cstat-no" title="statement not covered" > pipeline.push({ $sort: Object.assign({ time: 1, runTime: -1, level: 1 }, query.$sort) })</span>
1190
- <span class="cstat-no" title="statement not covered" > // Keep track of all feature values</span>
1191
- <span class="cstat-no" title="statement not covered" > if (singleTime) {</span>
1192
- <span class="cstat-no" title="statement not covered" > pipeline.push({ $group: groupBy })</span>
1193
- <span class="cstat-no" title="statement not covered" > // As we replace the root document with the feature in this case keep track of any accumlated element before</span>
1194
- <span class="cstat-no" title="statement not covered" > // If the accumulated properties is name maxProperty then we copy it in the feature as feature.properties.maxProperty</span>
1195
- <span class="cstat-no" title="statement not covered" > if (!_.isEmpty(group)) {</span>
1196
- <span class="cstat-no" title="statement not covered" > pipeline.push({</span>
1197
- <span class="cstat-no" title="statement not covered" > $set: _.mapKeys(_.mapValues(group, (value, key) =&gt; `$${key}`), (value, key) =&gt; `feature.properties.${key}`)</span>
1198
- <span class="cstat-no" title="statement not covered" > })</span>
1199
- <span class="cstat-no" title="statement not covered" > }</span>
1200
- <span class="cstat-no" title="statement not covered" > pipeline.push({ $replaceRoot: { newRoot: '$feature' } })</span>
1201
- <span class="cstat-no" title="statement not covered" > } else {</span>
1202
- <span class="cstat-no" title="statement not covered" > pipeline.push({ $group: Object.assign({ [element]: { $push: '$' + prefix + element } }, groupBy) })</span>
1203
- <span class="cstat-no" title="statement not covered" > }</span>
1204
- <span class="cstat-no" title="statement not covered" > debug(`Aggregating ${element} element for features`)</span>
1205
- <span class="cstat-no" title="statement not covered" > pipeline.forEach(stage =&gt; {</span>
1206
- <span class="cstat-no" title="statement not covered" > _.forOwn(stage, (value, key) =&gt; debug('Stage', key, value))</span>
1207
- <span class="cstat-no" title="statement not covered" > })</span>
1208
- <span class="cstat-no" title="statement not covered" > const aggregateElementOptions = Object.assign({}, aggregateOptions)</span>
1209
- <span class="cstat-no" title="statement not covered" > debug('Aggregation options', aggregateElementOptions)</span>
1210
- <span class="cstat-no" title="statement not covered" > const elementResults = await collection.aggregate(pipeline, aggregateElementOptions).toArray()</span>
1211
- <span class="cstat-no" title="statement not covered" > debug(`Generated ${elementResults.length} feature(s) for ${element} element, picked first two`, elementResults.slice(0,2))</span>
1212
- <span class="cstat-no" title="statement not covered" > // Rearrange data so that we get ordered arrays indexed by element</span>
1213
- <span class="cstat-no" title="statement not covered" > elementResults.forEach(result =&gt; {</span>
1214
- <span class="cstat-no" title="statement not covered" > result.time = { [element]: result.time }</span>
1215
- <span class="cstat-no" title="statement not covered" > if (result.runTime) result.runTime = { [element]: result.runTime }</span>
1216
- <span class="cstat-no" title="statement not covered" > if (!singleTime &amp;&amp; !isGeometry) {</span>
1217
- <span class="cstat-no" title="statement not covered" > // Set back the element values as properties because we aggregated in an accumulator</span>
1218
- <span class="cstat-no" title="statement not covered" > // to avoid conflict with non-aggregated feature properties</span>
1219
- <span class="cstat-no" title="statement not covered" > _.set(result, prefix + element, _.get(result, element))</span>
1220
- <span class="cstat-no" title="statement not covered" > // Delete accumulator</span>
1221
- <span class="cstat-no" title="statement not covered" > _.unset(result, element)</span>
1222
- <span class="cstat-no" title="statement not covered" > }</span>
1223
- <span class="cstat-no" title="statement not covered" > // Keep track of result in map to improve search later</span>
1224
- <span class="cstat-no" title="statement not covered" > const resultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))</span>
1225
- <span class="cstat-no" title="statement not covered" > const resultKey = resultKeys.join('-')</span>
1226
- <span class="cstat-no" title="statement not covered" > if (!aggregatedResultsMap.has(resultKey)) aggregatedResultsMap.set(resultKey, result)</span>
1227
- <span class="cstat-no" title="statement not covered" > })</span>
1228
- <span class="cstat-no" title="statement not covered" > // Now merge with previous element results</span>
1229
- <span class="cstat-no" title="statement not covered" > if (!aggregatedResults) {</span>
1230
- <span class="cstat-no" title="statement not covered" > aggregatedResults = elementResults</span>
1231
- <span class="cstat-no" title="statement not covered" > } else {</span>
1232
- <span class="cstat-no" title="statement not covered" > elementResults.forEach(result =&gt; {</span>
1233
- <span class="cstat-no" title="statement not covered" > // When single time no aggregation is performed at all so we only have raw features</span>
1234
- <span class="cstat-no" title="statement not covered" > const resultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))</span>
1235
- <span class="cstat-no" title="statement not covered" > /* Optimize previous result search with map, kept the naive code here for reference/debug purpose</span>
1236
- <span class="cstat-no" title="statement not covered" > const previousResult = aggregatedResults.find(aggregatedResult =&gt; {</span>
1237
- <span class="cstat-no" title="statement not covered" > const aggregatedResultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))</span>
1238
- <span class="cstat-no" title="statement not covered" > return _.isEqual(aggregatedResultKeys, resultKeys)</span>
1239
- <span class="cstat-no" title="statement not covered" > })</span>
1240
- <span class="cstat-no" title="statement not covered" > */</span>
1241
- <span class="cstat-no" title="statement not covered" > const resultKey = resultKeys.join('-')</span>
1242
- <span class="cstat-no" title="statement not covered" > const previousResult = aggregatedResultsMap.get(resultKey)</span>
1243
- <span class="cstat-no" title="statement not covered" > // Merge with previous matching feature if any</span>
1244
- <span class="cstat-no" title="statement not covered" > if (previousResult) {</span>
1245
- <span class="cstat-no" title="statement not covered" > Object.assign(previousResult.time, result.time)</span>
1246
- <span class="cstat-no" title="statement not covered" > if (result.runTime) {</span>
1247
- <span class="cstat-no" title="statement not covered" > if (previousResult.runTime) Object.assign(previousResult.runTime, result.runTime)</span>
1248
- <span class="cstat-no" title="statement not covered" > else previousResult.runTime = { [element]: result.runTime }</span>
1249
- <span class="cstat-no" title="statement not covered" > }</span>
1250
- <span class="cstat-no" title="statement not covered" > _.set(previousResult, prefix + element, _.get(result, prefix + element))</span>
1251
- <span class="cstat-no" title="statement not covered" > } else {</span>
1191
+ // Find matching features only
1192
+ pipeline.push({ $match: Object.assign({ [prefix + element]: { $exists: true } }, match) })
1193
+ // Ensure they are ordered by increasing time by default,
1194
+ // most recent forecast and lower level first
1195
+ pipeline.push({ $sort: Object.assign({ time: 1, runTime: -1, level: 1 }, query.$sort) })
1196
+ // Keep track of all feature values
1197
+ if (singleTime) {
1198
+ pipeline.push({ $group: groupBy })
1199
+ // As we replace the root document with the feature in this case keep track of any accumlated element before
1200
+ // If the accumulated properties is name maxProperty then we copy it in the feature as feature.properties.maxProperty
1201
+ if (!_.isEmpty(group)) {
1202
+ pipeline.push({
1203
+ $set: _.mapKeys(_.mapValues(group, (value, key) =&gt; `$${key}`), (value, key) =&gt; `feature.properties.${key}`)
1204
+ })
1205
+ }
1206
+ pipeline.push({ $replaceRoot: { newRoot: '$feature' } })
1207
+ } else {
1208
+ pipeline.push({ $group: Object.assign({ [element]: { $push: '$' + prefix + element } }, groupBy) })
1209
+ }
1210
+ debug(`Aggregating ${element} element for features`)
1211
+ pipeline.forEach(stage =&gt; {
1212
+ _.forOwn(stage, (value, key) =&gt; debug('Stage', key, value))
1213
+ })
1214
+ const aggregateElementOptions = Object.assign({}, aggregateOptions)
1215
+ debug('Aggregation options', aggregateElementOptions)
1216
+ const elementResults = await collection.aggregate(pipeline, aggregateElementOptions).toArray()
1217
+ debug(`Generated ${elementResults.length} feature(s) for ${element} element, picked first two`, elementResults.slice(0,2))
1218
+ // Rearrange data so that we get ordered arrays indexed by element
1219
+ elementResults.forEach(result =&gt; {
1220
+ result.time = { [element]: result.time }
1221
+ if (result.runTime) result.runTime = { [element]: result.runTime }
1222
+ if (!singleTime &amp;&amp; !isGeometry) {
1223
+ // Set back the element values as properties because we aggregated in an accumulator
1224
+ // to avoid conflict with non-aggregated feature properties
1225
+ _.set(result, prefix + element, _.get(result, element))
1226
+ // Delete accumulator
1227
+ _.unset(result, element)
1228
+ }
1229
+ })
1230
+ // Now merge with previous element results
1231
+ if (!aggregatedResults) {
1232
+ aggregatedResults = elementResults
1233
+ aggregatedResults.forEach(result =&gt; {
1234
+ // Keep track of result in map to improve search later
1235
+ const resultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))
1236
+ const resultKey = resultKeys.join('-')
1237
+ aggregatedResultsMap.set(resultKey, result)
1238
+ })
1239
+ } else {
1240
+ elementResults.forEach(result =&gt; {
1241
+ // When single time no aggregation is performed at all so we only have raw features
1242
+ const resultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))
1243
+ /* Optimize previous result search with map, kept the naive code here for reference/debug purpose
1244
+ const previousResult = aggregatedResults.find(aggregatedResult =&gt; {
1245
+ const aggregatedResultKeys = keys.map(key =&gt; _.get(singleTime ? result : result._id, key))
1246
+ return _.isEqual(aggregatedResultKeys, resultKeys)
1247
+ })
1248
+ */
1249
+ const resultKey = resultKeys.join('-')
1250
+ const previousResult = aggregatedResultsMap.get(resultKey)
1251
+ // Merge with previous matching feature if any
1252
+ if (previousResult) {
1253
+ Object.assign(previousResult.time, result.time)
1254
+ if (result.runTime) {
1255
+ if (previousResult.runTime) Object.assign(previousResult.runTime, result.runTime)
1256
+ <span class="cstat-no" title="statement not covered" ><span class="branch-0 cbranch-no" title="branch not covered" > else previousResult.runTime = { [element]: result.runTime }</span></span>
1257
+ }
1258
+ _.set(previousResult, prefix + element, _.get(result, prefix + element))
1259
+ }<span class="branch-0 cbranch-no" title="branch not covered" > else {</span>
1252
1260
  <span class="cstat-no" title="statement not covered" > aggregatedResults.push(result)</span>
1261
+ <span class="cstat-no" title="statement not covered" > aggregatedResultsMap.set(resultKey, result)</span>
1253
1262
  <span class="cstat-no" title="statement not covered" > }</span>
1254
- <span class="cstat-no" title="statement not covered" > })</span>
1255
- <span class="cstat-no" title="statement not covered" > }</span>
1256
- <span class="cstat-no" title="statement not covered" > }))</span>
1257
- <span class="cstat-no" title="statement not covered" > delete query.$groupBy</span>
1258
- <span class="cstat-no" title="statement not covered" > delete query.$group</span>
1259
- <span class="cstat-no" title="statement not covered" > delete query.$aggregate</span>
1260
- <span class="cstat-no" title="statement not covered" > delete query.$geoNear</span>
1261
- <span class="cstat-no" title="statement not covered" > // Set result to avoid service DB call</span>
1262
- <span class="cstat-no" title="statement not covered" > hook.result = aggregatedResults || []</span>
1263
- <span class="cstat-no" title="statement not covered" > }</span>
1264
- <span class="cstat-no" title="statement not covered" > return hook</span>
1265
- <span class="cstat-no" title="statement not covered" >}</span>
1263
+ })
1264
+ }
1265
+ }))
1266
+ delete query.$groupBy
1267
+ delete query.$group
1268
+ delete query.$aggregate
1269
+ delete query.$geoNear
1270
+ // Set result to avoid service DB call
1271
+ hook.result = aggregatedResults <span class="branch-0 cbranch-no" title="branch not covered" >|| []</span>
1272
+ }
1273
+ return hook
1274
+ }
1266
1275
  &nbsp;</pre></td></tr></table></pre>
1267
1276
 
1268
1277
  <div class='push'></div><!-- for sticky footer -->
@@ -1270,7 +1279,7 @@
1270
1279
  <div class='footer quiet pad2 space-top1 center small'>
1271
1280
  Code coverage generated by
1272
1281
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1273
- at 2025-05-21T12:01:19.833Z
1282
+ at 2025-07-24T10:00:20.508Z
1274
1283
  </div>
1275
1284
  <script src="../../../prettify.js"></script>
1276
1285
  <script>