umap-project 2.1.2__py3-none-any.whl → 2.2.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.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (211) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +1 -0
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/en/LC_MESSAGES/django.po +32 -32
  5. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  8. umap/migrations/0020_alter_tilelayer_url_template.py +19 -0
  9. umap/migrations/0021_remove_map_description.py +16 -0
  10. umap/models.py +10 -6
  11. umap/settings/base.py +1 -0
  12. umap/static/umap/base.css +43 -156
  13. umap/static/umap/content.css +7 -25
  14. umap/static/umap/css/icon.css +112 -0
  15. umap/static/umap/css/panel.css +140 -0
  16. umap/static/umap/img/16-white.svg +5 -1
  17. umap/static/umap/img/16.svg +7 -4
  18. umap/static/umap/img/24-white.svg +3 -1
  19. umap/static/umap/img/24.svg +3 -4
  20. umap/static/umap/img/source/16-white.svg +176 -940
  21. umap/static/umap/img/source/16.svg +8 -5
  22. umap/static/umap/img/source/24-white.svg +5 -3
  23. umap/static/umap/img/source/24.svg +6 -7
  24. umap/static/umap/js/modules/browser.js +97 -73
  25. umap/static/umap/js/modules/dompurify.js +12 -0
  26. umap/static/umap/js/modules/facets.js +149 -0
  27. umap/static/umap/js/modules/global.js +9 -1
  28. umap/static/umap/js/modules/i18n.js +7 -0
  29. umap/static/umap/js/modules/orderable.js +84 -0
  30. umap/static/umap/js/modules/panel.js +76 -0
  31. umap/static/umap/js/modules/request.js +0 -1
  32. umap/static/umap/js/modules/schema.js +324 -223
  33. umap/static/umap/js/modules/urls.js +1 -16
  34. umap/static/umap/js/modules/utils.js +340 -0
  35. umap/static/umap/js/umap.autocomplete.js +40 -25
  36. umap/static/umap/js/umap.controls.js +248 -361
  37. umap/static/umap/js/umap.core.js +77 -366
  38. umap/static/umap/js/umap.datalayer.permissions.js +1 -1
  39. umap/static/umap/js/umap.features.js +65 -43
  40. umap/static/umap/js/umap.forms.js +128 -36
  41. umap/static/umap/js/umap.icon.js +11 -4
  42. umap/static/umap/js/umap.importer.js +78 -58
  43. umap/static/umap/js/umap.js +206 -192
  44. umap/static/umap/js/umap.layer.js +86 -46
  45. umap/static/umap/js/umap.permissions.js +13 -9
  46. umap/static/umap/js/umap.popup.js +26 -30
  47. umap/static/umap/js/umap.share.js +12 -9
  48. umap/static/umap/js/umap.tableeditor.js +4 -6
  49. umap/static/umap/js/umap.ui.js +10 -60
  50. umap/static/umap/locale/am_ET.js +243 -227
  51. umap/static/umap/locale/am_ET.json +21 -9
  52. umap/static/umap/locale/ar.js +243 -227
  53. umap/static/umap/locale/ar.json +21 -9
  54. umap/static/umap/locale/ast.js +243 -227
  55. umap/static/umap/locale/ast.json +21 -9
  56. umap/static/umap/locale/bg.js +243 -227
  57. umap/static/umap/locale/bg.json +21 -9
  58. umap/static/umap/locale/br.js +253 -237
  59. umap/static/umap/locale/br.json +25 -13
  60. umap/static/umap/locale/ca.js +243 -227
  61. umap/static/umap/locale/ca.json +21 -9
  62. umap/static/umap/locale/cs_CZ.js +243 -227
  63. umap/static/umap/locale/cs_CZ.json +21 -9
  64. umap/static/umap/locale/da.js +243 -227
  65. umap/static/umap/locale/da.json +21 -9
  66. umap/static/umap/locale/de.js +243 -227
  67. umap/static/umap/locale/de.json +21 -9
  68. umap/static/umap/locale/el.js +243 -227
  69. umap/static/umap/locale/el.json +21 -9
  70. umap/static/umap/locale/en.js +243 -234
  71. umap/static/umap/locale/en.json +22 -10
  72. umap/static/umap/locale/en_US.json +21 -9
  73. umap/static/umap/locale/es.js +243 -227
  74. umap/static/umap/locale/es.json +21 -9
  75. umap/static/umap/locale/et.js +243 -227
  76. umap/static/umap/locale/et.json +21 -9
  77. umap/static/umap/locale/eu.js +227 -199
  78. umap/static/umap/locale/eu.json +1 -1
  79. umap/static/umap/locale/fa_IR.js +243 -227
  80. umap/static/umap/locale/fa_IR.json +21 -9
  81. umap/static/umap/locale/fi.js +243 -227
  82. umap/static/umap/locale/fi.json +21 -9
  83. umap/static/umap/locale/fr.js +243 -234
  84. umap/static/umap/locale/fr.json +21 -9
  85. umap/static/umap/locale/gl.js +243 -227
  86. umap/static/umap/locale/gl.json +21 -9
  87. umap/static/umap/locale/he.js +243 -227
  88. umap/static/umap/locale/he.json +21 -9
  89. umap/static/umap/locale/hr.js +243 -227
  90. umap/static/umap/locale/hr.json +21 -9
  91. umap/static/umap/locale/hu.js +243 -234
  92. umap/static/umap/locale/hu.json +21 -9
  93. umap/static/umap/locale/id.js +243 -227
  94. umap/static/umap/locale/id.json +21 -9
  95. umap/static/umap/locale/is.js +243 -227
  96. umap/static/umap/locale/is.json +21 -9
  97. umap/static/umap/locale/it.js +243 -234
  98. umap/static/umap/locale/it.json +21 -9
  99. umap/static/umap/locale/ja.js +243 -227
  100. umap/static/umap/locale/ja.json +21 -9
  101. umap/static/umap/locale/ko.js +243 -227
  102. umap/static/umap/locale/ko.json +21 -9
  103. umap/static/umap/locale/lt.js +243 -227
  104. umap/static/umap/locale/lt.json +21 -9
  105. umap/static/umap/locale/ms.js +243 -234
  106. umap/static/umap/locale/ms.json +22 -10
  107. umap/static/umap/locale/nl.js +246 -230
  108. umap/static/umap/locale/nl.json +21 -9
  109. umap/static/umap/locale/no.js +243 -227
  110. umap/static/umap/locale/no.json +21 -9
  111. umap/static/umap/locale/pl.js +243 -227
  112. umap/static/umap/locale/pl.json +21 -9
  113. umap/static/umap/locale/pl_PL.json +21 -9
  114. umap/static/umap/locale/pt.js +243 -227
  115. umap/static/umap/locale/pt.json +21 -9
  116. umap/static/umap/locale/pt_BR.js +243 -227
  117. umap/static/umap/locale/pt_BR.json +21 -9
  118. umap/static/umap/locale/pt_PT.js +243 -227
  119. umap/static/umap/locale/pt_PT.json +21 -9
  120. umap/static/umap/locale/ro.js +243 -227
  121. umap/static/umap/locale/ro.json +21 -9
  122. umap/static/umap/locale/ru.js +243 -227
  123. umap/static/umap/locale/ru.json +21 -9
  124. umap/static/umap/locale/si.js +1 -1
  125. umap/static/umap/locale/si.json +1 -1
  126. umap/static/umap/locale/sk_SK.js +243 -227
  127. umap/static/umap/locale/sk_SK.json +21 -9
  128. umap/static/umap/locale/sl.js +243 -227
  129. umap/static/umap/locale/sl.json +21 -9
  130. umap/static/umap/locale/sr.js +243 -227
  131. umap/static/umap/locale/sr.json +21 -9
  132. umap/static/umap/locale/sv.js +243 -227
  133. umap/static/umap/locale/sv.json +21 -9
  134. umap/static/umap/locale/th_TH.js +243 -227
  135. umap/static/umap/locale/th_TH.json +21 -9
  136. umap/static/umap/locale/tr.js +243 -227
  137. umap/static/umap/locale/tr.json +21 -9
  138. umap/static/umap/locale/uk_UA.js +243 -227
  139. umap/static/umap/locale/uk_UA.json +21 -9
  140. umap/static/umap/locale/vi.js +243 -227
  141. umap/static/umap/locale/vi.json +21 -9
  142. umap/static/umap/locale/vi_VN.json +21 -9
  143. umap/static/umap/locale/zh.js +243 -227
  144. umap/static/umap/locale/zh.json +21 -9
  145. umap/static/umap/locale/zh_CN.json +21 -9
  146. umap/static/umap/locale/zh_TW.Big5.json +21 -9
  147. umap/static/umap/locale/zh_TW.js +243 -234
  148. umap/static/umap/locale/zh_TW.json +21 -9
  149. umap/static/umap/map.css +124 -264
  150. umap/static/umap/test/DataLayer.js +1 -1
  151. umap/static/umap/test/Feature.js +0 -226
  152. umap/static/umap/test/Map.js +0 -304
  153. umap/static/umap/test/Polygon.js +0 -256
  154. umap/static/umap/test/Polyline.js +0 -116
  155. umap/static/umap/test/TableEditor.js +10 -10
  156. umap/static/umap/test/Util.js +0 -521
  157. umap/static/umap/test/index.html +1 -5
  158. umap/static/umap/unittests/URLs.js +1 -1
  159. umap/static/umap/unittests/utils.js +610 -0
  160. umap/static/umap/vars.css +9 -0
  161. umap/static/umap/vendors/dompurify/purify.es.mjs +1525 -0
  162. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +1 -0
  163. umap/static/umap/vendors/iconlayers/iconLayers.js +1 -1
  164. umap/templates/umap/css.html +2 -0
  165. umap/templates/umap/js.html +0 -1
  166. umap/templates/umap/map_detail.html +4 -0
  167. umap/templates/umap/map_table.html +12 -10
  168. umap/templatetags/umap_tags.py +5 -0
  169. umap/tests/conftest.py +9 -0
  170. umap/tests/fixtures/test_upload_data.csv +2 -1
  171. umap/tests/fixtures/test_upload_data.umap +171 -0
  172. umap/tests/fixtures/test_upload_data_osm.json +33 -0
  173. umap/tests/integration/conftest.py +16 -0
  174. umap/tests/integration/test_anonymous_owned_map.py +30 -5
  175. umap/tests/integration/test_basics.py +21 -0
  176. umap/tests/integration/test_browser.py +16 -36
  177. umap/tests/integration/test_choropleth.py +89 -0
  178. umap/tests/integration/test_collaborative_editing.py +30 -1
  179. umap/tests/integration/test_dashboard.py +10 -0
  180. umap/tests/integration/test_datalayer.py +132 -0
  181. umap/tests/integration/test_draw_polygon.py +363 -0
  182. umap/tests/integration/test_draw_polyline.py +325 -0
  183. umap/tests/integration/test_edit_datalayer.py +145 -6
  184. umap/tests/integration/test_edit_map.py +202 -0
  185. umap/tests/integration/test_edit_marker.py +120 -0
  186. umap/tests/integration/test_edit_polygon.py +122 -0
  187. umap/tests/integration/test_facets_browser.py +132 -11
  188. umap/tests/integration/test_import.py +407 -10
  189. umap/tests/integration/test_map.py +36 -54
  190. umap/tests/integration/test_map_list.py +28 -0
  191. umap/tests/integration/test_owned_map.py +24 -6
  192. umap/tests/integration/test_picto.py +25 -38
  193. umap/tests/integration/test_querystring.py +9 -15
  194. umap/tests/integration/test_slideshow.py +0 -5
  195. umap/tests/integration/test_statics.py +3 -2
  196. umap/tests/integration/test_tableeditor.py +23 -0
  197. umap/tests/integration/test_tilelayer.py +10 -0
  198. umap/tests/integration/test_view_marker.py +64 -0
  199. umap/tests/integration/test_view_polygon.py +59 -0
  200. umap/tests/integration/test_view_polyline.py +51 -0
  201. umap/tests/test_map_views.py +13 -0
  202. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/METADATA +12 -12
  203. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/RECORD +206 -187
  204. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/WHEEL +1 -1
  205. umap/static/umap/test/Choropleth.js +0 -245
  206. umap/static/umap/test/Permissions.js +0 -74
  207. umap/static/umap/vendors/dompurify/purify.min.js +0 -3
  208. umap/static/umap/vendors/dompurify/purify.min.js.map +0 -1
  209. umap/tests/integration/test_drawing.py +0 -243
  210. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/entry_points.txt +0 -0
  211. {umap_project-2.1.2.dist-info → umap_project-2.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -56,6 +56,11 @@ U.Map = L.Map.extend({
56
56
  if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
57
57
  this.urls = new U.URLs(this.options.urls)
58
58
 
59
+ this.panel = new U.Panel(this)
60
+ if (this.hasEditMode()) {
61
+ this.editPanel = new U.EditPanel(this)
62
+ this.fullPanel = new U.FullPanel(this)
63
+ }
59
64
  this.ui = new U.UI(this._container)
60
65
  this.ui.on('dataloading', (e) => this.fire('dataloading', e))
61
66
  this.ui.on('dataload', (e) => this.fire('dataload', e))
@@ -67,13 +72,15 @@ U.Map = L.Map.extend({
67
72
  this.description = this.options.description
68
73
  this.demoTileInfos = this.options.demoTileInfos
69
74
  this.options.zoomControl = zoomControl !== undefined ? zoomControl : true
70
- this.options.fullscreenControl = fullscreenControl !== undefined ? fullscreenControl : true
71
- this.datalayersOnLoad = L.Util.queryString('datalayers')
72
- if (this.datalayersOnLoad) {
73
- this.datalayersOnLoad = this.datalayersOnLoad.toString().split(',')
74
- }
75
+ this.options.fullscreenControl =
76
+ fullscreenControl !== undefined ? fullscreenControl : true
75
77
 
76
- if (L.Browser.ielt9) this.options.editMode = 'disabled' // TODO include ie9
78
+ this.datalayersFromQueryString = L.Util.queryString('datalayers')
79
+ if (this.datalayersFromQueryString) {
80
+ this.datalayersFromQueryString = this.datalayersFromQueryString
81
+ .toString()
82
+ .split(',')
83
+ }
77
84
 
78
85
  let editedFeature = null
79
86
  const self = this
@@ -99,27 +106,29 @@ U.Map = L.Map.extend({
99
106
  this.options.slideshow &&
100
107
  this.options.slideshow.delay &&
101
108
  this.options.slideshow.active === undefined
102
- )
109
+ ) {
103
110
  this.options.slideshow.active = true
104
- if (this.options.advancedFilterKey)
111
+ }
112
+ if (this.options.advancedFilterKey) {
105
113
  this.options.facetKey = this.options.advancedFilterKey
114
+ delete this.options.advancedFilterKey
115
+ }
106
116
 
107
117
  // Global storage for retrieving datalayers and features
108
118
  this.datalayers = {}
109
119
  this.datalayers_index = []
110
120
  this.dirty_datalayers = []
111
121
  this.features_index = {}
112
- this.facets = {}
113
122
 
114
123
  // Needed for actions labels
115
124
  this.help = new U.Help(this)
116
125
 
117
- if (this.options.hash) this.addHash()
118
- this.initTileLayers()
119
- // Needs tilelayer to exist for minimap
120
126
  this.initControls()
121
127
  // Needs locate control and hash to exist
122
128
  this.initCenter()
129
+ this.initTileLayers()
130
+ // Needs tilelayer to exist for minimap
131
+ this.renderControls()
123
132
  this.handleLimitBounds()
124
133
  this.initDataLayers()
125
134
 
@@ -137,14 +146,9 @@ U.Map = L.Map.extend({
137
146
  }
138
147
  delete this.options.displayDataBrowserOnLoad
139
148
  }
140
-
141
- this.ui.on(
142
- 'panel:closed',
143
- function () {
144
- this.invalidateSize({ pan: false })
145
- },
146
- this
147
- )
149
+ if (this.options.datalayersControl === 'expanded') {
150
+ this.options.onLoadPanel = 'datalayers'
151
+ }
148
152
 
149
153
  let isDirty = false // self status
150
154
  try {
@@ -194,30 +198,29 @@ U.Map = L.Map.extend({
194
198
 
195
199
  this.slideshow = new U.Slideshow(this, this.options.slideshow)
196
200
  this.permissions = new U.MapPermissions(this)
197
- this.initCaptionBar()
198
201
  if (this.hasEditMode()) {
199
202
  this.editTools = new U.Editable(this)
200
- this.ui.on(
201
- 'panel:closed panel:open',
202
- function () {
203
- this.editedFeature = null
204
- },
205
- this
206
- )
207
203
  this.renderEditToolbar()
208
204
  }
209
205
  this.initShortcuts()
210
206
  this.onceDataLoaded(function () {
211
- if (L.Util.queryString('share')) this.share.open()
212
- else if (this.options.onLoadPanel === 'databrowser') this.openBrowser()
213
- else if (this.options.onLoadPanel === 'caption') this.displayCaption()
214
- else if (
215
- this.options.onLoadPanel === 'facet' ||
216
- this.options.onLoadPanel === 'datafilters'
217
- )
218
- this.openFacet()
219
207
  const slug = L.Util.queryString('feature')
220
208
  if (slug && this.features_index[slug]) this.features_index[slug].view()
209
+ if (this.options.noControl) return
210
+ this.initCaptionBar()
211
+ if (L.Util.queryString('share')) {
212
+ this.share.open()
213
+ } else if (this.options.onLoadPanel === 'databrowser') {
214
+ this.openBrowser('expanded')
215
+ } else if (this.options.onLoadPanel === 'datalayers') {
216
+ this.openBrowser('condensed')
217
+ } else if (this.options.onLoadPanel === 'caption') {
218
+ this.panel.mode = 'condensed'
219
+ this.displayCaption()
220
+ } else if (['facet', 'datafilters'].includes(this.options.onLoadPanel)) {
221
+ this.panel.mode = 'expanded'
222
+ this.openFacet()
223
+ }
221
224
  if (L.Util.queryString('edit')) {
222
225
  if (this.hasEditMode()) this.enableEdit()
223
226
  // Sometimes users share the ?edit link by mistake, let's remove
@@ -240,6 +243,44 @@ U.Map = L.Map.extend({
240
243
  this.on('click contextmenu.show', this.closeInplaceToolbar)
241
244
  },
242
245
 
246
+ render: function (fields) {
247
+ let impacts = U.Utils.getImpactsFromSchema(fields)
248
+
249
+ for (let impact of impacts) {
250
+ switch (impact) {
251
+ case 'ui':
252
+ this.initCaptionBar()
253
+ this.renderEditToolbar()
254
+ this.renderControls()
255
+ this.facets.redraw()
256
+ break
257
+ case 'data':
258
+ this.redrawVisibleDataLayers()
259
+ break
260
+ case 'datalayer-index':
261
+ this.reindexDataLayers()
262
+ break
263
+ case 'background':
264
+ this.initTileLayers()
265
+ break
266
+ case 'bounds':
267
+ this.handleLimitBounds()
268
+ break
269
+ }
270
+ }
271
+ },
272
+
273
+ reindexDataLayers: function () {
274
+ this.eachDataLayer((datalayer) => datalayer.reindex())
275
+ this.onDataLayersChanged()
276
+ },
277
+
278
+ redrawVisibleDataLayers: function () {
279
+ this.eachVisibleDataLayer((datalayer) => {
280
+ datalayer.redraw()
281
+ })
282
+ },
283
+
243
284
  setOptionsFromQueryString: function (options) {
244
285
  // This is not an editable option
245
286
  L.Util.setFromQueryString(options, 'editMode')
@@ -263,14 +304,16 @@ U.Map = L.Map.extend({
263
304
  // Specific case for datalayersControl
264
305
  // which accepts "expanded" value, on top of true/false/null
265
306
  if (L.Util.queryString('datalayersControl') === 'expanded') {
266
- L.Util.setFromQueryString(options, 'datalayersControl')
307
+ options.onLoadPanel = 'datalayers'
267
308
  }
268
309
  },
269
310
 
311
+ // Merge the given schema with the default one
312
+ // Missing keys inside the schema are merged with the default ones.
270
313
  overrideSchema: function (schema) {
271
- for (const [key, extra] of Object.entries(schema)) {
272
- U.SCHEMA[key] = L.extend({}, U.SCHEMA[key], extra)
273
- }
314
+ for (const [key, extra] of Object.entries(schema)) {
315
+ U.SCHEMA[key] = L.extend({}, U.SCHEMA[key], extra)
316
+ }
274
317
  },
275
318
 
276
319
  initControls: function () {
@@ -281,23 +324,26 @@ U.Map = L.Map.extend({
281
324
  new U.EditControl(this).addTo(this)
282
325
 
283
326
  new U.DrawToolbar({ map: this }).addTo(this)
284
-
285
327
  const editActions = [
286
- U.ImportAction,
328
+ U.EditCaptionAction,
287
329
  U.EditPropertiesAction,
288
- U.ManageDatalayersAction,
330
+ U.EditLayersAction,
289
331
  U.ChangeTileLayerAction,
290
332
  U.UpdateExtentAction,
291
333
  U.UpdatePermsAction,
334
+ U.ImportAction,
292
335
  ]
293
- new U.SettingsToolbar({ actions: editActions }).addTo(this)
336
+ if (this.options.editMode === 'advanced') {
337
+ new U.SettingsToolbar({ actions: editActions }).addTo(this)
338
+ }
294
339
  }
295
340
  this._controls.zoom = new L.Control.Zoom({
296
341
  zoomInTitle: L._('Zoom in'),
297
342
  zoomOutTitle: L._('Zoom out'),
298
343
  })
299
344
  this._controls.datalayers = new U.DataLayersControl(this)
300
- this._controls.locate = L.control.locate({
345
+ this._controls.caption = new U.CaptionControl(this)
346
+ this._controls.locate = new U.Locate(this, {
301
347
  strings: {
302
348
  title: L._('Center map on your location'),
303
349
  },
@@ -332,13 +378,11 @@ U.Map = L.Map.extend({
332
378
  if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
333
379
  else this.scrollWheelZoom.disable()
334
380
  this.browser = new U.Browser(this)
381
+ this.facets = new U.Facets(this)
335
382
  this.importer = new U.Importer(this)
336
383
  this.drop = new U.DropControl(this)
337
384
  this.share = new U.Share(this)
338
385
  this._controls.tilelayers = new U.TileLayerControl(this)
339
- this._controls.tilelayers.setLayers()
340
-
341
- this.renderControls()
342
386
  },
343
387
 
344
388
  renderControls: function () {
@@ -353,13 +397,13 @@ U.Map = L.Map.extend({
353
397
  'umap-slideshow-enabled',
354
398
  this.options.slideshow && this.options.slideshow.active
355
399
  )
356
- for (const i in this._controls) {
357
- this.removeControl(this._controls[i])
400
+ for (const control of Object.values(this._controls)) {
401
+ this.removeControl(control)
358
402
  }
359
403
  if (this.options.noControl) return
360
404
 
361
405
  this._controls.attribution = new U.AttributionControl().addTo(this)
362
- if (this.options.miniMap && !this.options.noControl) {
406
+ if (this.options.miniMap) {
363
407
  this.whenReady(function () {
364
408
  if (this.selected_tilelayer) {
365
409
  this._controls.miniMap = new L.Control.MiniMap(this.selected_tilelayer, {
@@ -392,6 +436,7 @@ U.Map = L.Map.extend({
392
436
  if (this.getOption('permanentCredit')) this._controls.permanentCredit.addTo(this)
393
437
  if (this.getOption('moreControl')) this._controls.more.addTo(this)
394
438
  if (this.getOption('scaleControl')) this._controls.scale.addTo(this)
439
+ this._controls.tilelayers.setLayers()
395
440
  },
396
441
 
397
442
  initDataLayers: async function (datalayers) {
@@ -421,7 +466,11 @@ U.Map = L.Map.extend({
421
466
  if (!pane.dataset || !pane.dataset.id) continue
422
467
  this.datalayers_index.push(this.datalayers[pane.dataset.id])
423
468
  }
424
- this.updateDatalayersControl()
469
+ this.onDataLayersChanged()
470
+ },
471
+
472
+ onDataLayersChanged: function () {
473
+ if (this.browser) this.browser.update()
425
474
  },
426
475
 
427
476
  ensurePanesOrder: function () {
@@ -450,10 +499,6 @@ U.Map = L.Map.extend({
450
499
  return this
451
500
  },
452
501
 
453
- updateDatalayersControl: function () {
454
- if (this._controls.datalayers) this._controls.datalayers.update()
455
- },
456
-
457
502
  backupOptions: function () {
458
503
  this._backupOptions = L.extend({}, this.options)
459
504
  this._backupOptions.tilelayer = L.extend({}, this.options.tilelayer)
@@ -477,8 +522,13 @@ U.Map = L.Map.extend({
477
522
  L.DomEvent.stop(e)
478
523
  this.search()
479
524
  } else if (e.keyCode === U.Keys.ESC) {
480
- if (this.help.visible()) this.help.hide()
481
- else this.ui.closePanel()
525
+ if (this.help.visible()) {
526
+ this.help.hide()
527
+ } else {
528
+ this.panel.close()
529
+ this.editPanel?.close()
530
+ this.fullPanel?.close()
531
+ }
482
532
  }
483
533
 
484
534
  if (!this.hasEditMode()) return
@@ -490,7 +540,6 @@ U.Map = L.Map.extend({
490
540
  } else if (key === U.Keys.E && modifierKey && this.editEnabled && !this.isDirty) {
491
541
  L.DomEvent.stop(e)
492
542
  this.disableEdit()
493
- this.ui.closePanel()
494
543
  }
495
544
  if (key === U.Keys.S && modifierKey) {
496
545
  L.DomEvent.stop(e)
@@ -652,40 +701,28 @@ U.Map = L.Map.extend({
652
701
  },
653
702
 
654
703
  initCenter: function () {
704
+ this._setDefaultCenter()
705
+ if (this.options.hash) this.addHash()
655
706
  if (this.options.hash && this._hash.parseHash(location.hash)) {
656
707
  // FIXME An invalid hash will cause the load to fail
657
708
  this._hash.update()
658
709
  } else if (this.options.defaultView === 'locate' && !this.options.noControl) {
659
- // When using locate as default map view AND activating easing
660
- // Leaflet.locate will ask the map view to compute transition to user
661
- // position, so in this case we do need a default center, so let's
662
- // set it anyway
663
- this._setDefaultCenter()
664
710
  this._controls.locate.start()
665
711
  } else if (this.options.defaultView === 'data') {
666
- this.onceDataLoaded(() => {
667
- if (!this.fitDataBounds()) return this._setDefaultCenter()
668
- })
712
+ this.onceDataLoaded(this.fitDataBounds)
669
713
  } else if (this.options.defaultView === 'latest') {
670
714
  this.onceDataLoaded(() => {
671
- if (!this.hasData()) {
672
- this._setDefaultCenter()
673
- return
674
- }
715
+ if (!this.hasData()) return
675
716
  const datalayer = this.firstVisibleDatalayer()
676
717
  let feature
677
718
  if (datalayer) {
678
719
  const feature = datalayer.getFeatureByIndex(-1)
679
720
  if (feature) {
680
- feature.zoomTo()
721
+ feature.zoomTo({ callback: this.options.noControl ? null : feature.view })
681
722
  return
682
723
  }
683
724
  }
684
- // Fallback, no datalayer or no feature found
685
- this._setDefaultCenter()
686
725
  })
687
- } else {
688
- this._setDefaultCenter()
689
726
  }
690
727
  },
691
728
 
@@ -740,12 +777,17 @@ U.Map = L.Map.extend({
740
777
  return new U.DataLayer(this, datalayer)
741
778
  },
742
779
 
780
+ newDataLayer: function () {
781
+ const datalayer = this.createDataLayer({})
782
+ datalayer.edit()
783
+ },
784
+
743
785
  getDefaultOption: function (option) {
744
786
  return U.SCHEMA[option] && U.SCHEMA[option].default
745
787
  },
746
788
 
747
789
  getOption: function (option) {
748
- if (L.Util.usableOption(this.options, option)) return this.options[option]
790
+ if (U.Utils.usableOption(this.options, option)) return this.options[option]
749
791
  return this.getDefaultOption(option)
750
792
  },
751
793
 
@@ -777,10 +819,6 @@ U.Map = L.Map.extend({
777
819
  })
778
820
  },
779
821
 
780
- manageDatalayers: function () {
781
- if (this._controls.datalayers) this._controls.datalayers.openPanel()
782
- },
783
-
784
822
  toGeoJSON: function () {
785
823
  let features = []
786
824
  this.eachDataLayer((datalayer) => {
@@ -802,7 +840,7 @@ U.Map = L.Map.extend({
802
840
  },
803
841
 
804
842
  processFileToImport: function (file, layer, type) {
805
- type = type || L.Util.detectFileType(file)
843
+ type = type || U.Utils.detectFileType(file)
806
844
  if (!type) {
807
845
  this.ui.alert({
808
846
  content: L._('Unable to detect format of file {filename}', {
@@ -869,7 +907,8 @@ U.Map = L.Map.extend({
869
907
  }
870
908
  },
871
909
 
872
- openBrowser: function () {
910
+ openBrowser: function (mode) {
911
+ if (mode) this.panel.mode = mode
873
912
  this.onceDatalayersLoaded(function () {
874
913
  this.browser.open()
875
914
  })
@@ -924,9 +963,9 @@ U.Map = L.Map.extend({
924
963
  })
925
964
  this.ensurePanesOrder()
926
965
  this.dirty_datalayers = []
927
- this.updateDatalayersControl()
928
966
  this.initTileLayers()
929
967
  this.isDirty = false
968
+ this.onDataLayersChanged()
930
969
  },
931
970
 
932
971
  checkDirty: function () {
@@ -974,6 +1013,8 @@ U.Map = L.Map.extend({
974
1013
  formData.append('settings', JSON.stringify(geojson))
975
1014
  const uri = this.urls.get('map_save', { map_id: this.options.umap_id })
976
1015
  const [data, response, error] = await this.server.post(uri, {}, formData)
1016
+ // FIXME: login_required response will not be an error, so it will not
1017
+ // stop code while it should
977
1018
  if (!error) {
978
1019
  let duration = 3000,
979
1020
  alert = { content: L._('Map has been saved!'), level: 'info' }
@@ -982,11 +1023,7 @@ U.Map = L.Map.extend({
982
1023
  this.options.umap_id = data.id
983
1024
  this.permissions.setOptions(data.permissions)
984
1025
  this.permissions.commit()
985
- if (
986
- data.permissions &&
987
- data.permissions.anonymous_edit_url &&
988
- this.options.urls.map_send_edit_link
989
- ) {
1026
+ if (data.permissions && data.permissions.anonymous_edit_url) {
990
1027
  alert.duration = Infinity
991
1028
  alert.content =
992
1029
  L._(
@@ -994,12 +1031,6 @@ U.Map = L.Map.extend({
994
1031
  ) + `<br>${data.permissions.anonymous_edit_url}`
995
1032
 
996
1033
  alert.actions = [
997
- {
998
- label: L._('Send me the link'),
999
- input: L._('Email'),
1000
- callback: this.sendEditLink,
1001
- callbackContext: this,
1002
- },
1003
1034
  {
1004
1035
  label: L._('Copy link'),
1005
1036
  callback: () => {
@@ -1012,6 +1043,14 @@ U.Map = L.Map.extend({
1012
1043
  callbackContext: this,
1013
1044
  },
1014
1045
  ]
1046
+ if (this.options.urls.map_send_edit_link) {
1047
+ alert.actions.push({
1048
+ label: L._('Send me the link'),
1049
+ input: L._('Email'),
1050
+ callback: this.sendEditLink,
1051
+ callbackContext: this,
1052
+ })
1053
+ }
1015
1054
  }
1016
1055
  } else if (!this.permissions.isDirty) {
1017
1056
  // Do not override local changes to permissions,
@@ -1025,7 +1064,7 @@ U.Map = L.Map.extend({
1025
1064
  else window.location = data.url
1026
1065
  alert.content = data.info || alert.content
1027
1066
  this.once('saved', () => this.ui.alert(alert))
1028
- this.ui.closePanel()
1067
+ this.editPanel.close()
1029
1068
  this.permissions.save()
1030
1069
  }
1031
1070
  },
@@ -1138,13 +1177,7 @@ U.Map = L.Map.extend({
1138
1177
  'options.captionBar',
1139
1178
  'options.captionMenus',
1140
1179
  ])
1141
- builder = new U.FormBuilder(this, UIFields, {
1142
- callback: function () {
1143
- this.renderControls()
1144
- this.initCaptionBar()
1145
- },
1146
- callbackContext: this,
1147
- })
1180
+ builder = new U.FormBuilder(this, UIFields)
1148
1181
  const controlsOptions = L.DomUtil.createFieldset(
1149
1182
  container,
1150
1183
  L._('User interface options')
@@ -1167,14 +1200,7 @@ U.Map = L.Map.extend({
1167
1200
  'options.dashArray',
1168
1201
  ]
1169
1202
 
1170
- builder = new U.FormBuilder(this, shapeOptions, {
1171
- callback: function (e) {
1172
- if (this._controls.miniMap) this.renderControls()
1173
- this.eachVisibleDataLayer((datalayer) => {
1174
- datalayer.redraw()
1175
- })
1176
- },
1177
- })
1203
+ builder = new U.FormBuilder(this, shapeOptions)
1178
1204
  const defaultShapeProperties = L.DomUtil.createFieldset(
1179
1205
  container,
1180
1206
  L._('Default shape properties')
@@ -1210,9 +1236,9 @@ U.Map = L.Map.extend({
1210
1236
  [
1211
1237
  'options.facetKey',
1212
1238
  {
1213
- handler: 'Input',
1239
+ handler: 'BlurInput',
1214
1240
  helpEntries: 'facetKey',
1215
- placeholder: L._('Example: key1,key2,key3'),
1241
+ placeholder: L._('Example: key1,key2|Label 2,key3|Label 3|checkbox'),
1216
1242
  label: L._('Facet keys'),
1217
1243
  },
1218
1244
  ],
@@ -1227,14 +1253,7 @@ U.Map = L.Map.extend({
1227
1253
  ],
1228
1254
  ]
1229
1255
 
1230
- builder = new U.FormBuilder(this, optionsFields, {
1231
- callback: function (e) {
1232
- this.initCaptionBar()
1233
- if (e.helper.field === 'options.sortKey') {
1234
- this.eachDataLayer((datalayer) => datalayer.reindex())
1235
- }
1236
- },
1237
- })
1256
+ builder = new U.FormBuilder(this, optionsFields)
1238
1257
  const defaultProperties = L.DomUtil.createFieldset(
1239
1258
  container,
1240
1259
  L._('Default properties')
@@ -1252,20 +1271,7 @@ U.Map = L.Map.extend({
1252
1271
  'options.labelInteractive',
1253
1272
  'options.outlinkTarget',
1254
1273
  ]
1255
- builder = new U.FormBuilder(this, popupFields, {
1256
- callback: function (e) {
1257
- if (
1258
- e.helper.field === 'options.popupTemplate' ||
1259
- e.helper.field === 'options.popupContentTemplate' ||
1260
- e.helper.field === 'options.popupShape' ||
1261
- e.helper.field === 'options.outlinkTarget'
1262
- )
1263
- return
1264
- this.eachVisibleDataLayer((datalayer) => {
1265
- datalayer.redraw()
1266
- })
1267
- },
1268
- })
1274
+ builder = new U.FormBuilder(this, popupFields)
1269
1275
  const popupFieldset = L.DomUtil.createFieldset(
1270
1276
  container,
1271
1277
  L._('Default interaction options')
@@ -1274,7 +1280,7 @@ U.Map = L.Map.extend({
1274
1280
  },
1275
1281
 
1276
1282
  _editTilelayer: function (container) {
1277
- if (!L.Util.isObject(this.options.tilelayer)) {
1283
+ if (!U.Utils.isObject(this.options.tilelayer)) {
1278
1284
  this.options.tilelayer = {}
1279
1285
  }
1280
1286
  const tilelayerFields = [
@@ -1319,15 +1325,12 @@ U.Map = L.Map.extend({
1319
1325
  container,
1320
1326
  L._('Custom background')
1321
1327
  )
1322
- builder = new U.FormBuilder(this, tilelayerFields, {
1323
- callback: this.initTileLayers,
1324
- callbackContext: this,
1325
- })
1328
+ builder = new U.FormBuilder(this, tilelayerFields)
1326
1329
  customTilelayer.appendChild(builder.build())
1327
1330
  },
1328
1331
 
1329
1332
  _editOverlay: function (container) {
1330
- if (!L.Util.isObject(this.options.overlay)) {
1333
+ if (!U.Utils.isObject(this.options.overlay)) {
1331
1334
  this.options.overlay = {}
1332
1335
  }
1333
1336
  const overlayFields = [
@@ -1370,15 +1373,12 @@ U.Map = L.Map.extend({
1370
1373
  ['options.overlay.tms', { handler: 'Switch', label: L._('TMS format') }],
1371
1374
  ]
1372
1375
  const overlay = L.DomUtil.createFieldset(container, L._('Custom overlay'))
1373
- builder = new U.FormBuilder(this, overlayFields, {
1374
- callback: this.initTileLayers,
1375
- callbackContext: this,
1376
- })
1376
+ builder = new U.FormBuilder(this, overlayFields)
1377
1377
  overlay.appendChild(builder.build())
1378
1378
  },
1379
1379
 
1380
1380
  _editBounds: function (container) {
1381
- if (!L.Util.isObject(this.options.limitBounds)) {
1381
+ if (!U.Utils.isObject(this.options.limitBounds)) {
1382
1382
  this.options.limitBounds = {}
1383
1383
  }
1384
1384
  const limitBounds = L.DomUtil.createFieldset(container, L._('Limit bounds'))
@@ -1400,10 +1400,7 @@ U.Map = L.Map.extend({
1400
1400
  { handler: 'BlurFloatInput', placeholder: L._('max East') },
1401
1401
  ],
1402
1402
  ]
1403
- const boundsBuilder = new U.FormBuilder(this, boundsFields, {
1404
- callback: this.handleLimitBounds,
1405
- callbackContext: this,
1406
- })
1403
+ const boundsBuilder = new U.FormBuilder(this, boundsFields)
1407
1404
  limitBounds.appendChild(boundsBuilder.build())
1408
1405
  const boundsButtons = L.DomUtil.create('div', 'button-bar half', limitBounds)
1409
1406
  L.DomUtil.createButton(
@@ -1464,7 +1461,6 @@ U.Map = L.Map.extend({
1464
1461
  ]
1465
1462
  const slideshowHandler = function () {
1466
1463
  this.slideshow.setOptions(this.options.slideshow)
1467
- this.renderControls()
1468
1464
  }
1469
1465
  const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
1470
1466
  callback: slideshowHandler,
@@ -1473,22 +1469,6 @@ U.Map = L.Map.extend({
1473
1469
  slideshow.appendChild(slideshowBuilder.build())
1474
1470
  },
1475
1471
 
1476
- _editCredits: function (container) {
1477
- const credits = L.DomUtil.createFieldset(container, L._('Credits'))
1478
- const creditsFields = [
1479
- 'options.licence',
1480
- 'options.shortCredit',
1481
- 'options.longCredit',
1482
- 'options.permanentCredit',
1483
- 'options.permanentCreditBackground',
1484
- ]
1485
- const creditsBuilder = new U.FormBuilder(this, creditsFields, {
1486
- callback: this.renderControls,
1487
- callbackContext: this,
1488
- })
1489
- credits.appendChild(creditsBuilder.build())
1490
- },
1491
-
1492
1472
  _advancedActions: function (container) {
1493
1473
  const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
1494
1474
  const advancedButtons = L.DomUtil.create('div', 'button-bar half', advancedActions)
@@ -1531,16 +1511,37 @@ U.Map = L.Map.extend({
1531
1511
  )
1532
1512
  },
1533
1513
 
1534
- edit: function () {
1514
+ editCaption: function () {
1535
1515
  if (!this.editEnabled) return
1536
1516
  if (this.options.editMode !== 'advanced') return
1537
1517
  const container = L.DomUtil.create('div', 'umap-edit-container'),
1538
1518
  metadataFields = ['options.name', 'options.description'],
1539
1519
  title = L.DomUtil.create('h3', '', container)
1540
- title.textContent = L._('Edit map properties')
1541
- const builder = new U.FormBuilder(this, metadataFields)
1520
+ title.textContent = L._('Edit map details')
1521
+ const builder = new U.FormBuilder(this, metadataFields, {
1522
+ className: 'map-metadata',
1523
+ })
1542
1524
  const form = builder.build()
1543
1525
  container.appendChild(form)
1526
+
1527
+ const credits = L.DomUtil.createFieldset(container, L._('Credits'))
1528
+ const creditsFields = [
1529
+ 'options.licence',
1530
+ 'options.shortCredit',
1531
+ 'options.longCredit',
1532
+ 'options.permanentCredit',
1533
+ 'options.permanentCreditBackground',
1534
+ ]
1535
+ const creditsBuilder = new U.FormBuilder(this, creditsFields)
1536
+ credits.appendChild(creditsBuilder.build())
1537
+ this.editPanel.open({ content: container })
1538
+ },
1539
+
1540
+ edit: function () {
1541
+ if (!this.editEnabled) return
1542
+ if (this.options.editMode !== 'advanced') return
1543
+ const container = L.DomUtil.create('div')
1544
+ L.DomUtil.createTitle(container, L._('Map advanced properties'), 'icon-settings')
1544
1545
  this._editControls(container)
1545
1546
  this._editShapeProperties(container)
1546
1547
  this._editDefaultProperties(container)
@@ -1549,10 +1550,9 @@ U.Map = L.Map.extend({
1549
1550
  this._editOverlay(container)
1550
1551
  this._editBounds(container)
1551
1552
  this._editSlideshow(container)
1552
- this._editCredits(container)
1553
1553
  this._advancedActions(container)
1554
1554
 
1555
- this.ui.openPanel({ data: { html: container }, className: 'dark' })
1555
+ this.editPanel.open({ content: container, className: 'dark' })
1556
1556
  },
1557
1557
 
1558
1558
  enableEdit: function () {
@@ -1569,6 +1569,8 @@ U.Map = L.Map.extend({
1569
1569
  this.editedFeature = null
1570
1570
  this.editEnabled = false
1571
1571
  this.fire('edit:disabled')
1572
+ this.editPanel.close()
1573
+ this.fullPanel.close()
1572
1574
  },
1573
1575
 
1574
1576
  hasEditMode: function () {
@@ -1600,8 +1602,7 @@ U.Map = L.Map.extend({
1600
1602
  'umap-open-browser-link flat',
1601
1603
  container,
1602
1604
  L._('Browse data'),
1603
- this.openBrowser,
1604
- this
1605
+ () => this.openBrowser('expanded')
1605
1606
  )
1606
1607
  if (this.options.facetKey) {
1607
1608
  L.DomUtil.createButton(
@@ -1626,8 +1627,7 @@ U.Map = L.Map.extend({
1626
1627
  askForReset: function (e) {
1627
1628
  if (!confirm(L._('Are you sure you want to cancel your changes?'))) return
1628
1629
  this.reset()
1629
- this.disableEdit(e)
1630
- this.ui.closePanel()
1630
+ this.disableEdit()
1631
1631
  },
1632
1632
 
1633
1633
  startMarker: function () {
@@ -1743,10 +1743,17 @@ U.Map = L.Map.extend({
1743
1743
  })
1744
1744
  }
1745
1745
  }
1746
- items.push('-', {
1747
- text: L._('Browse data'),
1748
- callback: this.openBrowser,
1749
- })
1746
+ items.push(
1747
+ '-',
1748
+ {
1749
+ text: L._('See layers'),
1750
+ callback: () => this.openBrowser('condensed'),
1751
+ },
1752
+ {
1753
+ text: L._('Browse data'),
1754
+ callback: () => this.openBrowser('expanded'),
1755
+ }
1756
+ )
1750
1757
  if (this.options.facetKey) {
1751
1758
  items.push({
1752
1759
  text: L._('Facet search'),
@@ -1769,14 +1776,29 @@ U.Map = L.Map.extend({
1769
1776
  callback: this.openExternalRouting,
1770
1777
  })
1771
1778
  }
1779
+ if (this.options.urls.edit_in_osm) {
1780
+ items.push('-', {
1781
+ text: L._('Edit in OpenStreetMap'),
1782
+ callback: this.editInOSM,
1783
+ })
1784
+ }
1772
1785
  this.options.contextmenuItems = items
1773
1786
  },
1774
1787
 
1788
+ editInOSM: function (e) {
1789
+ const url = this.urls.get('edit_in_osm', {
1790
+ lat: e.latlng.lat,
1791
+ lng: e.latlng.lng,
1792
+ zoom: Math.max(this.getZoom(), 16),
1793
+ })
1794
+ if (url) window.open(url)
1795
+ },
1796
+
1775
1797
  openExternalRouting: function (e) {
1776
1798
  const url = this.urls.get('routing', {
1777
1799
  lat: e.latlng.lat,
1778
1800
  lng: e.latlng.lng,
1779
- locale: L.locale,
1801
+ locale: L.getLocale(),
1780
1802
  zoom: this.getZoom(),
1781
1803
  })
1782
1804
  if (url) window.open(url)
@@ -1805,12 +1827,12 @@ U.Map = L.Map.extend({
1805
1827
  },
1806
1828
 
1807
1829
  localizeUrl: function (url) {
1808
- return L.Util.greedyTemplate(url, this.getGeoContext(), true)
1830
+ return U.Utils.greedyTemplate(url, this.getGeoContext(), true)
1809
1831
  },
1810
1832
 
1811
1833
  proxyUrl: function (url, ttl) {
1812
1834
  if (this.options.urls.ajax_proxy) {
1813
- url = L.Util.greedyTemplate(this.options.urls.ajax_proxy, {
1835
+ url = U.Utils.greedyTemplate(this.options.urls.ajax_proxy, {
1814
1836
  url: encodeURIComponent(url),
1815
1837
  ttl: ttl,
1816
1838
  })
@@ -1824,21 +1846,13 @@ U.Map = L.Map.extend({
1824
1846
  },
1825
1847
 
1826
1848
  search: function () {
1827
- if (this._controls.search) this._controls.search.openPanel(this)
1849
+ if (this._controls.search) this._controls.search.open()
1828
1850
  },
1829
1851
 
1830
1852
  getFilterKeys: function () {
1831
1853
  return (this.options.filterKey || this.options.sortKey || 'name').split(',')
1832
1854
  },
1833
1855
 
1834
- getFacetKeys: function () {
1835
- return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
1836
- const els = curr.split('|')
1837
- acc[els[0]] = els[1] || els[0]
1838
- return acc
1839
- }, {})
1840
- },
1841
-
1842
1856
  getLayersBounds: function () {
1843
1857
  const bounds = new L.latLngBounds()
1844
1858
  this.eachBrowsableDataLayer((d) => {