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
@@ -9,6 +9,190 @@ export const getDefaultFields = () => [
9
9
  { key: 'description', type: 'Text' },
10
10
  ]
11
11
 
12
+ export const Registry = {}
13
+
14
+ class BaseField {
15
+ constructor(key) {
16
+ this.key = key
17
+ }
18
+
19
+ values(features) {
20
+ return features
21
+ .map((feature) => this.parse(feature.properties[this.key]))
22
+ .filter((val, idx, arr) => arr.indexOf(val) === idx)
23
+ }
24
+
25
+ equal(expected, other) {
26
+ return expected === other
27
+ }
28
+
29
+ not_equal(expected, other) {
30
+ return expected !== other
31
+ }
32
+
33
+ gt(expected, other) {
34
+ return other > expected
35
+ }
36
+
37
+ lt(expected, other) {
38
+ return other < expected
39
+ }
40
+
41
+ render(value) {
42
+ return Utils.escapeHTML(value).trim()
43
+ }
44
+
45
+ cast(value) {
46
+ return this.parse(value)
47
+ }
48
+
49
+ dumps() {
50
+ return {
51
+ key: this.key,
52
+ type: this.TYPE,
53
+ }
54
+ }
55
+ }
56
+
57
+ Registry.String = class extends BaseField {
58
+ constructor(key) {
59
+ super(key)
60
+ // FIXME make it dynamic from class name
61
+ this.TYPE = 'String'
62
+ this.LABEL = translate('Short text')
63
+ }
64
+
65
+ parse(value) {
66
+ return String(value ?? '')
67
+ }
68
+
69
+ render(value) {
70
+ value = super.render(value)
71
+ // TODO, manage links (url, mailto, wikipedia...)
72
+ if (value.indexOf('http') === 0) {
73
+ value = `<a href="${value}" target="_blank">${value}</a>`
74
+ }
75
+ return value
76
+ }
77
+ }
78
+
79
+ Registry.Text = class extends Registry.String {
80
+ constructor(key) {
81
+ super(key)
82
+ // FIXME make it dynamic from class name
83
+ this.TYPE = 'Text'
84
+ this.LABEL = translate('Text')
85
+ }
86
+
87
+ render(value) {
88
+ return Utils.toHTML(value)
89
+ }
90
+ }
91
+
92
+ Registry.Number = class extends BaseField {
93
+ constructor(key) {
94
+ super(key)
95
+ // FIXME make it dynamic from class name
96
+ this.TYPE = 'Number'
97
+ this.LABEL = translate('Number')
98
+ }
99
+
100
+ parse(value) {
101
+ const parsed = Number.parseFloat(value)
102
+ if (Number.isNaN(parsed)) return null
103
+ return parsed
104
+ }
105
+ }
106
+
107
+ Registry.Datetime = class extends BaseField {
108
+ constructor(key) {
109
+ super(key)
110
+ // FIXME make it dynamic from class name
111
+ this.TYPE = 'Datetime'
112
+ this.LABEL = translate('Date and time')
113
+ }
114
+
115
+ parse(value) {
116
+ return new Date(value)
117
+ }
118
+ }
119
+
120
+ Registry.Date = class extends BaseField {
121
+ constructor(key) {
122
+ super(key)
123
+ // FIXME make it dynamic from class name
124
+ this.TYPE = 'Date'
125
+ this.LABEL = translate('Date')
126
+ }
127
+
128
+ parse(value) {
129
+ return Utils.parseNaiveDate(value)
130
+ }
131
+ }
132
+
133
+ Registry.Boolean = class extends BaseField {
134
+ constructor(key) {
135
+ super(key)
136
+ // FIXME make it dynamic from class name
137
+ this.TYPE = 'Boolean'
138
+ this.LABEL = translate('Yes / No')
139
+ }
140
+
141
+ parse(value) {
142
+ // 'yes' is used in OpenStreetMap data
143
+ return ['true', '1', 'yes'].includes(`${value}`.toLowerCase())
144
+ }
145
+ }
146
+
147
+ Registry.Enum = class extends BaseField {
148
+ constructor(key) {
149
+ super(key)
150
+ // FIXME make it dynamic from class name
151
+ this.TYPE = 'Enum'
152
+ this.LABEL = translate('List of values')
153
+ }
154
+
155
+ cast(value) {
156
+ return String(value || '')
157
+ }
158
+
159
+ parse(value) {
160
+ return this.cast(value)
161
+ .split(',')
162
+ .map((s) => s.trim())
163
+ }
164
+
165
+ values(features) {
166
+ return features
167
+ .reduce(
168
+ (acc, feature) => acc.concat(this.parse(feature.properties[this.key])),
169
+ []
170
+ )
171
+ .filter((val, idx, arr) => arr.indexOf(val) === idx)
172
+ }
173
+
174
+ equal(expected, other) {
175
+ return Boolean(new Set(other).intersection(new Set(expected)).size)
176
+ }
177
+
178
+ not_equal(expected, other) {
179
+ return !new Set(other).intersection(new Set(expected)).size
180
+ }
181
+
182
+ gt(expected, other) {
183
+ return false
184
+ }
185
+
186
+ lt(expected, other) {
187
+ return false
188
+ }
189
+ }
190
+
191
+ const FIELD_TYPES = Object.entries(Registry).map(([name, klass]) => [
192
+ name,
193
+ new klass().LABEL,
194
+ ])
195
+
12
196
  export class Fields extends Map {
13
197
  constructor(parent, dialog) {
14
198
  super()
@@ -38,7 +222,7 @@ export class Fields extends Map {
38
222
  this.parent.properties.fields = this.all().map((field) => {
39
223
  // We don't want to keep the reference, otherwise editing
40
224
  // it will also change the old value
41
- return { ...field }
225
+ return { ...field.dumps() }
42
226
  })
43
227
  }
44
228
 
@@ -60,10 +244,8 @@ export class Fields extends Map {
60
244
  console.error('Invalid field', field)
61
245
  return
62
246
  }
63
- field.type ??= 'String'
64
- // Copy object, so not to affect original
65
- // when edited.
66
- this.set(field.key, { ...field })
247
+ const klass = Registry[field.type] || Registry.String
248
+ this.set(field.key, new klass(field.key))
67
249
  this.push()
68
250
  }
69
251
 
@@ -103,7 +285,7 @@ export class Fields extends Map {
103
285
  const [row, { edit, del, addFilter, editFilter }] = Utils.loadTemplateWithRefs(
104
286
  `<li class="orderable with-toolbox" data-key="${field.key}">
105
287
  <span>
106
- <i class="icon icon-16 icon-field-${field.type}" title="${field.type}"></i>
288
+ <i class="icon icon-16 icon-field-${field.TYPE}" title="${field.LABEL}"></i>
107
289
  ${field.key}
108
290
  </span>
109
291
  <span>
@@ -161,16 +343,8 @@ export class Fields extends Map {
161
343
 
162
344
  async editField(name) {
163
345
  if (!name && this.parent.isRemoteLayer?.()) return
164
- const FIELD_TYPES = [
165
- ['String', translate('Short text')],
166
- ['Text', translate('Text')],
167
- ['Number', translate('Number')],
168
- ['Date', translate('Date')],
169
- ['Datetime', translate('Date and time')],
170
- ['Enum', translate('List of values')],
171
- ['Boolean', translate('Yes / No')],
172
- ]
173
- const field = this.get(name) || {}
346
+ const field = this.get(name)
347
+ const data = field?.dumps() || {}
174
348
  const metadatas = [
175
349
  [
176
350
  'key',
@@ -189,7 +363,7 @@ export class Fields extends Map {
189
363
  },
190
364
  ],
191
365
  ]
192
- const form = new Form(field, metadatas)
366
+ const form = new Form(data, metadatas)
193
367
 
194
368
  const [container, { body, addFilter }] = Utils.loadTemplateWithRefs(`
195
369
  <div>
@@ -202,32 +376,34 @@ export class Fields extends Map {
202
376
  if (this.parent.filters) {
203
377
  addFilter.addEventListener('click', () => {
204
378
  this.dialog.accept().then(() => {
205
- this.parent.filters.createFilterForm(field.key)
379
+ this.parent.filters.createFilterForm(data.key)
206
380
  })
207
381
  })
208
382
  addFilter.hidden = false
209
383
  }
210
384
 
211
385
  return this.dialog.open({ template: container }).then(() => {
212
- if (!this.validateName(field.key, field.key !== name)) {
386
+ if (!this.validateName(data.key, data.key !== name)) {
213
387
  this.pull()
214
388
  return
215
389
  }
216
390
  this.parent.sync.startBatch()
217
391
  const oldFields = Utils.CopyJSON(this.parent.properties.fields)
218
392
  if (!name) {
219
- this.add(field)
220
- } else if (name !== field.key) {
393
+ this.add(data)
394
+ } else if (name !== data.key || field.TYPE !== data.type) {
221
395
  this.clear()
222
396
  // Keep order on rename
223
397
  for (const old of oldFields) {
224
398
  if (old.key === name) {
225
- this.add(field)
399
+ this.add(data)
226
400
  } else {
227
401
  this.add(old)
228
402
  }
229
403
  }
230
- this.parent.renameField(name, field.key)
404
+ if (name !== data.key) {
405
+ this.parent.renameField(name, data.key)
406
+ }
231
407
  } else {
232
408
  this.push()
233
409
  }
@@ -1,9 +1,9 @@
1
1
  // FIXME: this module should not depend on Leaflet
2
2
  import {
3
3
  DomEvent,
4
- DomUtil,
5
4
  GeoJSON,
6
5
  stamp,
6
+ SVG,
7
7
  } from '../../../vendors/leaflet/leaflet-src.esm.js'
8
8
  import {
9
9
  uMapAlert as Alert,
@@ -19,6 +19,7 @@ import { Heat } from '../rendering/layers/heat.js'
19
19
  import * as Schema from '../schema.js'
20
20
  import TableEditor from '../tableeditor.js'
21
21
  import * as Utils from '../utils.js'
22
+ import * as DOMUtils from '../domutils.js'
22
23
  import { LineString, Point, Polygon } from './features.js'
23
24
  import Rules from '../rules.js'
24
25
  import { FeatureManager } from '../managers.js'
@@ -50,7 +51,7 @@ export class DataLayer {
50
51
  this.parentPane = this._leafletMap.getPane('overlayPane')
51
52
  this.pane = this._leafletMap.createPane(`datalayer${stamp(this)}`, this.parentPane)
52
53
  // FIXME: should be on layer
53
- this.renderer = L.svg({ pane: this.pane })
54
+ this.renderer = new SVG({ pane: this.pane })
54
55
  this.defaultProperties = {
55
56
  displayOnLoad: true,
56
57
  inCaption: true,
@@ -69,6 +70,7 @@ export class DataLayer {
69
70
  if (this.properties.rank === undefined) {
70
71
  this.properties.rank = this._umap.datalayers.count()
71
72
  }
73
+ this._umap.datalayers.add(this)
72
74
 
73
75
  if (!Utils.isObject(this.properties.remoteData)) {
74
76
  this.properties.remoteData = {}
@@ -82,19 +84,17 @@ export class DataLayer {
82
84
  this.properties.toZoom = this.properties.remoteData.to
83
85
  delete this.properties.remoteData.to
84
86
  }
85
- this.connectToMap()
86
87
  this.permissions = new DataLayerPermissions(this._umap, this)
87
- this.rules = new Rules(umap, this)
88
88
 
89
89
  this._needsFetch = this.createdOnServer || this.isRemoteLayer()
90
+ this.fields = new Fields(this, this._umap.dialog)
91
+ this.filters = new Filters(this, this._umap)
92
+ this.rules = new Rules(umap, this)
93
+ this._umap.onDataLayersChanged()
94
+
90
95
  if (!this.createdOnServer) {
91
96
  if (this.showAtLoad()) this.show()
92
97
  }
93
- if (!this._needsFetch && !this._umap.fields.size) {
94
- this.properties.fields = getDefaultFields()
95
- }
96
- this.fields = new Fields(this, this._umap.dialog)
97
- this.filters = new Filters(this, this._umap)
98
98
 
99
99
  // Only layers that are displayed on load must be hidden/shown
100
100
  // Automatically, others will be shown manually, and thus will
@@ -146,12 +146,6 @@ export class DataLayer {
146
146
  this.properties.rank = value
147
147
  }
148
148
 
149
- get fieldKeys() {
150
- // Needed to get a similar API from layer and uMap, but
151
- // uMap would return concat of all datalayers fields
152
- return Array.from(this.fields.keys())
153
- }
154
-
155
149
  get sortKey() {
156
150
  return this.getProperty('sortKey') || U.DEFAULT_LABEL_KEY
157
151
  }
@@ -230,27 +224,28 @@ export class DataLayer {
230
224
  }
231
225
 
232
226
  showAtLoad() {
233
- return this.autoLoaded && this.showAtZoom()
227
+ return this.autoVisibility && this.showAtZoom()
234
228
  }
235
229
 
236
- get autoLoaded() {
237
- if (this._autoLoaded === undefined) {
230
+ get autoVisibility() {
231
+ if (this._autoVisibility === undefined) {
238
232
  if (this._umap.datalayersFromQueryString) {
239
233
  const datalayerIds = this._umap.datalayersFromQueryString
240
- this._autoLoaded = datalayerIds.includes(this.id.toString())
234
+ this._autoVisibility = datalayerIds.includes(this.id.toString())
241
235
  if (this.properties.old_id) {
242
- this._autoLoaded =
243
- this._autoLoaded || datalayerIds.includes(this.properties.old_id.toString())
236
+ this._autoVisibility =
237
+ this._autoVisibility ||
238
+ datalayerIds.includes(this.properties.old_id.toString())
244
239
  }
245
240
  } else {
246
- this._autoLoaded = this.properties.displayOnLoad
241
+ this._autoVisibility = this.properties.displayOnLoad
247
242
  }
248
243
  }
249
- return this._autoLoaded
244
+ return this._autoVisibility
250
245
  }
251
246
 
252
- set autoLoaded(value) {
253
- this._autoLoaded = value
247
+ set autoVisibility(value) {
248
+ this._autoVisibility = value
254
249
  }
255
250
 
256
251
  insertBefore(other) {
@@ -356,10 +351,15 @@ export class DataLayer {
356
351
 
357
352
  async getUrl(url, initialUrl) {
358
353
  const response = await this._umap.request.get(url)
359
- return new Promise((resolve) => {
354
+ return new Promise(async (resolve) => {
360
355
  if (response?.ok) {
361
356
  this._umap.modifiedAt = response.headers.get('last-modified')
362
- return resolve(response.text())
357
+ const id = Math.random()
358
+ this._umap.loader.start(id)
359
+ const raw = await response.text()
360
+ const promise = resolve(raw)
361
+ this._umap.loader.stop(id)
362
+ return promise
363
363
  }
364
364
  Alert.error(
365
365
  translate('Cannot load remote data for layer "{layer}" with url "{url}"', {
@@ -412,11 +412,6 @@ export class DataLayer {
412
412
  this.resetLayer()
413
413
  }
414
414
 
415
- connectToMap() {
416
- this._umap.datalayers.add(this)
417
- this._umap.onDataLayersChanged()
418
- }
419
-
420
415
  _dataUrl() {
421
416
  let url = this._umap.urls.get('datalayer_view', {
422
417
  pk: this.id,
@@ -451,6 +446,10 @@ export class DataLayer {
451
446
  this._umap.featuresIndex[feature.getSlug()] = feature
452
447
  // TODO: quid for remote data ?
453
448
  this.inferFields(feature)
449
+ if (!this.fields.size && !this._umap.fields.size) {
450
+ this.properties.fields = getDefaultFields()
451
+ this.fields.pull()
452
+ }
454
453
  try {
455
454
  this.showFeature(feature)
456
455
  } catch (error) {
@@ -527,15 +526,15 @@ export class DataLayer {
527
526
  this.features.forEach((feature) => callback(feature))
528
527
  }
529
528
 
530
- sortedValues(property) {
531
- return this.features
532
- .all()
533
- .map((feature) => feature.properties[property])
534
- .filter((val, idx, arr) => arr.indexOf(val) === idx)
535
- .sort(Utils.naturalSort)
529
+ sortedValues(key) {
530
+ const field = this.fields.get(key) || this._umap.fields.get(key)
531
+ if (!field) return []
532
+ return field.values(this.features.all()).sort(Utils.naturalSort)
536
533
  }
537
534
 
538
535
  addData(geojson, sync) {
536
+ const id = Math.random()
537
+ this._umap.loader.start(id)
539
538
  let data = []
540
539
  this._batch = true
541
540
  try {
@@ -548,6 +547,7 @@ export class DataLayer {
548
547
  }
549
548
  this._batch = false
550
549
  this.dataChanged()
550
+ this._umap.loader.stop(id)
551
551
  return data
552
552
  }
553
553
 
@@ -777,7 +777,11 @@ export class DataLayer {
777
777
  },
778
778
  ],
779
779
  ]
780
- DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
780
+ container.appendChild(
781
+ DOMUtils.loadTemplate(`
782
+ <h3><i class="icon icon-16 icon-layers"></i>${translate('Layer properties')}</h3>
783
+ `)
784
+ )
781
785
  const builder = new MutatingForm(this, metadataFields)
782
786
  builder.on('set', ({ detail }) => {
783
787
  this._umap.onDataLayersChanged()
@@ -823,7 +827,7 @@ export class DataLayer {
823
827
  const builder = new MutatingForm(this, fields, {
824
828
  id: 'datalayer-advanced-properties',
825
829
  })
826
- const shapeFieldset = DomUtil.createFieldset(
830
+ const shapeFieldset = DOMUtils.createFieldset(
827
831
  container,
828
832
  translate('Shape properties')
829
833
  )
@@ -848,7 +852,7 @@ export class DataLayer {
848
852
  this.reindex()
849
853
  }
850
854
  })
851
- const advancedFieldset = DomUtil.createFieldset(
855
+ const advancedFieldset = DOMUtils.createFieldset(
852
856
  container,
853
857
  translate('Advanced properties')
854
858
  )
@@ -867,7 +871,7 @@ export class DataLayer {
867
871
  'properties.interactive',
868
872
  ]
869
873
  const builder = new MutatingForm(this, fields)
870
- const popupFieldset = DomUtil.createFieldset(
874
+ const popupFieldset = DOMUtils.createFieldset(
871
875
  container,
872
876
  translate('Interaction options')
873
877
  )
@@ -885,7 +889,7 @@ export class DataLayer {
885
889
  'properties.textPathPosition',
886
890
  ]
887
891
  const builder = new MutatingForm(this, fields)
888
- const fieldset = DomUtil.createFieldset(container, translate('Line decoration'))
892
+ const fieldset = DOMUtils.createFieldset(container, translate('Line decoration'))
889
893
  fieldset.appendChild(builder.build())
890
894
  }
891
895
 
@@ -935,23 +939,21 @@ export class DataLayer {
935
939
  fields.push('properties.remoteData.ttl')
936
940
  }
937
941
 
938
- const remoteDataContainer = DomUtil.createFieldset(
942
+ const remoteDataContainer = DOMUtils.createFieldset(
939
943
  container,
940
944
  translate('Remote data')
941
945
  )
942
946
  const builder = new MutatingForm(this, fields)
943
947
  remoteDataContainer.appendChild(builder.build())
944
- DomUtil.createButton(
945
- 'button umap-verify',
946
- remoteDataContainer,
947
- translate('Verify remote URL'),
948
- () => this.fetchRemoteData(true),
949
- this
950
- )
948
+ const button = DOMUtils.loadTemplate(`
949
+ <button class="umap-verify" type="button">${translate('Verify remote URL')}</button>
950
+ `)
951
+ button.addEventListener('click', () => this.fetchRemoteData(true))
952
+ remoteDataContainer.appendChild(button)
951
953
  }
952
954
 
953
955
  _buildAdvancedActions(container) {
954
- const advancedActions = DomUtil.createFieldset(
956
+ const advancedActions = DOMUtils.createFieldset(
955
957
  container,
956
958
  translate('Advanced actions')
957
959
  )
@@ -991,7 +993,7 @@ export class DataLayer {
991
993
  if (!this._umap.editEnabled) {
992
994
  return
993
995
  }
994
- const container = DomUtil.create('div', 'umap-layer-properties-container')
996
+ const container = document.createElement('div')
995
997
  this._editMetadata(container)
996
998
  this._editLayerProperties(container)
997
999
  this._editShapeProperties(container)
@@ -1008,15 +1010,13 @@ export class DataLayer {
1008
1010
 
1009
1011
  this._buildAdvancedActions(container)
1010
1012
 
1011
- const backButton = DomUtil.createButtonIcon(
1012
- undefined,
1013
- 'icon-back',
1014
- translate('Back to layers')
1015
- )
1013
+ const backButton = DOMUtils.loadTemplate(`
1014
+ <button class="icon icon-16 icon-back" type="button" title="${translate('Back to layers')}"></button>
1015
+ `)
1016
1016
  // Fixme: remove me when this is merged and released
1017
1017
  // https://github.com/Leaflet/Leaflet/pull/9052
1018
1018
  DomEvent.disableClickPropagation(backButton)
1019
- DomEvent.on(backButton, 'click', this._umap.editDatalayers, this._umap)
1019
+ backButton.addEventListener('click', () => this._umap.editDatalayers())
1020
1020
 
1021
1021
  return this._umap.editPanel.open({
1022
1022
  content: container,
@@ -1067,14 +1067,14 @@ export class DataLayer {
1067
1067
  button.addEventListener('click', () => this.restore(data.ref))
1068
1068
  }
1069
1069
 
1070
- const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
1071
- async callback() {
1070
+ const versionsContainer = DOMUtils.createFieldset(container, translate('Versions'))
1071
+ versionsContainer.closest('details').addEventListener('toggle', async (event) => {
1072
+ if (event.target.open) {
1072
1073
  const [{ versions }, response, error] = await this._umap.server.get(
1073
1074
  this.getVersionsUrl()
1074
1075
  )
1075
1076
  if (!error) versions.forEach(appendVersion)
1076
- },
1077
- context: this,
1077
+ }
1078
1078
  })
1079
1079
  }
1080
1080
 
@@ -1124,7 +1124,7 @@ export class DataLayer {
1124
1124
  // From now on, do not try to how/hide
1125
1125
  // automatically this layer, as user
1126
1126
  // has taken control on this.
1127
- this.autoLoaded = false
1127
+ this.autoVisibility = false
1128
1128
  let display = force
1129
1129
  if (force === undefined) {
1130
1130
  if (!this.isVisible()) display = true
@@ -1290,7 +1290,7 @@ export class DataLayer {
1290
1290
 
1291
1291
  this.setReferenceVersion({ response, sync: true })
1292
1292
 
1293
- this.connectToMap()
1293
+ this._umap.onDataLayersChanged()
1294
1294
  this.redraw() // Needed for reordering features
1295
1295
  return true
1296
1296
  }
@@ -1345,7 +1345,10 @@ export class DataLayer {
1345
1345
  rules.set(rule.condition, rule)
1346
1346
  }
1347
1347
  for (const rule of this._umap.rules) {
1348
- if (!rules.has(rule.condition) && this.fields.has(rule.key)) {
1348
+ if (
1349
+ !rules.has(rule.condition) &&
1350
+ (this.fields.has(rule.field.key) || this._umap.fields.has(rule.field.key))
1351
+ ) {
1349
1352
  rules.set(rule.condition, rule)
1350
1353
  }
1351
1354
  }
@@ -1364,31 +1367,17 @@ export class DataLayer {
1364
1367
  }
1365
1368
 
1366
1369
  renderToolbox(container) {
1367
- const toggle = DomUtil.createButtonIcon(
1368
- container,
1369
- 'icon-eye',
1370
- translate('Show/hide layer')
1371
- )
1372
- const table = DomUtil.createButtonIcon(
1373
- container,
1374
- 'icon-table show-on-edit',
1375
- translate('Edit properties in a table')
1376
- )
1377
- const zoomTo = DomUtil.createButtonIcon(
1378
- container,
1379
- 'icon-zoom',
1380
- translate('Zoom to layer extent')
1381
- )
1382
- const edit = DomUtil.createButtonIcon(
1383
- container,
1384
- 'icon-edit show-on-edit',
1385
- translate('Edit')
1386
- )
1387
- const remove = DomUtil.createButtonIcon(
1388
- container,
1389
- 'icon-delete show-on-edit',
1390
- translate('Delete layer')
1391
- )
1370
+ const [span, { toggle, table, zoomTo, edit, remove }] =
1371
+ DOMUtils.loadTemplateWithRefs(`
1372
+ <span>
1373
+ <button class="icon icon-16 icon-eye" title="${translate('Show/hide layer')}" type="button" data-ref="toggle"></button>
1374
+ <button class="icon icon-16 icon-table show-on-edit" title="${translate('Edit properties in a table')}" type="button" data-ref="table"></button>
1375
+ <button class="icon icon-16 icon-zoom" title="${translate('Zoom to layer extent')}" type="button" data-ref="zoomTo"></button>
1376
+ <button class="icon icon-16 icon-edit show-on-edit" title="${translate('Edit')}" type="button" data-ref="edit"></button>
1377
+ <button class="icon icon-16 icon-delete show-on-edit" title="${translate('Delete layer')}" type="button" data-ref="remove"></button>
1378
+ </span>
1379
+ `)
1380
+ container.appendChild(span)
1392
1381
  if (this.isReadOnly()) {
1393
1382
  container.classList.add('readonly')
1394
1383
  } else {
@@ -1412,7 +1401,7 @@ export class DataLayer {
1412
1401
  propagateDelete() {
1413
1402
  const els = this.getHidableElements()
1414
1403
  for (const el of els) {
1415
- DomUtil.remove(el)
1404
+ el.remove()
1416
1405
  }
1417
1406
  }
1418
1407
 
@@ -1425,15 +1414,15 @@ export class DataLayer {
1425
1414
 
1426
1415
  propagateHide() {
1427
1416
  const els = this.getHidableElements()
1428
- for (let i = 0; i < els.length; i++) {
1429
- DomUtil.addClass(els[i], 'off')
1417
+ for (const el of els) {
1418
+ el.classList.add('off')
1430
1419
  }
1431
1420
  }
1432
1421
 
1433
1422
  propagateShow() {
1434
1423
  const els = this.getHidableElements()
1435
- for (let i = 0; i < els.length; i++) {
1436
- DomUtil.removeClass(els[i], 'off')
1424
+ for (const el of els) {
1425
+ el.classList.remove('off')
1437
1426
  }
1438
1427
  }
1439
1428
  }