umap-project 3.0.4__py3-none-any.whl → 3.0.5__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 (158) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/el/LC_MESSAGES/django.po +136 -56
  4. umap/locale/en/LC_MESSAGES/django.po +18 -18
  5. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/es/LC_MESSAGES/django.po +136 -56
  7. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/nl/LC_MESSAGES/django.po +1 -1
  9. umap/models.py +1 -0
  10. umap/settings/base.py +1 -0
  11. umap/static/umap/css/bar.css +1 -1
  12. umap/static/umap/css/tooltip.css +13 -0
  13. umap/static/umap/js/modules/autocomplete.js +7 -8
  14. umap/static/umap/js/modules/browser.js +89 -94
  15. umap/static/umap/js/modules/caption.js +6 -4
  16. umap/static/umap/js/modules/data/features.js +1 -19
  17. umap/static/umap/js/modules/data/layer.js +100 -61
  18. umap/static/umap/js/modules/facets.js +1 -1
  19. umap/static/umap/js/modules/form/fields.js +1 -1
  20. umap/static/umap/js/modules/importer.js +1 -1
  21. umap/static/umap/js/modules/managers.js +46 -0
  22. umap/static/umap/js/modules/permissions.js +1 -1
  23. umap/static/umap/js/modules/rendering/controls.js +251 -0
  24. umap/static/umap/js/modules/rendering/layers/heat.js +5 -0
  25. umap/static/umap/js/modules/rendering/map.js +21 -10
  26. umap/static/umap/js/modules/rendering/ui.js +0 -1
  27. umap/static/umap/js/modules/rules.js +56 -46
  28. umap/static/umap/js/modules/schema.js +5 -1
  29. umap/static/umap/js/modules/share.js +2 -2
  30. umap/static/umap/js/modules/slideshow.js +1 -1
  31. umap/static/umap/js/modules/sync/engine.js +23 -9
  32. umap/static/umap/js/modules/ui/bar.js +2 -2
  33. umap/static/umap/js/modules/ui/base.js +13 -0
  34. umap/static/umap/js/modules/umap.js +69 -111
  35. umap/static/umap/js/umap.controls.js +0 -310
  36. umap/static/umap/js/umap.core.js +0 -40
  37. umap/static/umap/locale/am_ET.js +8 -3
  38. umap/static/umap/locale/am_ET.json +8 -3
  39. umap/static/umap/locale/ar.js +8 -3
  40. umap/static/umap/locale/ar.json +8 -3
  41. umap/static/umap/locale/ast.js +8 -3
  42. umap/static/umap/locale/ast.json +8 -3
  43. umap/static/umap/locale/bg.js +8 -3
  44. umap/static/umap/locale/bg.json +8 -3
  45. umap/static/umap/locale/br.js +8 -3
  46. umap/static/umap/locale/br.json +8 -3
  47. umap/static/umap/locale/ca.js +20 -17
  48. umap/static/umap/locale/ca.json +20 -17
  49. umap/static/umap/locale/cs_CZ.js +5 -2
  50. umap/static/umap/locale/cs_CZ.json +5 -2
  51. umap/static/umap/locale/da.js +8 -3
  52. umap/static/umap/locale/da.json +8 -3
  53. umap/static/umap/locale/de.js +5 -2
  54. umap/static/umap/locale/de.json +5 -2
  55. umap/static/umap/locale/el.js +92 -87
  56. umap/static/umap/locale/el.json +92 -87
  57. umap/static/umap/locale/en.js +5 -2
  58. umap/static/umap/locale/en.json +5 -2
  59. umap/static/umap/locale/en_US.json +8 -3
  60. umap/static/umap/locale/es.js +18 -15
  61. umap/static/umap/locale/es.json +18 -15
  62. umap/static/umap/locale/et.js +8 -3
  63. umap/static/umap/locale/et.json +8 -3
  64. umap/static/umap/locale/eu.js +5 -2
  65. umap/static/umap/locale/eu.json +5 -2
  66. umap/static/umap/locale/fa_IR.js +5 -2
  67. umap/static/umap/locale/fa_IR.json +5 -2
  68. umap/static/umap/locale/fi.js +8 -3
  69. umap/static/umap/locale/fi.json +8 -3
  70. umap/static/umap/locale/fr.js +5 -2
  71. umap/static/umap/locale/fr.json +5 -2
  72. umap/static/umap/locale/gl.js +5 -2
  73. umap/static/umap/locale/gl.json +5 -2
  74. umap/static/umap/locale/he.js +8 -3
  75. umap/static/umap/locale/he.json +8 -3
  76. umap/static/umap/locale/hr.js +8 -3
  77. umap/static/umap/locale/hr.json +8 -3
  78. umap/static/umap/locale/hu.js +5 -2
  79. umap/static/umap/locale/hu.json +5 -2
  80. umap/static/umap/locale/id.js +8 -3
  81. umap/static/umap/locale/id.json +8 -3
  82. umap/static/umap/locale/is.js +8 -3
  83. umap/static/umap/locale/is.json +8 -3
  84. umap/static/umap/locale/it.js +5 -2
  85. umap/static/umap/locale/it.json +5 -2
  86. umap/static/umap/locale/ja.js +8 -3
  87. umap/static/umap/locale/ja.json +8 -3
  88. umap/static/umap/locale/ko.js +8 -3
  89. umap/static/umap/locale/ko.json +8 -3
  90. umap/static/umap/locale/lt.js +8 -3
  91. umap/static/umap/locale/lt.json +8 -3
  92. umap/static/umap/locale/ms.js +8 -3
  93. umap/static/umap/locale/ms.json +8 -3
  94. umap/static/umap/locale/nl.js +7 -4
  95. umap/static/umap/locale/nl.json +7 -4
  96. umap/static/umap/locale/no.js +8 -3
  97. umap/static/umap/locale/no.json +8 -3
  98. umap/static/umap/locale/pl.js +8 -3
  99. umap/static/umap/locale/pl.json +8 -3
  100. umap/static/umap/locale/pl_PL.json +8 -3
  101. umap/static/umap/locale/pt.js +5 -2
  102. umap/static/umap/locale/pt.json +5 -2
  103. umap/static/umap/locale/pt_BR.js +8 -3
  104. umap/static/umap/locale/pt_BR.json +8 -3
  105. umap/static/umap/locale/pt_PT.js +5 -2
  106. umap/static/umap/locale/pt_PT.json +5 -2
  107. umap/static/umap/locale/ro.js +8 -3
  108. umap/static/umap/locale/ro.json +8 -3
  109. umap/static/umap/locale/ru.js +8 -3
  110. umap/static/umap/locale/ru.json +8 -3
  111. umap/static/umap/locale/sk_SK.js +8 -3
  112. umap/static/umap/locale/sk_SK.json +8 -3
  113. umap/static/umap/locale/sl.js +8 -3
  114. umap/static/umap/locale/sl.json +8 -3
  115. umap/static/umap/locale/sr.js +8 -3
  116. umap/static/umap/locale/sr.json +8 -3
  117. umap/static/umap/locale/sv.js +8 -3
  118. umap/static/umap/locale/sv.json +8 -3
  119. umap/static/umap/locale/th_TH.js +8 -3
  120. umap/static/umap/locale/th_TH.json +8 -3
  121. umap/static/umap/locale/tr.js +8 -3
  122. umap/static/umap/locale/tr.json +8 -3
  123. umap/static/umap/locale/uk_UA.js +8 -3
  124. umap/static/umap/locale/uk_UA.json +8 -3
  125. umap/static/umap/locale/vi.js +8 -3
  126. umap/static/umap/locale/vi.json +8 -3
  127. umap/static/umap/locale/vi_VN.json +8 -3
  128. umap/static/umap/locale/zh.js +8 -3
  129. umap/static/umap/locale/zh.json +8 -3
  130. umap/static/umap/locale/zh_CN.json +8 -3
  131. umap/static/umap/locale/zh_TW.Big5.json +8 -3
  132. umap/static/umap/locale/zh_TW.js +5 -2
  133. umap/static/umap/locale/zh_TW.json +5 -2
  134. umap/static/umap/map.css +9 -31
  135. umap/static/umap/vendors/togeojson/togeojson.es.js +350 -177
  136. umap/static/umap/vendors/togeojson/togeojson.es.mjs.map +1 -1
  137. umap/templates/umap/design_system.html +355 -0
  138. umap/tests/base.py +2 -2
  139. umap/tests/fixtures/heatmap_data.json +1044 -0
  140. umap/tests/integration/test_browser.py +3 -3
  141. umap/tests/integration/test_conditional_rules.py +2 -2
  142. umap/tests/integration/test_datalayer.py +0 -1
  143. umap/tests/integration/test_edit_map.py +7 -7
  144. umap/tests/integration/test_facets_browser.py +2 -2
  145. umap/tests/integration/test_heatmap.py +41 -0
  146. umap/tests/integration/test_import.py +58 -1
  147. umap/tests/integration/test_map.py +7 -8
  148. umap/tests/integration/test_optimistic_merge.py +12 -4
  149. umap/tests/integration/test_querystring.py +1 -1
  150. umap/tests/integration/test_remote_data.py +79 -0
  151. umap/tests/integration/test_websocket_sync.py +2 -2
  152. umap/urls.py +1 -0
  153. umap/views.py +7 -0
  154. {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/METADATA +8 -8
  155. {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/RECORD +158 -152
  156. {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/WHEEL +0 -0
  157. {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/entry_points.txt +0 -0
  158. {umap_project-3.0.4.dist-info → umap_project-3.0.5.dist-info}/licenses/LICENSE +0 -0
@@ -64,6 +64,9 @@ export class DataLayer {
64
64
 
65
65
  this.setOptions(data)
66
66
  this.pane.dataset.id = this.id
67
+ if (this.options.rank === undefined) {
68
+ this.options.rank = this._umap.datalayers.count()
69
+ }
67
70
 
68
71
  if (!Utils.isObject(this.options.remoteData)) {
69
72
  this.options.remoteData = {}
@@ -122,6 +125,20 @@ export class DataLayer {
122
125
  return `datalayer-${stamp(this)}`
123
126
  }
124
127
 
128
+ get rank() {
129
+ // Make sure we always have a valid rank. Undefined rank may happen
130
+ // after importing an old umap backup, and not touching the layers
131
+ // after that.
132
+ if (this.options.rank === undefined) {
133
+ this.options.rank = this.getDOMOrder()
134
+ }
135
+ return this.options.rank
136
+ }
137
+
138
+ set rank(value) {
139
+ this.options.rank = value
140
+ }
141
+
125
142
  getSyncMetadata() {
126
143
  return {
127
144
  subject: 'datalayer',
@@ -130,6 +147,10 @@ export class DataLayer {
130
147
  }
131
148
 
132
149
  render(fields, builder) {
150
+ // Propagate will remove the fields it has already
151
+ // processed
152
+ fields = this.propagate(fields)
153
+
133
154
  const impacts = Utils.getImpactsFromSchema(fields)
134
155
 
135
156
  for (const impact of impacts) {
@@ -149,8 +170,34 @@ export class DataLayer {
149
170
  case 'remote-data':
150
171
  this.fetchRemoteData()
151
172
  break
173
+ case 'datalayer-rank':
174
+ this._umap.reorderDataLayers()
175
+ break
176
+ }
177
+ }
178
+ }
179
+
180
+ // This method does a targeted update of the UI,
181
+ // it whould be merged with `render`` method and the
182
+ // SCHEMA at some point
183
+ propagate(fields = []) {
184
+ const impacts = {
185
+ 'properties.name': () => {
186
+ Utils.eachElement('.datalayer-name', (el) => {
187
+ if (el.dataset.id === this.id) {
188
+ el.textContent = this.getName()
189
+ el.title = this.getName()
190
+ }
191
+ })
192
+ },
193
+ }
194
+ for (const [field, impact] of Object.entries(impacts)) {
195
+ if (!fields.length || fields.includes(field)) {
196
+ impact()
197
+ fields = fields.filter((item) => item !== field)
152
198
  }
153
199
  }
200
+ return fields
154
201
  }
155
202
 
156
203
  showAtLoad() {
@@ -222,16 +269,9 @@ export class DataLayer {
222
269
  if (!error) {
223
270
  this._umap.modifiedAt = response.headers.get('last-modified')
224
271
  this.setReferenceVersion({ response, sync: false })
225
- // FIXME: for now the _umap_options property is set dynamically from backend
226
- // And thus it's not in the geojson file in the server
227
- // So do not let all options to be reset
228
- // Fix is a proper migration so all datalayers settings are
229
- // in DB, and we remove it from geojson flat files.
230
- if (geojson._umap_options) {
231
- geojson._umap_options.editMode = this.options.editMode
232
- }
272
+ delete geojson._umap_options
233
273
  // In case of maps pre 1.0 still around
234
- if (geojson._storage) geojson._storage.editMode = this.options.editMode
274
+ delete geojson._storage
235
275
  await this.fromUmapGeoJSON(geojson)
236
276
  this.backupOptions()
237
277
  this._loading = false
@@ -260,7 +300,6 @@ export class DataLayer {
260
300
 
261
301
  async fromUmapGeoJSON(geojson) {
262
302
  if (geojson._storage) geojson._umap_options = geojson._storage // Retrocompat
263
- geojson._umap_options.id = this.id
264
303
  if (geojson._umap_options) this.setOptions(geojson._umap_options)
265
304
  if (this.isRemoteLayer()) {
266
305
  await this.fetchRemoteData()
@@ -269,15 +308,6 @@ export class DataLayer {
269
308
  }
270
309
  }
271
310
 
272
- clear() {
273
- this.sync.startBatch()
274
- for (const feature of Object.values(this._features)) {
275
- feature.del()
276
- }
277
- this.sync.commitBatch()
278
- this.dataChanged()
279
- }
280
-
281
311
  backupData() {
282
312
  if (this._geojson) {
283
313
  this._geojson_bk = Utils.CopyJSON(this._geojson)
@@ -328,7 +358,7 @@ export class DataLayer {
328
358
  url = this._umap.proxyUrl(url, this.options.remoteData.ttl)
329
359
  }
330
360
  return await this.getUrl(url, remoteUrl).then((raw) => {
331
- this.clear()
361
+ this.clear(false)
332
362
  return this._umap.formatter
333
363
  .parse(raw, this.options.remoteData.format)
334
364
  .then((geojson) => this.fromGeoJSON(geojson, false))
@@ -368,12 +398,7 @@ export class DataLayer {
368
398
  }
369
399
 
370
400
  connectToMap() {
371
- if (!this._umap.datalayers[this.id]) {
372
- this._umap.datalayers[this.id] = this
373
- }
374
- if (!this._umap.datalayersIndex.includes(this)) {
375
- this._umap.datalayersIndex.push(this)
376
- }
401
+ this._umap.datalayers.add(this)
377
402
  this._umap.onDataLayersChanged()
378
403
  }
379
404
 
@@ -492,8 +517,19 @@ export class DataLayer {
492
517
  const features = []
493
518
  this.sortFeatures(collection)
494
519
  for (const featureJson of collection) {
495
- const feature = this.makeFeature(featureJson, sync)
496
- if (feature) features.push(feature)
520
+ if (featureJson.geometry?.type === 'GeometryCollection') {
521
+ for (const geometry of featureJson.geometry.geometries) {
522
+ const feature = this.makeFeature({
523
+ type: 'Feature',
524
+ geometry,
525
+ properties: featureJson.properties,
526
+ })
527
+ if (feature) features.push(feature)
528
+ }
529
+ } else {
530
+ const feature = this.makeFeature(featureJson, sync)
531
+ if (feature) features.push(feature)
532
+ }
497
533
  }
498
534
  return features
499
535
  }
@@ -606,16 +642,33 @@ export class DataLayer {
606
642
 
607
643
  del(sync = true) {
608
644
  const oldValue = Utils.CopyJSON(this.umapGeoJSON())
609
- this.erase()
645
+ // TODO merge datalayer del and features del in same
646
+ // batch
647
+ this.clear()
610
648
  if (sync) {
611
649
  this.isDeleted = true
612
650
  this.sync.delete(oldValue)
613
651
  }
652
+ this.hide()
653
+ this.parentPane.removeChild(this.pane)
654
+ this._umap.onDataLayersChanged()
655
+ this.layer.onDelete(this._leafletMap)
656
+ this.propagateDelete()
657
+ this._leaflet_events_bk = this._leaflet_events
614
658
  }
615
659
 
616
660
  empty() {
617
661
  if (this.isRemoteLayer()) return
662
+ this.sync.startBatch()
618
663
  this.clear()
664
+ this.sync.commitBatch()
665
+ }
666
+
667
+ clear(sync = true) {
668
+ for (const feature of Object.values(this._features)) {
669
+ feature.del(sync)
670
+ }
671
+ this.dataChanged()
619
672
  }
620
673
 
621
674
  clone() {
@@ -628,24 +681,13 @@ export class DataLayer {
628
681
  return datalayer
629
682
  }
630
683
 
631
- erase() {
632
- this.hide()
633
- this._umap.datalayersIndex.splice(this.getRank(), 1)
634
- this.parentPane.removeChild(this.pane)
635
- this._umap.onDataLayersChanged()
636
- this.layer.onDelete(this._leafletMap)
637
- this.propagateDelete()
638
- this._leaflet_events_bk = this._leaflet_events
639
- this.clear()
640
- }
641
-
642
684
  redraw() {
643
685
  if (!this.isVisible()) return
644
686
  this.eachFeature((feature) => feature.redraw())
645
687
  }
646
688
 
647
689
  edit() {
648
- if (!this._umap.editEnabled || !this.isLoaded()) {
690
+ if (!this._umap.editEnabled) {
649
691
  return
650
692
  }
651
693
  const container = DomUtil.create('div', 'umap-layer-properties-container')
@@ -1053,23 +1095,11 @@ export class DataLayer {
1053
1095
  }
1054
1096
 
1055
1097
  getPreviousBrowsable() {
1056
- let id = this.getRank()
1057
- let next
1058
- const index = this._umap.datalayersIndex
1059
- while (((id = index[++id] ? id : 0), (next = index[id]))) {
1060
- if (next === this || next.canBrowse()) break
1061
- }
1062
- return next
1098
+ return this._umap.datalayers.prev(this)
1063
1099
  }
1064
1100
 
1065
1101
  getNextBrowsable() {
1066
- let id = this.getRank()
1067
- let prev
1068
- const index = this._umap.datalayersIndex
1069
- while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
1070
- if (prev === this || prev.canBrowse()) break
1071
- }
1072
- return prev
1102
+ return this._umap.datalayers.next(this)
1073
1103
  }
1074
1104
 
1075
1105
  umapGeoJSON() {
@@ -1080,8 +1110,8 @@ export class DataLayer {
1080
1110
  }
1081
1111
  }
1082
1112
 
1083
- getRank() {
1084
- return this._umap.datalayersIndex.indexOf(this)
1113
+ getDOMOrder() {
1114
+ return Array.from(this.parentPane.children).indexOf(this.pane)
1085
1115
  }
1086
1116
 
1087
1117
  isReadOnly() {
@@ -1103,15 +1133,21 @@ export class DataLayer {
1103
1133
  }
1104
1134
  }
1105
1135
 
1136
+ prepareOptions() {
1137
+ const options = Utils.CopyJSON(this.options)
1138
+ delete options.permissions
1139
+ return JSON.stringify(options)
1140
+ }
1141
+
1106
1142
  async save() {
1107
1143
  if (this.isDeleted) return await this.saveDelete()
1108
- if (!this.isLoaded()) return
1144
+ if (!this.isRemoteLayer() && !this.isLoaded()) return
1109
1145
  const geojson = this.umapGeoJSON()
1110
1146
  const formData = new FormData()
1111
1147
  formData.append('name', this.options.name)
1112
1148
  formData.append('display_on_load', !!this.options.displayOnLoad)
1113
- formData.append('rank', this.getRank())
1114
- formData.append('settings', JSON.stringify(this.options))
1149
+ formData.append('rank', this.rank)
1150
+ formData.append('settings', this.prepareOptions())
1115
1151
  // Filename support is shaky, don't do it for now.
1116
1152
  const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
1117
1153
  formData.append('geojson', blob)
@@ -1146,12 +1182,15 @@ export class DataLayer {
1146
1182
  await this._umap.saveAll()
1147
1183
  }
1148
1184
  )
1185
+ } else {
1186
+ console.debug(error)
1187
+ Alert.error(translate('Cannot save layer, please try again in a few minutes.'))
1149
1188
  }
1150
1189
  } else {
1151
1190
  // Response contains geojson only if save has conflicted and conflicts have
1152
1191
  // been resolved. So we need to reload to get extra data (added by someone else)
1153
1192
  if (data.geojson) {
1154
- this.clear()
1193
+ this.clear(false)
1155
1194
  this.fromGeoJSON(data.geojson)
1156
1195
  delete data.geojson
1157
1196
  }
@@ -24,7 +24,7 @@ export default class Facets {
24
24
  this.selected[name] = selected
25
25
  }
26
26
 
27
- this._umap.eachBrowsableDataLayer((datalayer) => {
27
+ this._umap.datalayers.browsable().map((datalayer) => {
28
28
  datalayer.eachFeature((feature) => {
29
29
  for (const name of names) {
30
30
  let value = feature.properties[name]
@@ -560,7 +560,7 @@ Fields.SlideshowDelay = class extends Fields.IntSelect {
560
560
  Fields.DataLayerSwitcher = class extends Fields.Select {
561
561
  getOptions() {
562
562
  const options = []
563
- this.builder._umap.eachDataLayerReverse((datalayer) => {
563
+ this.builder._umap.datalayers.reverse().map((datalayer) => {
564
564
  if (
565
565
  datalayer.isLoaded() &&
566
566
  !datalayer.isDataReadOnly() &&
@@ -243,7 +243,7 @@ export default class Importer extends Utils.WithTemplate {
243
243
  this.raw = null
244
244
  const layerSelect = this.qs('[name="layer-id"]')
245
245
  layerSelect.innerHTML = ''
246
- this._umap.eachDataLayerReverse((datalayer) => {
246
+ this._umap.datalayers.reverse().map((datalayer) => {
247
247
  if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
248
248
  DomUtil.element({
249
249
  tagName: 'option',
@@ -0,0 +1,46 @@
1
+ export class DataLayerManager extends Object {
2
+ add(datalayer) {
3
+ this[datalayer.id] = datalayer
4
+ }
5
+ active() {
6
+ return Object.values(this)
7
+ .filter((datalayer) => !datalayer.isDeleted)
8
+ .sort((a, b) => a.rank > b.rank)
9
+ }
10
+ reverse() {
11
+ return this.active().reverse()
12
+ }
13
+ count() {
14
+ return this.active().length
15
+ }
16
+ find(func) {
17
+ for (const datalayer of this.reverse()) {
18
+ if (func.call(datalayer, datalayer)) {
19
+ return datalayer
20
+ }
21
+ }
22
+ }
23
+ filter(func) {
24
+ return this.active().filter(func)
25
+ }
26
+ visible() {
27
+ return this.filter((datalayer) => datalayer.isVisible())
28
+ }
29
+ browsable() {
30
+ return this.reverse().filter((datalayer) => datalayer.allowBrowse())
31
+ }
32
+ prev(datalayer) {
33
+ const browsable = this.browsable()
34
+ const current = browsable.indexOf(datalayer)
35
+ const prev = browsable[current - 1] || browsable[browsable.length - 1]
36
+ if (!prev.canBrowse()) return this.prev(prev)
37
+ return prev
38
+ }
39
+ next(datalayer) {
40
+ const browsable = this.browsable()
41
+ const current = browsable.indexOf(datalayer)
42
+ const next = browsable[current + 1] || browsable[0]
43
+ if (!next.canBrowse()) return this.next(next)
44
+ return next
45
+ }
46
+ }
@@ -159,7 +159,7 @@ export class MapPermissions {
159
159
  `<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
160
160
  )
161
161
  container.appendChild(fieldset)
162
- this._umap.eachDataLayer((datalayer) => {
162
+ this._umap.datalayers.active().map((datalayer) => {
163
163
  datalayer.permissions.edit(fieldset)
164
164
  })
165
165
  }
@@ -0,0 +1,251 @@
1
+ import { Control } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
+ import * as Utils from '../utils.js'
3
+ import { translate } from '../i18n.js'
4
+
5
+ export const HomeControl = Control.extend({
6
+ options: {
7
+ position: 'topleft',
8
+ },
9
+
10
+ onAdd: (map) => {
11
+ const path = map._umap.getStaticPathFor('home.svg')
12
+ const container = Utils.loadTemplate(
13
+ `<a href="/" class="home-button" title="${translate('Back to home')}"><img src="${path}" alt="${translate('Home logo')}" width="38px" height="38px" /></a>`
14
+ )
15
+ return container
16
+ },
17
+ })
18
+
19
+ export const EditControl = Control.extend({
20
+ options: {
21
+ position: 'topright',
22
+ },
23
+
24
+ onAdd: (map) => {
25
+ const template = `
26
+ <div class="edit-enable">
27
+ <button type="button" data-ref="button">${translate('Edit')}</button>
28
+ </div>
29
+ `
30
+ const [container, { button }] = Utils.loadTemplateWithRefs(template)
31
+ button.addEventListener('click', () => map._umap.enableEdit())
32
+ button.addEventListener('mouseover', () => {
33
+ map._umap.tooltip.open({
34
+ content: map._umap.help.displayLabel('TOGGLE_EDIT'),
35
+ anchor: button,
36
+ position: 'bottom',
37
+ delay: 750,
38
+ duration: 5000,
39
+ })
40
+ })
41
+ return container
42
+ },
43
+ })
44
+
45
+ export const MoreControl = Control.extend({
46
+ options: {
47
+ position: 'topleft',
48
+ },
49
+
50
+ onAdd: function (map) {
51
+ const pos = this.getPosition()
52
+ const corner = map._controlCorners[pos]
53
+ const className = 'umap-more-controls'
54
+ const template = `
55
+ <div class="umap-control-text">
56
+ <button class="umap-control-more" type="button" data-ref="button"></button>
57
+ </div>
58
+ `
59
+ const [container, { button }] = Utils.loadTemplateWithRefs(template)
60
+ button.addEventListener('click', () => corner.classList.toggle(className))
61
+ button.addEventListener('mouseover', () => {
62
+ const extended = corner.classList.contains(className)
63
+ map._umap.tooltip.open({
64
+ content: extended ? translate('Hide controls') : translate('More controls'),
65
+ anchor: button,
66
+ position: 'right',
67
+ delay: 750,
68
+ })
69
+ })
70
+ return container
71
+ },
72
+ })
73
+
74
+ export const PermanentCreditsControl = Control.extend({
75
+ options: {
76
+ position: 'bottomleft',
77
+ },
78
+
79
+ onAdd: (map) => {
80
+ const container = Utils.loadTemplate(
81
+ `<div class="umap-permanent-credits-container text">${Utils.toHTML(map.options.permanentCredit)}</div>`
82
+ )
83
+ const background = map.options.permanentCreditBackground ? '#FFFFFFB0' : ''
84
+ container.style.backgroundColor = background
85
+ return container
86
+ },
87
+ })
88
+
89
+ const BaseButton = Control.extend({
90
+ initialize: function (umap, options) {
91
+ this._umap = umap
92
+ Control.prototype.initialize.call(this, options)
93
+ },
94
+
95
+ onAdd: function (map) {
96
+ const template = `
97
+ <div class="${this.options.className} umap-control">
98
+ <button type="button" title="${this.options.title}" data-ref="button"></button>
99
+ </div>
100
+ `
101
+ const [container, { button }] = Utils.loadTemplateWithRefs(template)
102
+ button.addEventListener('click', (event) => {
103
+ event.stopPropagation()
104
+ this.onClick()
105
+ })
106
+ button.addEventListener('dblclick', (event) => {
107
+ event.stopPropagation()
108
+ })
109
+ this.afterAdd(container)
110
+ return container
111
+ },
112
+
113
+ afterAdd: (container) => {},
114
+ })
115
+
116
+ export const DataLayersControl = BaseButton.extend({
117
+ options: {
118
+ position: 'topleft',
119
+ className: 'umap-control-browse',
120
+ title: translate('Open browser'),
121
+ },
122
+
123
+ afterAdd: function (container) {
124
+ Utils.toggleBadge(container, this._umap.browser?.hasFilters())
125
+ },
126
+
127
+ onClick: function () {
128
+ this._umap.openBrowser()
129
+ },
130
+ })
131
+
132
+ export const CaptionControl = BaseButton.extend({
133
+ options: {
134
+ position: 'topleft',
135
+ className: 'umap-control-caption',
136
+ title: translate('About'),
137
+ },
138
+
139
+ onClick: function () {
140
+ this._umap.openCaption()
141
+ },
142
+ })
143
+
144
+ export const EmbedControl = BaseButton.extend({
145
+ options: {
146
+ position: 'topleft',
147
+ title: translate('Share and download'),
148
+ className: 'leaflet-control-embed',
149
+ },
150
+
151
+ onClick: function () {
152
+ this._umap.share.open()
153
+ },
154
+ })
155
+
156
+ export const AttributionControl = Control.Attribution.extend({
157
+ options: {
158
+ prefix: '',
159
+ },
160
+
161
+ _update: function () {
162
+ // Layer is no more on the map
163
+ if (!this._map) return
164
+ Control.Attribution.prototype._update.call(this)
165
+ const shortCredit = this._map._umap.getProperty('shortCredit')
166
+ const captionMenus = this._map._umap.getProperty('captionMenus')
167
+ // Use our own container, so we can hide/show on small screens
168
+ const originalCredits = this._container.innerHTML
169
+ this._container.innerHTML = ''
170
+ const template = `
171
+ <div class="attribution-container">
172
+ ${originalCredits}
173
+ <span data-ref="short"> — ${Utils.toHTML(shortCredit)}</span>
174
+ <a href="#" data-ref="caption"> — ${translate('Open caption')}</a>
175
+ <a href="/" data-ref="home"> — ${translate('Home')}</a>
176
+ <a href="https://umap-project.org/" data-ref="site"> — ${translate('Powered by uMap')}</a>
177
+ <a href="#" class="attribution-toggle"></a>
178
+ </div>
179
+ `
180
+ const [container, { short, caption, home, site }] =
181
+ Utils.loadTemplateWithRefs(template)
182
+ caption.addEventListener('click', () => this._map._umap.openCaption())
183
+ this._container.appendChild(container)
184
+ short.hidden = !shortCredit
185
+ caption.hidden = !captionMenus
186
+ site.hidden = !captionMenus
187
+ home.hidden = this._map._umap.isEmbed || !captionMenus
188
+ },
189
+ })
190
+
191
+ /* Used in edit mode to define the default tilelayer */
192
+ export const TileLayerChooser = BaseButton.extend({
193
+ options: {
194
+ position: 'topleft',
195
+ },
196
+
197
+ onClick: function () {
198
+ this.openSwitcher({ edit: true })
199
+ },
200
+
201
+ openSwitcher: function (options = {}) {
202
+ const template = `
203
+ <div class="umap-edit-tilelayers">
204
+ <h3><i class="icon icon-16 icon-tilelayer" title=""></i><span class="">${translate('Change tilelayers')}</span></h3>
205
+ <ul data-ref="tileContainer"></ul>
206
+ </div>
207
+ `
208
+ const [container, { tileContainer }] = Utils.loadTemplateWithRefs(template)
209
+ this.buildList(tileContainer, options)
210
+ const panel = options.edit ? this._umap.editPanel : this._umap.panel
211
+ panel.open({ content: container, highlight: 'tilelayers' })
212
+ },
213
+
214
+ buildList: function (container, options) {
215
+ this._umap._leafletMap.eachTileLayer((tilelayer) => {
216
+ const browserIsHttps = window.location.protocol === 'https:'
217
+ const tileLayerIsHttp = tilelayer.options.url_template.indexOf('http:') === 0
218
+ if (browserIsHttps && tileLayerIsHttp) return
219
+ container.appendChild(this.addTileLayerElement(tilelayer, options))
220
+ })
221
+ },
222
+
223
+ addTileLayerElement: function (tilelayer, options) {
224
+ const selectedClass = this._umap._leafletMap.hasLayer(tilelayer) ? 'selected' : ''
225
+ const src = Utils.template(
226
+ tilelayer.options.url_template,
227
+ this._umap._leafletMap.options.demoTileInfos
228
+ )
229
+ const template = `
230
+ <li>
231
+ <img src="${src}" loading="lazy" />
232
+ <div>${tilelayer.options.name}</div>
233
+ </li>
234
+ `
235
+ const li = Utils.loadTemplate(template)
236
+ li.addEventListener('click', () => {
237
+ const oldTileLayer = this._umap.properties.tilelayer
238
+ this._umap._leafletMap.selectTileLayer(tilelayer)
239
+ this._umap._leafletMap._controls.tilelayers.setLayers()
240
+ if (options?.edit) {
241
+ this._umap.properties.tilelayer = tilelayer.toJSON()
242
+ this._umap.sync.update(
243
+ 'properties.tilelayer',
244
+ this._umap.properties.tilelayer,
245
+ oldTileLayer
246
+ )
247
+ }
248
+ })
249
+ return li
250
+ },
251
+ })
@@ -41,6 +41,11 @@ export const Heat = L.HeatLayer.extend({
41
41
  }
42
42
  },
43
43
 
44
+ removeLayer: (layer) => {
45
+ // No op, there is no "removeLatLng" in Leaflet.heat
46
+ // but this method is expected by DataLayer
47
+ },
48
+
44
49
  onAdd: function (map) {
45
50
  LayerMixin.onAdd.call(this, map)
46
51
  return L.HeatLayer.prototype.onAdd.call(this, map)