umap-project 3.4.0b3__py3-none-any.whl → 3.6.0__py3-none-any.whl

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 (222) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/br/LC_MESSAGES/django.po +71 -57
  4. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/da/LC_MESSAGES/django.po +18 -14
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +20 -16
  8. umap/locale/en/LC_MESSAGES/django.po +18 -14
  9. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/es/LC_MESSAGES/django.po +20 -16
  11. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/fr/LC_MESSAGES/django.po +18 -14
  13. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/hu/LC_MESSAGES/django.po +20 -16
  15. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/pl/LC_MESSAGES/django.po +101 -95
  17. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/zh_TW/LC_MESSAGES/django.po +20 -16
  19. umap/management/commands/clean_tilelayer.py +0 -1
  20. umap/management/commands/search_maps.py +95 -0
  21. umap/settings/__init__.py +9 -1
  22. umap/settings/base.py +7 -6
  23. umap/static/umap/content.css +0 -3
  24. umap/static/umap/css/bar.css +9 -6
  25. umap/static/umap/css/form.css +25 -9
  26. umap/static/umap/css/icon.css +8 -0
  27. umap/static/umap/css/popup.css +1 -0
  28. umap/static/umap/img/16-white.svg +5 -2
  29. umap/static/umap/img/16.svg +1 -1
  30. umap/static/umap/img/source/16-white.svg +7 -4
  31. umap/static/umap/img/source/16.svg +1 -1
  32. umap/static/umap/js/components/copiable.js +47 -0
  33. umap/static/umap/js/modules/autocomplete.js +32 -67
  34. umap/static/umap/js/modules/browser.js +31 -14
  35. umap/static/umap/js/modules/data/features.js +34 -36
  36. umap/static/umap/js/modules/data/fields.js +199 -23
  37. umap/static/umap/js/modules/data/layer.js +85 -96
  38. umap/static/umap/js/modules/domutils.js +25 -1
  39. umap/static/umap/js/modules/filters.js +24 -50
  40. umap/static/umap/js/modules/form/builder.js +17 -16
  41. umap/static/umap/js/modules/form/fields.js +20 -20
  42. umap/static/umap/js/modules/formatter.js +9 -1
  43. umap/static/umap/js/modules/help.js +12 -13
  44. umap/static/umap/js/modules/importer.js +17 -26
  45. umap/static/umap/js/modules/importers/banfr.js +0 -1
  46. umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
  47. umap/static/umap/js/modules/importers/communesfr.js +7 -8
  48. umap/static/umap/js/modules/importers/datasets.js +14 -14
  49. umap/static/umap/js/modules/importers/geodatamine.js +20 -22
  50. umap/static/umap/js/modules/importers/opendata.js +10 -0
  51. umap/static/umap/js/modules/importers/overpass.js +19 -18
  52. umap/static/umap/js/modules/managers.js +1 -1
  53. umap/static/umap/js/modules/permissions.js +15 -5
  54. umap/static/umap/js/modules/rendering/controls.js +203 -10
  55. umap/static/umap/js/modules/rendering/icon.js +5 -9
  56. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  57. umap/static/umap/js/modules/rendering/layers/classified.js +16 -11
  58. umap/static/umap/js/modules/rendering/layers/heat.js +1 -0
  59. umap/static/umap/js/modules/rendering/map.js +67 -57
  60. umap/static/umap/js/modules/rendering/popup.js +6 -3
  61. umap/static/umap/js/modules/rendering/template.js +40 -40
  62. umap/static/umap/js/modules/rendering/ui.js +1 -2
  63. umap/static/umap/js/modules/rules.js +34 -41
  64. umap/static/umap/js/modules/schema.js +0 -7
  65. umap/static/umap/js/modules/share.js +36 -69
  66. umap/static/umap/js/modules/slideshow.js +3 -3
  67. umap/static/umap/js/modules/tableeditor.js +0 -1
  68. umap/static/umap/js/modules/ui/bar.js +53 -33
  69. umap/static/umap/js/modules/ui/hash.js +36 -0
  70. umap/static/umap/js/modules/ui/loader.js +26 -0
  71. umap/static/umap/js/modules/ui/panel.js +33 -21
  72. umap/static/umap/js/modules/ui/tooltip.js +1 -1
  73. umap/static/umap/js/modules/umap.js +81 -80
  74. umap/static/umap/js/modules/utils.js +13 -3
  75. umap/static/umap/js/umap.controls.js +16 -179
  76. umap/static/umap/locale/am_ET.js +7 -8
  77. umap/static/umap/locale/am_ET.json +7 -8
  78. umap/static/umap/locale/ar.js +7 -8
  79. umap/static/umap/locale/ar.json +7 -8
  80. umap/static/umap/locale/ast.js +7 -8
  81. umap/static/umap/locale/ast.json +7 -8
  82. umap/static/umap/locale/bg.js +7 -8
  83. umap/static/umap/locale/bg.json +7 -8
  84. umap/static/umap/locale/br.js +44 -36
  85. umap/static/umap/locale/br.json +44 -36
  86. umap/static/umap/locale/ca.js +7 -8
  87. umap/static/umap/locale/ca.json +7 -8
  88. umap/static/umap/locale/cs_CZ.js +7 -8
  89. umap/static/umap/locale/cs_CZ.json +7 -8
  90. umap/static/umap/locale/da.js +8 -9
  91. umap/static/umap/locale/da.json +8 -9
  92. umap/static/umap/locale/de.js +62 -63
  93. umap/static/umap/locale/de.json +62 -63
  94. umap/static/umap/locale/el.js +7 -8
  95. umap/static/umap/locale/el.json +7 -8
  96. umap/static/umap/locale/en.js +7 -8
  97. umap/static/umap/locale/en.json +7 -8
  98. umap/static/umap/locale/en_US.json +7 -8
  99. umap/static/umap/locale/es.js +19 -20
  100. umap/static/umap/locale/es.json +19 -20
  101. umap/static/umap/locale/et.js +7 -8
  102. umap/static/umap/locale/et.json +7 -8
  103. umap/static/umap/locale/eu.js +23 -24
  104. umap/static/umap/locale/eu.json +23 -24
  105. umap/static/umap/locale/fa_IR.js +7 -8
  106. umap/static/umap/locale/fa_IR.json +7 -8
  107. umap/static/umap/locale/fi.js +7 -8
  108. umap/static/umap/locale/fi.json +7 -8
  109. umap/static/umap/locale/fr.js +11 -12
  110. umap/static/umap/locale/fr.json +11 -12
  111. umap/static/umap/locale/gl.js +147 -148
  112. umap/static/umap/locale/gl.json +147 -148
  113. umap/static/umap/locale/he.js +7 -8
  114. umap/static/umap/locale/he.json +7 -8
  115. umap/static/umap/locale/hr.js +7 -8
  116. umap/static/umap/locale/hr.json +7 -8
  117. umap/static/umap/locale/hu.js +8 -9
  118. umap/static/umap/locale/hu.json +8 -9
  119. umap/static/umap/locale/id.js +7 -8
  120. umap/static/umap/locale/id.json +7 -8
  121. umap/static/umap/locale/is.js +7 -8
  122. umap/static/umap/locale/is.json +7 -8
  123. umap/static/umap/locale/it.js +7 -8
  124. umap/static/umap/locale/it.json +7 -8
  125. umap/static/umap/locale/ja.js +7 -8
  126. umap/static/umap/locale/ja.json +7 -8
  127. umap/static/umap/locale/ko.js +7 -8
  128. umap/static/umap/locale/ko.json +7 -8
  129. umap/static/umap/locale/lt.js +7 -8
  130. umap/static/umap/locale/lt.json +7 -8
  131. umap/static/umap/locale/ms.js +7 -8
  132. umap/static/umap/locale/ms.json +7 -8
  133. umap/static/umap/locale/nl.js +7 -8
  134. umap/static/umap/locale/nl.json +7 -8
  135. umap/static/umap/locale/no.js +7 -8
  136. umap/static/umap/locale/no.json +7 -8
  137. umap/static/umap/locale/pl.js +53 -54
  138. umap/static/umap/locale/pl.json +53 -54
  139. umap/static/umap/locale/pl_PL.json +7 -8
  140. umap/static/umap/locale/pt.js +7 -8
  141. umap/static/umap/locale/pt.json +7 -8
  142. umap/static/umap/locale/pt_BR.js +7 -8
  143. umap/static/umap/locale/pt_BR.json +7 -8
  144. umap/static/umap/locale/pt_PT.js +7 -8
  145. umap/static/umap/locale/pt_PT.json +7 -8
  146. umap/static/umap/locale/ro.js +7 -8
  147. umap/static/umap/locale/ro.json +7 -8
  148. umap/static/umap/locale/ru.js +7 -8
  149. umap/static/umap/locale/ru.json +7 -8
  150. umap/static/umap/locale/sk_SK.js +7 -8
  151. umap/static/umap/locale/sk_SK.json +7 -8
  152. umap/static/umap/locale/sl.js +7 -8
  153. umap/static/umap/locale/sl.json +7 -8
  154. umap/static/umap/locale/sr.js +7 -8
  155. umap/static/umap/locale/sr.json +7 -8
  156. umap/static/umap/locale/sv.js +7 -8
  157. umap/static/umap/locale/sv.json +7 -8
  158. umap/static/umap/locale/th_TH.js +7 -8
  159. umap/static/umap/locale/th_TH.json +7 -8
  160. umap/static/umap/locale/tr.js +7 -8
  161. umap/static/umap/locale/tr.json +7 -8
  162. umap/static/umap/locale/uk_UA.js +7 -8
  163. umap/static/umap/locale/uk_UA.json +7 -8
  164. umap/static/umap/locale/vi.js +7 -8
  165. umap/static/umap/locale/vi.json +7 -8
  166. umap/static/umap/locale/vi_VN.json +7 -8
  167. umap/static/umap/locale/zh.js +7 -8
  168. umap/static/umap/locale/zh.json +7 -8
  169. umap/static/umap/locale/zh_CN.json +7 -8
  170. umap/static/umap/locale/zh_TW.Big5.json +7 -8
  171. umap/static/umap/locale/zh_TW.js +20 -21
  172. umap/static/umap/locale/zh_TW.json +20 -21
  173. umap/static/umap/map.css +6 -21
  174. umap/static/umap/unittests/utils.js +7 -7
  175. umap/static/umap/vendors/locatecontrol/L.Control.Locate.esm.js +942 -0
  176. umap/static/umap/vendors/photon/leaflet.photon.esm.js +472 -0
  177. umap/sync/app.py +4 -1
  178. umap/templates/umap/content_footer.html +1 -0
  179. umap/templates/umap/css.html +0 -4
  180. umap/templates/umap/js.html +1 -8
  181. umap/templates/umap/team_form.html +2 -1
  182. umap/tests/integration/conftest.py +3 -2
  183. umap/tests/integration/test_anonymous_owned_map.py +1 -1
  184. umap/tests/integration/test_conditional_rules.py +106 -51
  185. umap/tests/integration/test_draw_polygon.py +4 -0
  186. umap/tests/integration/test_draw_polyline.py +11 -0
  187. umap/tests/integration/test_edit_datalayer.py +1 -1
  188. umap/tests/integration/test_edit_map.py +2 -0
  189. umap/tests/integration/test_fields.py +19 -0
  190. umap/tests/integration/test_filters.py +24 -0
  191. umap/tests/integration/test_iframe.py +1 -1
  192. umap/tests/integration/test_import.py +26 -0
  193. umap/tests/integration/test_map.py +3 -3
  194. umap/tests/integration/test_optimistic_merge.py +7 -1
  195. umap/tests/integration/test_owned_map.py +2 -2
  196. umap/tests/integration/test_popup.py +31 -0
  197. umap/tests/integration/test_remote_data.py +5 -5
  198. umap/tests/integration/test_search.py +41 -0
  199. umap/tests/integration/test_share.py +2 -2
  200. umap/tests/integration/test_team.py +1 -1
  201. umap/tests/integration/test_websocket_sync.py +6 -1
  202. umap/tests/test_search_maps_command.py +44 -0
  203. umap/tests/test_utils.py +4 -1
  204. umap/utils.py +10 -3
  205. umap/views.py +17 -4
  206. {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/METADATA +29 -23
  207. {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/RECORD +210 -214
  208. {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/WHEEL +1 -1
  209. umap/static/umap/js/umap.core.js +0 -93
  210. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
  211. umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
  212. umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
  213. umap/static/umap/vendors/hash/leaflet-hash.js +0 -162
  214. umap/static/umap/vendors/loading/Control.Loading.css +0 -26
  215. umap/static/umap/vendors/loading/Control.Loading.js +0 -351
  216. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css +0 -1
  217. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css.map +0 -1
  218. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +0 -4
  219. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +0 -1
  220. umap/static/umap/vendors/photon/leaflet.photon.js +0 -487
  221. {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/entry_points.txt +0 -0
  222. {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,15 @@
1
1
  // Goes here all code related to Leaflet, DOM and user interactions.
2
2
  import {
3
3
  Map as BaseMap,
4
+ Browser,
4
5
  Control,
5
6
  DomEvent,
6
- DomUtil,
7
7
  latLng,
8
- latLngBounds,
8
+ LatLng,
9
+ LatLngBounds,
10
+ stamp,
9
11
  setOptions,
12
+ TileLayer,
10
13
  } from '../../../vendors/leaflet/leaflet-src.esm.js'
11
14
  import { uMapAlert as Alert } from '../../components/alerts/alert.js'
12
15
  import DropControl from '../drop.js'
@@ -18,6 +21,7 @@ import {
18
21
  EmbedControl,
19
22
  EditControl,
20
23
  HomeControl,
24
+ LocateControl,
21
25
  MoreControl,
22
26
  PermanentCreditsControl,
23
27
  TileLayerChooser,
@@ -46,7 +50,6 @@ const ControlsMixin = {
46
50
  'caption',
47
51
  'locate',
48
52
  'measure',
49
- 'editinosm',
50
53
  'print',
51
54
  'tilelayers',
52
55
  ],
@@ -68,19 +71,7 @@ const ControlsMixin = {
68
71
  })
69
72
  this._controls.datalayers = new DataLayersControl(this._umap)
70
73
  this._controls.caption = new CaptionControl(this._umap)
71
- this._controls.locate = new U.Locate(this, {
72
- strings: {
73
- title: translate('Center map on your location'),
74
- },
75
- showPopup: false,
76
- // We style this control in our own CSS for consistency with other controls,
77
- // but the control breaks if we don't specify a class here, so a fake class
78
- // will do.
79
- icon: 'umap-fake-class',
80
- iconLoading: 'umap-fake-class',
81
- flyTo: this.options.easing,
82
- onLocationError: (err) => U.Alert.error(err.message),
83
- })
74
+ this._controls.locate = new LocateControl(this._umap)
84
75
  this._controls.fullscreen = new Control.Fullscreen({
85
76
  title: {
86
77
  false: translate('View Fullscreen'),
@@ -91,17 +82,9 @@ const ControlsMixin = {
91
82
  this._controls.embed = new EmbedControl(this._umap)
92
83
  this._controls.print = new PrintControl(this._umap)
93
84
  this._controls.tilelayersChooser = new TileLayerChooser(this._umap)
94
- this._controls.editinosm = new Control.EditInOSM({
95
- position: 'topleft',
96
- widgetOptions: {
97
- helpText: translate(
98
- 'Open this map extent in a map editor to provide more accurate data to OpenStreetMap'
99
- ),
100
- },
101
- })
102
85
  this._controls.measure = new L.MeasureControl().initHandler(this)
103
86
  this._controls.more = new MoreControl()
104
- this._controls.scale = L.control.scale()
87
+ this._controls.scale = new Control.Scale()
105
88
  this._controls.permanentCredit = new PermanentCreditsControl(this)
106
89
  this._umap.drop = new DropControl(this._umap, this, this._container)
107
90
  this._controls.tilelayers = new U.TileLayerControl(this)
@@ -138,11 +121,10 @@ const ControlsMixin = {
138
121
  const control = this._controls[name]
139
122
  if (!control) continue
140
123
  control.addTo(this)
141
- if (status === undefined || status === null) {
142
- DomUtil.addClass(control._container, 'display-on-more')
143
- } else {
144
- DomUtil.removeClass(control._container, 'display-on-more')
145
- }
124
+ control._container.classList.toggle(
125
+ 'display-on-more',
126
+ status === undefined || status === null
127
+ )
146
128
  }
147
129
  if (this._umap.getProperty('permanentCredit'))
148
130
  this._controls.permanentCredit.addTo(this)
@@ -176,13 +158,25 @@ const ManageTilelayerMixin = {
176
158
  if (this._controls) this._controls.tilelayers.setLayers()
177
159
  },
178
160
 
179
- createTileLayer: (tilelayer) => new L.TileLayer(tilelayer.url_template, tilelayer),
161
+ createTileLayer: (tilelayer) => new TileLayer(tilelayer.url_template, tilelayer),
180
162
 
181
163
  selectTileLayer: function (tilelayer) {
182
164
  if (tilelayer === this.selectedTilelayer) {
183
165
  return
184
166
  }
167
+ const onLoading = () => {
168
+ this._umap.loader.start(stamp(tilelayer))
169
+ }
170
+ const onLoad = () => {
171
+ this._umap.loader.stop(stamp(tilelayer))
172
+ }
185
173
  try {
174
+ tilelayer.on('loading', onLoading)
175
+ tilelayer.on('load', onLoad)
176
+ tilelayer.on('remove', () => {
177
+ tilelayer.off('loading', onLoading)
178
+ tilelayer.off('load', onLoad)
179
+ })
186
180
  this.addLayer(tilelayer)
187
181
  this.fire('baselayerchange', { layer: tilelayer })
188
182
  if (this.selectedTilelayer) {
@@ -261,19 +255,13 @@ export const LeafletMap = BaseMap.extend({
261
255
 
262
256
  BaseMap.prototype.initialize.call(this, element, options)
263
257
 
264
- // After calling parent initialize, as we are doing initCenter our-selves
265
-
266
- this.loader = new Control.Loading()
267
- this.loader.onAdd(this)
268
-
269
- if (!this.options.noControl) {
270
- DomEvent.on(document.body, 'dataloading', (event) =>
271
- this.fire('dataloading', event.detail)
272
- )
273
- DomEvent.on(document.body, 'dataload', (event) =>
274
- this.fire('dataload', event.detail)
275
- )
276
- }
258
+ document.body.addEventListener('mapview:update', (event) => {
259
+ let { zoom, latlng } = event.detail
260
+ if (!Utils.LatLngIsValid(latlng)) return
261
+ zoom = Math.min(zoom, this.getMaxZoom())
262
+ zoom = Math.max(zoom, this.getMinZoom())
263
+ this.setView(latlng, zoom)
264
+ })
277
265
 
278
266
  this.on('baselayerchange', (e) => {
279
267
  if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
@@ -284,6 +272,21 @@ export const LeafletMap = BaseMap.extend({
284
272
  this.initControls()
285
273
  // Needs locate control and hash to exist
286
274
  this.initCenter()
275
+
276
+ // Wait for URL to have been parsed before modifying the hash
277
+ const updateHash = () => {
278
+ const center = this.getCenter()
279
+ document.body.dispatchEvent(
280
+ new CustomEvent('mapview:updated', {
281
+ detail: {
282
+ zoom: this.getZoom(),
283
+ latlng: [center.lat.toFixed(6), center.lng.toFixed(6)],
284
+ },
285
+ })
286
+ )
287
+ }
288
+ this.on('moveend', updateHash)
289
+ updateHash()
287
290
  this.initTileLayers()
288
291
  this.renderUI()
289
292
  },
@@ -303,7 +306,7 @@ export const LeafletMap = BaseMap.extend({
303
306
  // when scrolling the main page and touching the
304
307
  // map in an iframe. May be a bit dumb, but let's
305
308
  // try like this for now.
306
- if (L.Browser.mobile) this.dragging.disable()
309
+ if (Browser.mobile) this.dragging.disable()
307
310
  }
308
311
  // Needs tilelayer to exist for minimap
309
312
  this.renderControls()
@@ -312,7 +315,7 @@ export const LeafletMap = BaseMap.extend({
312
315
 
313
316
  latLng: (a, b, c) => {
314
317
  // manage geojson case and call original method
315
- if (!(a instanceof L.LatLng) && a.coordinates) {
318
+ if (!(a instanceof LatLng) && a.coordinates) {
316
319
  // Guess it's a geojson
317
320
  a = [a.coordinates[1], a.coordinates[0]]
318
321
  }
@@ -324,14 +327,13 @@ export const LeafletMap = BaseMap.extend({
324
327
  this.setView(this.options.center, this.options.zoom)
325
328
  },
326
329
 
327
- initCenter: function () {
330
+ initCenter: async function () {
328
331
  this._setDefaultCenter()
329
- if (this.options.hash) this.addHash()
330
- if (this.options.hash && this._hash.parseHash(location.hash)) {
332
+ if (this.options.hash && window.location.hash) {
331
333
  // FIXME An invalid hash will cause the load to fail
332
- this._hash.update()
334
+ this._umap.hash.parse()
333
335
  } else if (this.options.defaultView === 'locate' && !this.options.noControl) {
334
- this._controls.locate.start()
336
+ await this._controls.locate.start()
335
337
  } else if (this.options.defaultView === 'data') {
336
338
  this._umap.onceDataLoaded(this._umap.fitDataBounds)
337
339
  } else if (this.options.defaultView === 'latest') {
@@ -351,17 +353,17 @@ export const LeafletMap = BaseMap.extend({
351
353
  },
352
354
 
353
355
  handleLimitBounds: function () {
354
- const south = Number.parseFloat(this.options.limitBounds.south)
355
- const west = Number.parseFloat(this.options.limitBounds.west)
356
- const north = Number.parseFloat(this.options.limitBounds.north)
357
- const east = Number.parseFloat(this.options.limitBounds.east)
356
+ const south = Number.parseFloat(this.options.limitBounds?.south)
357
+ const west = Number.parseFloat(this.options.limitBounds?.west)
358
+ const north = Number.parseFloat(this.options.limitBounds?.north)
359
+ const east = Number.parseFloat(this.options.limitBounds?.east)
358
360
  if (
359
361
  !Number.isNaN(south) &&
360
362
  !Number.isNaN(west) &&
361
363
  !Number.isNaN(north) &&
362
364
  !Number.isNaN(east)
363
365
  ) {
364
- const bounds = latLngBounds([
366
+ const bounds = new LatLngBounds([
365
367
  [south, west],
366
368
  [north, east],
367
369
  ])
@@ -381,7 +383,7 @@ export const LeafletMap = BaseMap.extend({
381
383
  setMaxBounds: function (bounds) {
382
384
  // Hack. Remove me when fix is released:
383
385
  // https://github.com/Leaflet/Leaflet/pull/4494
384
- bounds = latLngBounds(bounds)
386
+ bounds = new LatLngBounds(bounds)
385
387
 
386
388
  if (!bounds.isValid()) {
387
389
  this.options.maxBounds = null
@@ -390,6 +392,14 @@ export const LeafletMap = BaseMap.extend({
390
392
  return BaseMap.prototype.setMaxBounds.call(this, bounds)
391
393
  },
392
394
 
395
+ getLayersBounds: (layers) => {
396
+ const bounds = new LatLngBounds()
397
+ for (const layer of layers) {
398
+ bounds.extend(layer.getBounds())
399
+ }
400
+ return bounds
401
+ },
402
+
393
403
  initEditTools: function () {
394
404
  this.editTools = new U.Editable(this._umap)
395
405
  },
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  Popup as BasePopup,
3
3
  DomEvent,
4
- DomUtil,
5
4
  Path,
6
5
  } from '../../../vendors/leaflet/leaflet-src.esm.js'
7
6
  import Browser from '../browser.js'
8
7
  import loadTemplate from './template.js'
8
+ import * as DOMUtils from '../domutils.js'
9
9
 
10
10
  export default function loadPopup(name) {
11
11
  switch (name) {
@@ -25,7 +25,8 @@ const Popup = BasePopup.extend({
25
25
  },
26
26
 
27
27
  loadContent: async function () {
28
- const container = DomUtil.create('div', 'umap-popup')
28
+ const container = document.createElement('div')
29
+ container.classList.add('umap-popup')
29
30
  const name = this.feature.getOption('popupTemplate')
30
31
  this.content = await loadTemplate(name, this.feature, container)
31
32
  const elements = container.querySelectorAll('img,iframe')
@@ -34,7 +35,9 @@ const Popup = BasePopup.extend({
34
35
  }
35
36
  if (!elements.length && container.textContent.replace('\n', '') === '') {
36
37
  container.innerHTML = ''
37
- DomUtil.add('h3', '', container, this.feature.getDisplayName())
38
+ container.appendChild(
39
+ DOMUtils.loadTemplate(`<h3>${this.feature.getDisplayName()}</h3>`)
40
+ )
38
41
  }
39
42
  this.setContent(container)
40
43
  },
@@ -1,8 +1,4 @@
1
- import {
2
- DomEvent,
3
- DomUtil,
4
- CircleMarker,
5
- } from '../../../vendors/leaflet/leaflet-src.esm.js'
1
+ import { DomEvent, CircleMarker } from '../../../vendors/leaflet/leaflet-src.esm.js'
6
2
  import { getLocale, translate } from '../i18n.js'
7
3
  import { Request } from '../request.js'
8
4
  import * as Utils from '../utils.js'
@@ -38,8 +34,7 @@ export default async function loadTemplate(name, feature, container) {
38
34
  class PopupTemplate {
39
35
  renderTitle(feature) {}
40
36
 
41
- renderBody(feature) {
42
- const template = feature.getOption('popupContentTemplate')
37
+ toHTML(feature, template) {
43
38
  const target = feature.getOption('outlinkTarget')
44
39
  const properties = feature.extendedProperties()
45
40
  // Resolve properties inside description
@@ -48,9 +43,15 @@ class PopupTemplate {
48
43
  properties
49
44
  )
50
45
  properties.name = properties.name ?? feature.getDisplayName()
51
- let content = Utils.greedyTemplate(template, properties)
52
- content = Utils.toHTML(content, { target: target })
53
- return Utils.loadTemplate(`<div class="umap-popup-container text">${content}</div>`)
46
+ const content = Utils.greedyTemplate(template, properties)
47
+ return Utils.toHTML(content, { target })
48
+ }
49
+
50
+ renderBody(feature) {
51
+ const template = feature.getOption('popupContentTemplate')
52
+ return Utils.loadTemplate(
53
+ `<div class="umap-popup-container text">${this.toHTML(feature, template)}</div>`
54
+ )
54
55
  }
55
56
 
56
57
  renderFooter(feature) {
@@ -92,7 +93,11 @@ class PopupTemplate {
92
93
  const title = this.renderTitle(feature)
93
94
  if (title) container.appendChild(title)
94
95
  const body = await this.renderBody(feature)
95
- if (body) DomUtil.add('div', 'umap-popup-content', container, body)
96
+ if (body) {
97
+ const div = DOMUtils.loadTemplate('<div class="umap-popup-content"></div>')
98
+ div.appendChild(body)
99
+ container.appendChild(div)
100
+ }
96
101
  const footer = this.renderFooter(feature)
97
102
  if (footer) container.appendChild(footer)
98
103
  }
@@ -108,18 +113,9 @@ export const TitleMixin = (Base) =>
108
113
  }
109
114
 
110
115
  class Table extends TitleMixin(PopupTemplate) {
111
- getValue(feature, key) {
112
- // TODO, manage links (url, mailto, wikipedia...)
113
- const value = Utils.escapeHTML(feature.properties[key]).trim()
114
- if (value.indexOf('http') === 0) {
115
- return `<a href="${value}" target="_blank">${value}</a>`
116
- }
117
- return value
118
- }
119
-
120
- makeRow(feature, key) {
116
+ makeRow(feature, field) {
121
117
  return Utils.loadTemplate(
122
- `<tr><th>${key}</th><td>${this.getValue(feature, key)}</td></tr>`
118
+ `<tr><th>${field.key}</th><td>${field.render(feature.properties[field.key])}</td></tr>`
123
119
  )
124
120
  }
125
121
 
@@ -130,7 +126,7 @@ class Table extends TitleMixin(PopupTemplate) {
130
126
  if (U.LABEL_KEYS.includes(field.key)) {
131
127
  continue
132
128
  }
133
- table.appendChild(this.makeRow(feature, field.key))
129
+ table.appendChild(this.makeRow(feature, field))
134
130
  }
135
131
  return table
136
132
  }
@@ -138,16 +134,17 @@ class Table extends TitleMixin(PopupTemplate) {
138
134
 
139
135
  class GeoRSSImage extends TitleMixin(PopupTemplate) {
140
136
  async renderBody(feature) {
141
- const body = DomUtil.create('a')
142
- body.href = feature.properties.link
143
- body.target = '_blank'
137
+ const body = DOMUtils.loadTemplate(
138
+ `<a href="${feature.properties.link}" target="_blank"></a>`
139
+ )
144
140
  if (feature.properties.img) {
145
- const img = DomUtil.create('img', '', body)
146
- img.src = feature.properties.img
147
141
  // Sadly, we are unable to override this from JS the clean way
148
142
  // See https://github.com/Leaflet/Leaflet/commit/61d746818b99d362108545c151a27f09d60960ee#commitcomment-6061847
149
- img.style.maxWidth = '500px'
150
- img.style.maxHeight = '500px'
143
+ body.appendChild(
144
+ DOMUtils.loadTemplate(
145
+ `<img src=${feature.properties.img} style="max-width: 500px; max-height: 500px;">`
146
+ )
147
+ )
151
148
  }
152
149
  return body
153
150
  }
@@ -165,15 +162,16 @@ class GeoRSSLink extends PopupTemplate {
165
162
 
166
163
  class OSM extends PopupTemplate {
167
164
  renderTitle(feature) {
168
- const title = DomUtil.add('h3', 'popup-title')
169
165
  const color = feature.getPreviewColor()
170
- title.style.backgroundColor = color
166
+ const [title, { iconContainer }] = DOMUtils.loadTemplateWithRefs(`
167
+ <h3 class="popup-title" style="background-color: ${color};">
168
+ <span data-ref="iconContainer"></span> ${this.getName(feature)}
169
+ </h3>`)
171
170
  const iconUrl = feature.getDynamicOption('iconUrl')
172
- const icon = Icon.makeElement(iconUrl, title)
173
- DomUtil.addClass(icon, 'icon')
171
+ const icon = Icon.makeElement(iconUrl, iconContainer)
172
+ icon.classList.add('icon')
174
173
  Icon.setContrast(icon, title, iconUrl, color)
175
174
  if (DOMUtils.contrastedColor(title, color)) title.style.color = 'white'
176
- DomUtil.add('span', '', title, this.getName(feature))
177
175
  return title
178
176
  }
179
177
 
@@ -190,15 +188,16 @@ class OSM extends PopupTemplate {
190
188
  const locale = getLocale()
191
189
  const street = props['addr:street']
192
190
  if (street) {
193
- const row = DomUtil.add('address', 'address', body)
194
191
  const number = props['addr:housenumber']
192
+ let content
195
193
  if (number) {
196
194
  // Poor way to deal with international forms of writing addresses
197
- DomUtil.add('span', '', row, `${translate('No.')}: ${number}`)
198
- DomUtil.add('span', '', row, `${translate('Street')}: ${street}`)
195
+ content = `<span>${translate('')}: ${number}</span><span>${translate('Street')}: ${street}</span>`
199
196
  } else {
200
- DomUtil.add('span', '', row, street)
197
+ content = street
201
198
  }
199
+ const row = DOMUtils.loadTemplate(`<address class="address">${content}</address>`)
200
+ body.appendChild(row)
202
201
  }
203
202
  if (props.website) {
204
203
  body.appendChild(
@@ -383,7 +382,8 @@ class Route extends TitleMixin(PopupTemplate) {
383
382
  }).addTo(map)
384
383
  })
385
384
  if (feature.properties.description) {
386
- root.appendChild(Utils.loadTemplate(`<p>${feature.properties.description}</p>`))
385
+ const content = this.toHTML(feature, feature.properties.description)
386
+ root.appendChild(Utils.loadTemplate(`<p>${content}</p>`))
387
387
  }
388
388
  return root
389
389
  }
@@ -2,7 +2,6 @@
2
2
  import {
3
3
  CircleMarker as BaseCircleMarker,
4
4
  DomEvent,
5
- DomUtil,
6
5
  GeoJSON,
7
6
  LatLng,
8
7
  LatLngBounds,
@@ -314,7 +313,7 @@ const PathMixin = {
314
313
  this.enableEdit()
315
314
  } else {
316
315
  this._map._umap.tooltip.open({
317
- content: L._('Please zoom in to edit the geometry'),
316
+ content: translate('Please zoom in to edit the geometry'),
318
317
  })
319
318
  this.disableEdit()
320
319
  }
@@ -6,6 +6,7 @@ import Orderable from './orderable.js'
6
6
  import * as Utils from './utils.js'
7
7
  import * as Icon from './rendering/icon.js'
8
8
  import { SCHEMA } from './schema.js'
9
+ import { Registry as Fields } from './data/fields.js'
9
10
 
10
11
  const EMPTY_VALUES = ['', undefined, null]
11
12
 
@@ -28,12 +29,12 @@ class Rule {
28
29
  // cf https://caniuse.com/?search=public%20class%20field
29
30
  this._condition = null
30
31
  this.OPERATORS = [
31
- ['>', this.gt],
32
- ['<', this.lt],
32
+ ['>', 'gt'],
33
+ ['<', 'lt'],
33
34
  // When sent by Django
34
- ['&lt;', this.lt],
35
- ['!=', this.not_equal],
36
- ['=', this.equal],
35
+ ['&lt;', 'lt'],
36
+ ['!=', 'not_equal'],
37
+ ['=', 'equal'],
37
38
  ]
38
39
  this.parent = parent
39
40
  this._umap = umap
@@ -47,58 +48,47 @@ class Rule {
47
48
  this.parent.render(fields)
48
49
  }
49
50
 
50
- equal(other) {
51
- return this.expected === other
52
- }
53
-
54
- not_equal(other) {
55
- return this.expected !== other
56
- }
57
-
58
- gt(other) {
59
- return other > this.expected
60
- }
61
-
62
- lt(other) {
63
- return other < this.expected
64
- }
65
-
66
51
  parse() {
67
52
  let vars = []
68
53
  this.cast = (v) => v
69
54
  this.operator = undefined
70
- for (const [sign, func] of this.OPERATORS) {
55
+ let operator = undefined
56
+ for (const [sign, funcName] of this.OPERATORS) {
71
57
  if (this.condition.includes(sign)) {
72
- this.operator = func
58
+ operator = funcName
73
59
  vars = this.condition.split(sign)
74
60
  break
75
61
  }
76
62
  }
77
63
  if (vars.length !== 2) return
78
- this.key = vars[0]
64
+ this.field = this.parent.fields.get(vars[0]) || new Fields.String(vars[0])
65
+ this.operator = this.field[operator]
79
66
  this.expected = vars[1]
80
67
  if (EMPTY_VALUES.includes(this.expected)) {
81
68
  this.cast = (v) => EMPTY_VALUES.includes(v)
82
69
  }
83
- // Special cases where we want to be lousy when checking isNaN without
84
- // coercing to a Number first because we handle multiple types.
85
- // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/
86
- // Reference/Global_Objects/Number/isNaN
87
- // biome-ignore lint/suspicious/noGlobalIsNan: expected might not be a number.
88
- else if (!isNaN(this.expected)) {
89
- this.cast = Number.parseFloat
90
- } else if (['true', 'false'].includes(this.expected)) {
91
- this.cast = (v) => {
92
- if (`${v}`.toLowerCase() === 'true') return true
93
- if (`${v}`.toLowerCase() === 'false') return false
70
+ // TODO: deal with legacy rules on non typed fields
71
+ else {
72
+ this.cast = this.field.parse
73
+ if (
74
+ // Special cases where we want to be lousy when checking isNaN without
75
+ // coercing to a Number first because we handle multiple types.
76
+ // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/
77
+ // Reference/Global_Objects/Number/isNaN
78
+ // biome-ignore lint/suspicious/noGlobalIsNan: expected might not be a number.
79
+ !isNaN(this.expected) &&
80
+ ['gt', 'lt'].includes(operator) &&
81
+ this.field.TYPE !== 'Number'
82
+ ) {
83
+ this.cast = Number.parseFloat
94
84
  }
95
85
  }
96
86
  this.expected = this.cast(this.expected)
97
87
  }
98
88
 
99
89
  match(props) {
100
- if (!this.operator || !this.active) return false
101
- return this.operator(this.cast(props[this.key]))
90
+ if (!this.operator || !this.active || !this.field) return false
91
+ return this.operator(this.expected, this.cast(props[this.field.key]))
102
92
  }
103
93
 
104
94
  getOption(option) {
@@ -118,6 +108,7 @@ class Rule {
118
108
  'name',
119
109
  'properties.color',
120
110
  'properties.iconClass',
111
+ 'properties.iconSize',
121
112
  'properties.iconUrl',
122
113
  'properties.iconOpacity',
123
114
  'properties.opacity',
@@ -132,7 +123,7 @@ class Rule {
132
123
  const container = document.createElement('div')
133
124
  container.appendChild(builder.build())
134
125
  const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
135
- const properties = this.parent.fieldKeys
126
+ const properties = Array.from(this.parent.fields.keys())
136
127
  autocomplete.suggestions = properties
137
128
  autocomplete.input.addEventListener('input', (event) => {
138
129
  const value = event.target.value
@@ -140,9 +131,11 @@ class Rule {
140
131
  autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
141
132
  } else if (value.endsWith('=')) {
142
133
  const key = value.split('!')[0].split('=')[0]
143
- autocomplete.suggestions = this.parent
144
- .sortedValues(key)
145
- .map((str) => `${value}${str ?? ''}`)
134
+ if (key) {
135
+ autocomplete.suggestions = this.parent
136
+ .sortedValues(key)
137
+ .map((str) => `${value}${str ?? ''}`)
138
+ }
146
139
  }
147
140
  })
148
141
  const backButton = Utils.loadTemplate(`
@@ -123,13 +123,6 @@ export const SCHEMA = {
123
123
  edit_status: {
124
124
  type: Number,
125
125
  },
126
- editinosmControl: {
127
- type: Boolean,
128
- impacts: ['ui'],
129
- nullable: true,
130
- label: translate('Display the control to open OpenStreetMap editor'),
131
- default: null,
132
- },
133
126
  editors: {
134
127
  type: Array,
135
128
  },