umap-project 3.3.5__py3-none-any.whl → 3.4.0b0__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 (218) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  4. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/da/LC_MESSAGES/django.po +43 -33
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +35 -29
  8. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/el/LC_MESSAGES/django.po +35 -29
  10. umap/locale/en/LC_MESSAGES/django.po +34 -28
  11. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/es/LC_MESSAGES/django.po +43 -33
  13. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/et/LC_MESSAGES/django.po +58 -54
  15. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  17. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  19. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  21. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  22. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  23. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  24. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  25. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  26. umap/locale/is/LC_MESSAGES/django.po +43 -33
  27. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  28. umap/locale/it/LC_MESSAGES/django.po +43 -33
  29. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  30. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  31. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  32. umap/locale/pl/LC_MESSAGES/django.po +43 -33
  33. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  34. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  35. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  36. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  37. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  38. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  39. umap/management/commands/switch_user.py +2 -2
  40. umap/static/umap/base.css +89 -32
  41. umap/static/umap/content.css +129 -33
  42. umap/static/umap/css/bar.css +82 -20
  43. umap/static/umap/css/browser.css +163 -0
  44. umap/static/umap/css/contextmenu.css +15 -0
  45. umap/static/umap/css/dialog.css +36 -16
  46. umap/static/umap/css/form.css +122 -32
  47. umap/static/umap/css/icon.css +46 -3
  48. umap/static/umap/css/panel.css +7 -3
  49. umap/static/umap/css/popup.css +34 -8
  50. umap/static/umap/css/tooltip.css +8 -4
  51. umap/static/umap/img/16-white.svg +26 -8
  52. umap/static/umap/img/16.svg +1 -1
  53. umap/static/umap/img/source/16-white.svg +36 -18
  54. umap/static/umap/img/source/16.svg +1 -1
  55. umap/static/umap/js/components/alerts/alert.css +69 -31
  56. umap/static/umap/js/components/alerts/alert.js +20 -2
  57. umap/static/umap/js/modules/browser.js +64 -56
  58. umap/static/umap/js/modules/caption.js +10 -7
  59. umap/static/umap/js/modules/data/features.js +82 -59
  60. umap/static/umap/js/modules/data/layer.js +75 -166
  61. umap/static/umap/js/modules/domutils.js +109 -0
  62. umap/static/umap/js/modules/filters.js +807 -0
  63. umap/static/umap/js/modules/form/builder.js +8 -5
  64. umap/static/umap/js/modules/form/fields.js +110 -220
  65. umap/static/umap/js/modules/formatter.js +24 -1
  66. umap/static/umap/js/modules/help.js +3 -2
  67. umap/static/umap/js/modules/importers/opendata.js +5 -0
  68. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  69. umap/static/umap/js/modules/managers.js +265 -0
  70. umap/static/umap/js/modules/permissions.js +35 -31
  71. umap/static/umap/js/modules/rendering/controls.js +7 -7
  72. umap/static/umap/js/modules/rendering/icon.js +3 -8
  73. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  74. umap/static/umap/js/modules/rendering/layers/classified.js +17 -10
  75. umap/static/umap/js/modules/rendering/layers/cluster.js +2 -2
  76. umap/static/umap/js/modules/rendering/template.js +44 -8
  77. umap/static/umap/js/modules/rendering/ui.js +29 -23
  78. umap/static/umap/js/modules/rules.js +4 -3
  79. umap/static/umap/js/modules/schema.js +3 -6
  80. umap/static/umap/js/modules/share.js +4 -3
  81. umap/static/umap/js/modules/tableeditor.js +50 -38
  82. umap/static/umap/js/modules/templates.js +2 -3
  83. umap/static/umap/js/modules/ui/bar.js +42 -18
  84. umap/static/umap/js/modules/ui/dialog.js +33 -31
  85. umap/static/umap/js/modules/ui/panel.js +21 -7
  86. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  87. umap/static/umap/js/modules/umap.js +155 -46
  88. umap/static/umap/js/modules/utils.js +23 -1
  89. umap/static/umap/js/umap.core.js +1 -110
  90. umap/static/umap/locale/am_ET.js +40 -14
  91. umap/static/umap/locale/am_ET.json +40 -14
  92. umap/static/umap/locale/ar.js +40 -14
  93. umap/static/umap/locale/ar.json +40 -14
  94. umap/static/umap/locale/ast.js +40 -14
  95. umap/static/umap/locale/ast.json +40 -14
  96. umap/static/umap/locale/bg.js +40 -14
  97. umap/static/umap/locale/bg.json +40 -14
  98. umap/static/umap/locale/br.js +47 -21
  99. umap/static/umap/locale/br.json +47 -21
  100. umap/static/umap/locale/ca.js +40 -14
  101. umap/static/umap/locale/ca.json +40 -14
  102. umap/static/umap/locale/cs_CZ.js +40 -14
  103. umap/static/umap/locale/cs_CZ.json +40 -14
  104. umap/static/umap/locale/da.js +40 -14
  105. umap/static/umap/locale/da.json +40 -14
  106. umap/static/umap/locale/de.js +39 -13
  107. umap/static/umap/locale/de.json +39 -13
  108. umap/static/umap/locale/el.js +40 -14
  109. umap/static/umap/locale/el.json +40 -14
  110. umap/static/umap/locale/en.js +39 -13
  111. umap/static/umap/locale/en.json +39 -13
  112. umap/static/umap/locale/en_US.json +40 -14
  113. umap/static/umap/locale/es.js +40 -14
  114. umap/static/umap/locale/es.json +40 -14
  115. umap/static/umap/locale/et.js +79 -53
  116. umap/static/umap/locale/et.json +79 -53
  117. umap/static/umap/locale/eu.js +72 -46
  118. umap/static/umap/locale/eu.json +72 -46
  119. umap/static/umap/locale/fa_IR.js +40 -14
  120. umap/static/umap/locale/fa_IR.json +40 -14
  121. umap/static/umap/locale/fi.js +40 -14
  122. umap/static/umap/locale/fi.json +40 -14
  123. umap/static/umap/locale/fr.js +39 -13
  124. umap/static/umap/locale/fr.json +39 -13
  125. umap/static/umap/locale/gl.js +40 -14
  126. umap/static/umap/locale/gl.json +40 -14
  127. umap/static/umap/locale/he.js +40 -14
  128. umap/static/umap/locale/he.json +40 -14
  129. umap/static/umap/locale/hr.js +40 -14
  130. umap/static/umap/locale/hr.json +40 -14
  131. umap/static/umap/locale/hu.js +40 -14
  132. umap/static/umap/locale/hu.json +40 -14
  133. umap/static/umap/locale/id.js +40 -14
  134. umap/static/umap/locale/id.json +40 -14
  135. umap/static/umap/locale/is.js +40 -14
  136. umap/static/umap/locale/is.json +40 -14
  137. umap/static/umap/locale/it.js +40 -14
  138. umap/static/umap/locale/it.json +40 -14
  139. umap/static/umap/locale/ja.js +40 -14
  140. umap/static/umap/locale/ja.json +40 -14
  141. umap/static/umap/locale/ko.js +40 -14
  142. umap/static/umap/locale/ko.json +40 -14
  143. umap/static/umap/locale/lt.js +40 -14
  144. umap/static/umap/locale/lt.json +40 -14
  145. umap/static/umap/locale/ms.js +40 -14
  146. umap/static/umap/locale/ms.json +40 -14
  147. umap/static/umap/locale/nl.js +40 -14
  148. umap/static/umap/locale/nl.json +40 -14
  149. umap/static/umap/locale/no.js +40 -14
  150. umap/static/umap/locale/no.json +40 -14
  151. umap/static/umap/locale/pl.js +40 -14
  152. umap/static/umap/locale/pl.json +40 -14
  153. umap/static/umap/locale/pl_PL.json +40 -14
  154. umap/static/umap/locale/pt.js +40 -14
  155. umap/static/umap/locale/pt.json +40 -14
  156. umap/static/umap/locale/pt_BR.js +40 -14
  157. umap/static/umap/locale/pt_BR.json +40 -14
  158. umap/static/umap/locale/pt_PT.js +40 -14
  159. umap/static/umap/locale/pt_PT.json +40 -14
  160. umap/static/umap/locale/ro.js +40 -14
  161. umap/static/umap/locale/ro.json +40 -14
  162. umap/static/umap/locale/ru.js +40 -14
  163. umap/static/umap/locale/ru.json +40 -14
  164. umap/static/umap/locale/sk_SK.js +40 -14
  165. umap/static/umap/locale/sk_SK.json +40 -14
  166. umap/static/umap/locale/sl.js +40 -14
  167. umap/static/umap/locale/sl.json +40 -14
  168. umap/static/umap/locale/sr.js +40 -14
  169. umap/static/umap/locale/sr.json +40 -14
  170. umap/static/umap/locale/sv.js +40 -14
  171. umap/static/umap/locale/sv.json +40 -14
  172. umap/static/umap/locale/th_TH.js +40 -14
  173. umap/static/umap/locale/th_TH.json +40 -14
  174. umap/static/umap/locale/tr.js +40 -14
  175. umap/static/umap/locale/tr.json +40 -14
  176. umap/static/umap/locale/uk_UA.js +40 -14
  177. umap/static/umap/locale/uk_UA.json +40 -14
  178. umap/static/umap/locale/vi.js +40 -14
  179. umap/static/umap/locale/vi.json +40 -14
  180. umap/static/umap/locale/vi_VN.json +40 -14
  181. umap/static/umap/locale/zh.js +40 -14
  182. umap/static/umap/locale/zh.json +40 -14
  183. umap/static/umap/locale/zh_CN.json +40 -14
  184. umap/static/umap/locale/zh_TW.Big5.json +40 -14
  185. umap/static/umap/locale/zh_TW.js +40 -14
  186. umap/static/umap/locale/zh_TW.json +40 -14
  187. umap/static/umap/map.css +60 -223
  188. umap/static/umap/unittests/utils.js +18 -0
  189. umap/static/umap/vars.css +23 -5
  190. umap/templates/umap/components/alerts/alert.html +32 -29
  191. umap/templates/umap/css.html +2 -1
  192. umap/templates/umap/login_popup_end.html +18 -9
  193. umap/templates/umap/user_map_table.html +7 -2
  194. umap/tests/integration/conftest.py +2 -6
  195. umap/tests/integration/test_anonymous_owned_map.py +89 -36
  196. umap/tests/integration/test_basics.py +25 -1
  197. umap/tests/integration/test_browser.py +37 -0
  198. umap/tests/integration/test_datalayer.py +9 -16
  199. umap/tests/integration/test_draw_polygon.py +2 -0
  200. umap/tests/integration/test_edit_marker.py +1 -1
  201. umap/tests/integration/test_export_map.py +19 -0
  202. umap/tests/integration/test_fields.py +522 -0
  203. umap/tests/integration/test_filters.py +617 -0
  204. umap/tests/integration/test_import.py +15 -42
  205. umap/tests/integration/test_remote_data.py +60 -4
  206. umap/tests/integration/test_share.py +4 -4
  207. umap/tests/integration/test_tableeditor.py +31 -7
  208. umap/tests/integration/test_websocket_sync.py +3 -1
  209. umap/tests/test_dashboard.py +10 -0
  210. umap/urls.py +1 -0
  211. umap/views.py +5 -0
  212. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/METADATA +12 -12
  213. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/RECORD +216 -213
  214. umap/static/umap/js/modules/facets.js +0 -164
  215. umap/tests/integration/test_facets_browser.py +0 -279
  216. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/WHEEL +0 -0
  217. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/entry_points.txt +0 -0
  218. {umap_project-3.3.5.dist-info → umap_project-3.4.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -10,7 +10,7 @@ import {
10
10
  import Browser from './browser.js'
11
11
  import Caption from './caption.js'
12
12
  import { DataLayer } from './data/layer.js'
13
- import Facets from './facets.js'
13
+ import { Filters, migrateLegacyFilters } from './filters.js'
14
14
  import { MutatingForm } from './form/builder.js'
15
15
  import { Formatter } from './formatter.js'
16
16
  import Help from './help.js'
@@ -32,7 +32,8 @@ import { EditPanel, FullPanel, Panel } from './ui/panel.js'
32
32
  import Tooltip from './ui/tooltip.js'
33
33
  import URLs from './urls.js'
34
34
  import * as Utils from './utils.js'
35
- import { DataLayerManager } from './managers.js'
35
+ import * as DOMUtils from './domutils.js'
36
+ import { DataLayerManager, FieldManager } from './managers.js'
36
37
  import { Importer as OpenRouteService } from './importers/openrouteservice.js'
37
38
 
38
39
  export default class Umap {
@@ -48,6 +49,7 @@ export default class Umap {
48
49
  }
49
50
 
50
51
  async init(element, geojson) {
52
+ this.migrateLegacyProperties(geojson.properties)
51
53
  this.properties = Object.assign(
52
54
  {
53
55
  enableMarkerDraw: true,
@@ -131,7 +133,8 @@ export default class Umap {
131
133
  this.contextmenu = new ContextMenu()
132
134
  this.server = new ServerRequest()
133
135
  this.request = new Request()
134
- this.facets = new Facets(this)
136
+ this.fields = new FieldManager(this, this.dialog)
137
+ this.filters = new Filters(this, this)
135
138
  this.browser = new Browser(this, this._leafletMap)
136
139
  this.caption = new Caption(this, this._leafletMap)
137
140
  this.importer = new Importer(this)
@@ -160,10 +163,6 @@ export default class Umap {
160
163
  ) {
161
164
  this.properties.slideshow.active = true
162
165
  }
163
- if (this.properties.advancedFilterKey) {
164
- this.properties.facetKey = this.properties.advancedFilterKey
165
- delete this.properties.advancedFilterKey
166
- }
167
166
 
168
167
  // Global storage for retrieving datalayers and features.
169
168
  this.datalayers = new DataLayerManager()
@@ -261,15 +260,13 @@ export default class Umap {
261
260
  return window.self !== window.top
262
261
  }
263
262
 
264
- get fields() {
265
- if (!this.properties.fields) this.properties.fields = []
266
- return this.properties.fields
267
- }
268
-
269
263
  get fieldKeys() {
270
- return this.fields
271
- .map((field) => field.key)
272
- .concat(...this.datalayers.active().map((dl) => dl.fieldKeys))
264
+ return Array.from(
265
+ new Set([
266
+ ...this.fields.keys(),
267
+ ...this.datalayers.active().reduce((acc, dl) => acc.concat(dl.fieldKeys), []),
268
+ ])
269
+ )
273
270
  }
274
271
 
275
272
  setPropertiesFromQueryString() {
@@ -391,6 +388,13 @@ export default class Umap {
391
388
  }
392
389
  }
393
390
 
391
+ hasFilters() {
392
+ return (
393
+ this.filters.isActive() ||
394
+ this.datalayers.active().some((d) => d.filters.isActive())
395
+ )
396
+ }
397
+
394
398
  getOwnContextMenu(event) {
395
399
  const items = []
396
400
  if (this.editEnabled) {
@@ -424,7 +428,7 @@ export default class Umap {
424
428
  action: () => this.openBrowser('data'),
425
429
  }
426
430
  )
427
- if (this.properties.facetKey) {
431
+ if (this.hasFilters()) {
428
432
  items.push({
429
433
  label: translate('Filter data'),
430
434
  action: () => this.openBrowser('filters'),
@@ -444,7 +448,13 @@ export default class Umap {
444
448
  }
445
449
 
446
450
  getSharedContextMenu(event) {
447
- const items = []
451
+ const latlng = `${event.latlng.lat.toFixed(6)},${event.latlng.lng.toFixed(6)}`
452
+ const items = [
453
+ {
454
+ label: latlng,
455
+ action: () => DOMUtils.copyToClipboard(latlng),
456
+ },
457
+ ]
448
458
  if (this.properties.urls.routing) {
449
459
  items.push({
450
460
  label: translate('Directions from here'),
@@ -481,7 +491,7 @@ export default class Umap {
481
491
  }
482
492
 
483
493
  search() {
484
- if (this._leafletMap._controls.search) this._leafletMap._controls.search.open()
494
+ if (this._leafletMap._controls.search) this._leafletMap._controls.search.onClick()
485
495
  }
486
496
 
487
497
  hasEditMode() {
@@ -720,7 +730,14 @@ export default class Umap {
720
730
  return this.properties.name || translate('Untitled map')
721
731
  }
722
732
 
733
+ migrateLegacyProperties(properties) {
734
+ if (migrateLegacyFilters(properties)) {
735
+ this._migrated = true
736
+ }
737
+ }
738
+
723
739
  setProperties(newProperties) {
740
+ this.migrateLegacyProperties(newProperties)
724
741
  for (const key of Object.keys(SCHEMA)) {
725
742
  if (newProperties[key] !== undefined) {
726
743
  this.properties[key] = newProperties[key]
@@ -758,7 +775,7 @@ export default class Umap {
758
775
  'properties.is_template',
759
776
  ]
760
777
 
761
- DomUtil.createTitle(container, translate('Edit map details'), 'icon-caption')
778
+ DomUtil.createTitle(container, translate('Edit map details'), 'icon-info')
762
779
  const builder = new MutatingForm(this, metadataFields, {
763
780
  className: 'map-metadata',
764
781
  umap: this,
@@ -868,23 +885,22 @@ export default class Umap {
868
885
  defaultShapeProperties.appendChild(builder.build())
869
886
  }
870
887
 
871
- _editDefaultProperties(container) {
872
- const optionsFields = [
888
+ _editDefaultKeys(container) {
889
+ const shapeOptions = [
873
890
  'properties.zoomTo',
874
891
  'properties.easing',
875
892
  'properties.labelKey',
876
893
  'properties.sortKey',
877
894
  'properties.filterKey',
878
- 'properties.facetKey',
879
895
  'properties.slugKey',
880
896
  ]
881
897
 
882
- const builder = new MutatingForm(this, optionsFields, { umap: this })
883
- const defaultProperties = DomUtil.createFieldset(
898
+ const builder = new MutatingForm(this, shapeOptions, { umap: this })
899
+ const defaultShapeProperties = DomUtil.createFieldset(
884
900
  container,
885
901
  translate('Default properties')
886
902
  )
887
- defaultProperties.appendChild(builder.build())
903
+ defaultShapeProperties.appendChild(builder.build())
888
904
  }
889
905
 
890
906
  _editInteractionsProperties(container) {
@@ -1159,7 +1175,8 @@ export default class Umap {
1159
1175
  )
1160
1176
  this._editControls(container)
1161
1177
  this._editShapeProperties(container)
1162
- this._editDefaultProperties(container)
1178
+ this._editDefaultKeys(container)
1179
+ this.fields.edit(container)
1163
1180
  this._editInteractionsProperties(container)
1164
1181
  this.rules.edit(container)
1165
1182
  this._editTilelayer(container)
@@ -1178,6 +1195,16 @@ export default class Umap {
1178
1195
  })
1179
1196
  }
1180
1197
 
1198
+ onAnonymousSave(editUrl) {
1199
+ AlertCreation.info(
1200
+ this,
1201
+ translate('Hey, you created a map without an account!'),
1202
+ Number.Infinity,
1203
+ editUrl,
1204
+ this.properties.urls.map_send_edit_link ? this.sendEditLinkEmail.bind(this) : null
1205
+ )
1206
+ }
1207
+
1181
1208
  async save() {
1182
1209
  const geojson = {
1183
1210
  type: 'Feature',
@@ -1200,8 +1227,7 @@ export default class Umap {
1200
1227
  // TOOD: map.save may not always be the first call during save process
1201
1228
  // since SAVEMANAGER refactor
1202
1229
  if (data.login_required) {
1203
- window.onLogin = () => this.saveAll()
1204
- window.open(data.login_required)
1230
+ this.askForLogin().then(() => this.saveAll())
1205
1231
  return
1206
1232
  }
1207
1233
  this.properties.user = data.user
@@ -1209,17 +1235,9 @@ export default class Umap {
1209
1235
  this.properties.id = data.id
1210
1236
  this.permissions.setProperties(data.permissions)
1211
1237
  this.permissions.commit()
1212
- if (data.permissions?.anonymous_edit_url) {
1213
- this._leafletMap.once('saved', () => {
1214
- AlertCreation.info(
1215
- translate('Your map has been created with an anonymous account!'),
1216
- Number.Infinity,
1217
- data.permissions.anonymous_edit_url,
1218
- this.properties.urls.map_send_edit_link
1219
- ? this.sendEditLinkEmail.bind(this)
1220
- : null
1221
- )
1222
- })
1238
+ const anonymousEditUrl = data.permissions?.anonymous_edit_url
1239
+ if (anonymousEditUrl) {
1240
+ this._leafletMap.once('saved', () => this.onAnonymousSave(anonymousEditUrl))
1223
1241
  } else {
1224
1242
  this._leafletMap.once('saved', () => {
1225
1243
  Alert.success(translate('Congratulations, your map has been created!'))
@@ -1241,6 +1259,28 @@ export default class Umap {
1241
1259
  return true
1242
1260
  }
1243
1261
 
1262
+ askForLogin() {
1263
+ const promise = new Promise((resolve) => {
1264
+ const bc = new BroadcastChannel('auth')
1265
+ bc.onmessage = (event) => {
1266
+ if (event.data === 'auth:ok') {
1267
+ bc.postMessage('auth:close')
1268
+ const url = this.urls.get('whoami', { map_id: this.id })
1269
+ this.server.get(url).then(([data]) => {
1270
+ this.properties.user = data.user
1271
+ if (!this.id) {
1272
+ this.properties.permissions.owner = { ...data.user }
1273
+ }
1274
+ this.render(['user', 'properties.permissions'])
1275
+ resolve()
1276
+ })
1277
+ }
1278
+ }
1279
+ })
1280
+ window.open(this.urls.get('login'))
1281
+ return promise
1282
+ }
1283
+
1244
1284
  exportProperties() {
1245
1285
  const properties = {}
1246
1286
  for (const key of Object.keys(SCHEMA)) {
@@ -1251,6 +1291,18 @@ export default class Umap {
1251
1291
  return properties
1252
1292
  }
1253
1293
 
1294
+ renameField(oldName, newName) {
1295
+ for (const datalayer of this.datalayers.active()) {
1296
+ datalayer.renameFeaturesField(oldName, newName)
1297
+ }
1298
+ }
1299
+
1300
+ deleteField(name) {
1301
+ for (const datalayer of this.datalayers.active()) {
1302
+ datalayer.deleteFeaturesField(name)
1303
+ }
1304
+ }
1305
+
1254
1306
  geometry() {
1255
1307
  /* Return a GeoJSON geometry Object */
1256
1308
  const latlng = this._leafletMap.latLng(
@@ -1269,6 +1321,51 @@ export default class Umap {
1269
1321
  this.drop.enable()
1270
1322
  this.fire('edit:enabled')
1271
1323
  this.initSyncEngine()
1324
+ this.checkForLegacy()
1325
+ this.checkForAnonymous()
1326
+ }
1327
+
1328
+ checkForAnonymous() {
1329
+ if (
1330
+ this.permissions.isAnonymousMap() &&
1331
+ this.permissions.isOwner() &&
1332
+ this.permissions.userIsAuth()
1333
+ ) {
1334
+ this.dialog
1335
+ .confirm(
1336
+ translate('This map is anonymous, do you want to attach it to your account?')
1337
+ )
1338
+ .then(() => {
1339
+ this.permissions.attach()
1340
+ })
1341
+ }
1342
+ }
1343
+
1344
+ checkForLegacy() {
1345
+ let needSaveAlert = false
1346
+ if (this._migrated) {
1347
+ needSaveAlert = true
1348
+ delete this._migrated
1349
+ // Force user to save
1350
+ this.sync.update('properties.name', this.properties.name, this.properties.name)
1351
+ }
1352
+ for (const datalayer of this.datalayers.active()) {
1353
+ if (!datalayer.isReadOnly() && datalayer._migrated) {
1354
+ datalayer._migrated = false
1355
+ // Force user to resave those datalayers
1356
+ datalayer.sync.update(
1357
+ 'properties.name',
1358
+ datalayer.properties.name,
1359
+ datalayer.properties.name
1360
+ )
1361
+ needSaveAlert = true
1362
+ }
1363
+ }
1364
+ if (needSaveAlert) {
1365
+ Alert.warning(
1366
+ translate('The map has been upgraded to latest version, please save it.')
1367
+ )
1368
+ }
1272
1369
  }
1273
1370
 
1274
1371
  disableEdit() {
@@ -1312,6 +1409,12 @@ export default class Umap {
1312
1409
  // Propagate will remove the fields it has already
1313
1410
  // processed
1314
1411
  fields = this.propagate(fields)
1412
+ if (fields.includes('properties.filters')) {
1413
+ this.filters.load()
1414
+ if (this.browser.isOpen()) {
1415
+ this.browser.buildFilters()
1416
+ }
1417
+ }
1315
1418
 
1316
1419
  const impacts = Utils.getImpactsFromSchema(fields)
1317
1420
  for (const impact of impacts) {
@@ -1364,7 +1467,7 @@ export default class Umap {
1364
1467
  },
1365
1468
  user: () => {
1366
1469
  Utils.eachElement('.umap-user .username', (el) => {
1367
- if (this.properties.user?.id) {
1470
+ if (this.permissions.userIsAuth()) {
1368
1471
  el.textContent = this.properties.user.name
1369
1472
  }
1370
1473
  })
@@ -1471,17 +1574,22 @@ export default class Umap {
1471
1574
  `
1472
1575
  const [container, { ul }] = Utils.loadTemplateWithRefs(template)
1473
1576
  this.datalayers.reverse().map((datalayer) => {
1474
- const row = Utils.loadTemplate(
1475
- `<li class="orderable"><i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i></li>`
1476
- )
1477
- datalayer.renderToolbox(row)
1577
+ const [row, { toolbox, formbox }] = Utils.loadTemplateWithRefs(`
1578
+ <li class="orderable with-toolbox ${datalayer.cssId}">
1579
+ <span data-ref=formbox class="datalayer-editable-title truncate"></span>
1580
+ <span data-ref=toolbox>
1581
+ <i class="icon icon-16 icon-drag" title="${translate('Drag to reorder')}"></i>
1582
+ </span>
1583
+ </li>
1584
+ `)
1585
+ datalayer.renderToolbox(toolbox)
1478
1586
  const builder = new MutatingForm(
1479
1587
  datalayer,
1480
1588
  [['properties.name', { handler: 'EditableText' }]],
1481
1589
  { className: 'umap-form-inline' }
1482
1590
  )
1483
1591
  const form = builder.build()
1484
- row.appendChild(form)
1592
+ formbox.appendChild(form)
1485
1593
  row.classList.toggle('off', !datalayer.isVisible())
1486
1594
  row.dataset.id = datalayer.id
1487
1595
  ul.appendChild(row)
@@ -1624,6 +1732,7 @@ export default class Umap {
1624
1732
  const fields = Object.keys(importedData.properties).map(
1625
1733
  (field) => `properties.${field}`
1626
1734
  )
1735
+ this.filters.load()
1627
1736
  this.render(fields)
1628
1737
  this._leafletMap._setDefaultCenter()
1629
1738
  }
@@ -1637,7 +1746,7 @@ export default class Umap {
1637
1746
  this.importRaw(rawData)
1638
1747
  } catch (e) {
1639
1748
  console.error('Error importing data', e)
1640
- U.Alert.error(
1749
+ Alert.error(
1641
1750
  translate('Invalid umap data in {filename}', { filename: file.name })
1642
1751
  )
1643
1752
  }
@@ -400,8 +400,30 @@ export function template(str, data) {
400
400
  })
401
401
  }
402
402
 
403
+ const DATE_REGEX = [
404
+ // Format 1: "YYYY-MM-DD"
405
+ /^(?<year>\d{4})[\-\/](?<month>\d{2})[\-\/](?<day>\d{2})$/,
406
+ // Format2 : "DD-MM-YYYY"
407
+ /^(?<day>0[1-9]|[12][0-9]|3[01])[\-\/](?<month>0[1-9]|1[0-2])[\-\/](?<year>\d{4})/,
408
+ ]
409
+
403
410
  export function parseNaiveDate(value) {
404
- const naive = new Date(value)
411
+ let naive
412
+ if (!value) return undefined
413
+ value = String(value)
414
+ for (const regex of DATE_REGEX) {
415
+ const parsed = value.match(regex)
416
+ if (parsed) {
417
+ const { year, month, day } = parsed.groups
418
+ naive = new Date(year, Number.parseInt(month, 10) - 1, Number.parseInt(day, 10))
419
+ break
420
+ }
421
+ }
422
+ if (!naive) {
423
+ naive = new Date(value)
424
+ }
425
+ // Number.isNaN will always return false for invalid date
426
+ if (isNaN(naive)) return undefined
405
427
  // Let's pretend naive date are UTC, and remove time…
406
428
  return new Date(Date.UTC(naive.getFullYear(), naive.getMonth(), naive.getDate()))
407
429
  }
@@ -1,30 +1,3 @@
1
- L.Util.copyToClipboard = (textToCopy) => {
2
- // https://stackoverflow.com/a/65996386
3
- // Navigator clipboard api needs a secure context (https)
4
- if (navigator.clipboard && window.isSecureContext) {
5
- navigator.clipboard.writeText(textToCopy)
6
- } else {
7
- // Use the 'out of viewport hidden text area' trick
8
- const textArea = document.createElement('textarea')
9
- textArea.value = textToCopy
10
-
11
- // Move textarea out of the viewport so it's not visible
12
- textArea.style.position = 'absolute'
13
- textArea.style.left = '-999999px'
14
-
15
- document.body.prepend(textArea)
16
- textArea.select()
17
-
18
- try {
19
- document.execCommand('copy')
20
- } catch (error) {
21
- console.error(error)
22
- } finally {
23
- textArea.remove()
24
- }
25
- }
26
- }
27
-
28
1
  L.DomUtil.add = (tagName, className, container, content) => {
29
2
  const el = L.DomUtil.create(tagName, className, container)
30
3
  if (content) {
@@ -42,7 +15,7 @@ L.DomUtil.createFieldset = (container, legend, options) => {
42
15
  const details = L.DomUtil.create('details', options.className || '', container)
43
16
  const summary = L.DomUtil.add('summary', '', details)
44
17
  if (options.icon) L.DomUtil.createIcon(summary, options.icon)
45
- L.DomUtil.add('span', '', summary, legend)
18
+ L.DomUtil.add('h4', '', summary, legend)
46
19
  const fieldset = L.DomUtil.add('fieldset', '', details)
47
20
  details.open = options.on === true
48
21
  if (options.callback) {
@@ -94,24 +67,6 @@ L.DomUtil.createTitle = (parent, text, iconClassName, className = '', tag = 'h3'
94
67
  return title
95
68
  }
96
69
 
97
- L.DomUtil.createCopiableInput = (parent, label, value) => {
98
- const wrapper = L.DomUtil.add('div', 'copiable-input', parent)
99
- const labelEl = L.DomUtil.add('label', '', wrapper, label)
100
- const input = L.DomUtil.add('input', '', labelEl)
101
- input.type = 'text'
102
- input.readOnly = true
103
- input.value = value
104
- const button = L.DomUtil.createButtonIcon(
105
- wrapper,
106
- 'icon-copy',
107
- L._('copy'),
108
- () => L.Util.copyToClipboard(input.value),
109
- 24
110
- )
111
- button.type = 'button'
112
- return input
113
- }
114
-
115
70
  L.DomUtil.element = ({ tagName, parent, ...attrs }) => {
116
71
  const el = document.createElement(tagName)
117
72
  if (attrs.innerHTML) {
@@ -128,70 +83,6 @@ L.DomUtil.element = ({ tagName, parent, ...attrs }) => {
128
83
  return el
129
84
  }
130
85
 
131
- // From https://gist.github.com/Accudio/b9cb16e0e3df858cef0d31e38f1fe46f
132
- // convert colour in range 0-255 to the modifier used within luminance calculation
133
- L.DomUtil.colourMod = (colour) => {
134
- const sRGB = colour / 255
135
- let mod = ((sRGB + 0.055) / 1.055) ** 2.4
136
- if (sRGB < 0.03928) mod = sRGB / 12.92
137
- return mod
138
- }
139
- L.DomUtil.RGBRegex = /rgb *\( *([0-9]{1,3}) *, *([0-9]{1,3}) *, *([0-9]{1,3}) *\)/
140
- L.DomUtil.TextColorFromBackgroundColor = (el, bgcolor) => {
141
- return L.DomUtil.contrastedColor(el, bgcolor) ? '#ffffff' : '#000000'
142
- }
143
- L.DomUtil.contrastWCAG21 = (rgb) => {
144
- const [r, g, b] = rgb
145
- // luminance of inputted colour
146
- const lum =
147
- 0.2126 * L.DomUtil.colourMod(r) +
148
- 0.7152 * L.DomUtil.colourMod(g) +
149
- 0.0722 * L.DomUtil.colourMod(b)
150
- // white has a luminance of 1
151
- const whiteLum = 1
152
- const contrast = (whiteLum + 0.05) / (lum + 0.05)
153
- return contrast > 3 ? 1 : 0
154
- }
155
- L.DomUtil.colorNameToHex = (str) => {
156
- const ctx = document.createElement('canvas').getContext('2d')
157
- ctx.fillStyle = str
158
- return ctx.fillStyle
159
- }
160
- L.DomUtil.hexToRGB = (hex) => {
161
- return hex
162
- .replace(
163
- /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
164
- (m, r, g, b) => `#${r}${r}${g}${g}${b}${b}`
165
- )
166
- .substring(1)
167
- .match(/.{2}/g)
168
- .map((x) => Number.parseInt(x, 16))
169
- }
170
-
171
- const _CACHE_CONSTRAST = {}
172
- L.DomUtil.contrastedColor = (el, bgcolor) => {
173
- // Return 0 for black and 1 for white
174
- // bgcolor is a human color, it can be a any keyword (purple…)
175
- if (typeof _CACHE_CONSTRAST[bgcolor] !== 'undefined') return _CACHE_CONSTRAST[bgcolor]
176
- let rgb = window.getComputedStyle(el).getPropertyValue('background-color')
177
- rgb = L.DomUtil.RGBRegex.exec(rgb)
178
- if (rgb && rgb.length === 4) {
179
- rgb = [
180
- Number.parseInt(rgb[1], 10),
181
- Number.parseInt(rgb[2], 10),
182
- Number.parseInt(rgb[3], 10),
183
- ]
184
- } else {
185
- // The element may not yet be added to the DOM, so let's try
186
- // another way
187
- const hex = L.DomUtil.colorNameToHex(bgcolor)
188
- rgb = L.DomUtil.hexToRGB(hex)
189
- }
190
- if (!rgb) return 1
191
- const out = L.DomUtil.contrastWCAG21(rgb)
192
- if (bgcolor) _CACHE_CONSTRAST[bgcolor] = out
193
- return out
194
- }
195
86
  L.LatLng.prototype.isValid = function () {
196
87
  return (
197
88
  Number.isFinite(this.lat) &&
@@ -267,7 +267,6 @@ const locale = {
267
267
  "Permanent credits": "Permanent credits",
268
268
  "Please be sure the licence is compliant with your use.": "እባክዎ ፈቃዱ እስርዎ ሊጠቀሙ ካሉበት አገልግሎት ጋር መጣጣሙን ያረጋግጡ",
269
269
  "Please choose a format": "እባክዎ ፎርማት ይምረጡ",
270
- "Please enter the name of the property": "እባክዎ የባህርይውን ስም ያስገቡ",
271
270
  "Please save the map first": "Please save the map first",
272
271
  "Popup (large)": "Popup (large)",
273
272
  "Popup content style": "Popup content style",
@@ -397,20 +396,16 @@ const locale = {
397
396
  "Max": "Max",
398
397
  "From": "From",
399
398
  "Until": "Until",
400
- "Example: key1,key2|Label 2,key3|Label 3|checkbox": "Example: key1,key2|Label 2,key3|Label 3|checkbox",
401
399
  "Edit in OpenStreetMap": "Edit in OpenStreetMap",
402
400
  "Back to layers": "Back to layers",
403
401
  "Filters": "Filters",
404
402
  "Comma separated list of properties to use when filtering features by text input": "Comma separated list of properties to use when filtering features by text input",
405
- "Comma separated list of properties to use for filters (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control input field type, add it after another | (eg.: mykey|My Key|checkbox,otherkey|Other Key|datetime). Allowed values for the input field type are checkbox (default), radio, number, date and datetime.": "Comma separated list of properties to use for filters (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control input field type, add it after another | (eg.: mykey|My Key|checkbox,otherkey|Other Key|datetime). Allowed values for the input field type are checkbox (default), radio, number, date and datetime.",
406
403
  "Search keys": "Search keys",
407
- "Filters keys": "Filters keys",
408
404
  "Filter data": "Filter data",
409
405
  "Search map features…": "Search map features…",
410
406
  "Reset all": "Reset all",
411
407
  "Open browser": "Open browser",
412
408
  "Open caption": "Open caption",
413
- "Your map has been created with an anonymous account!": "Your map has been created with an anonymous account!",
414
409
  "Real-time collaboration": "Real-time collaboration",
415
410
  "Cannot parse data": "Cannot parse data",
416
411
  "Start typing...": "Start typing...",
@@ -466,10 +461,6 @@ const locale = {
466
461
  "Categories": "Categories",
467
462
  "Comma separated list of categories.": "Comma separated list of categories.",
468
463
  "Categories mode": "Categories mode",
469
- "Remove filter for this column": "Remove filter for this column",
470
- "Add filter for this column": "Add filter for this column",
471
- "Rename this column": "Rename this column",
472
- "Delete this column": "Delete this column",
473
464
  "Name “{name}” should not contain a dot.": "Name “{name}” should not contain a dot.",
474
465
  "This name already exists: “{name}”": "This name already exists: “{name}”",
475
466
  "Delete selected rows": "Delete selected rows",
@@ -571,11 +562,8 @@ const locale = {
571
562
  "Please choose an instance first.": "Please choose an instance first.",
572
563
  "No geo column found: must be either `lat(itude)` and `lon(gitude)` or `geom(etry)`.": "No geo column found: must be either `lat(itude)` and `lon(gitude)` or `geom(etry)`.",
573
564
  "Add a new field": "Add a new field",
574
- "Are you sure you want to delete this field on all the features?": "Are you sure you want to delete this field on all the features?",
575
- "Please enter the new name of this field": "Please enter the new name of this field",
576
565
  "Cannot process MultiPoint": "Cannot process MultiPoint",
577
566
  "Manage Fields": "Manage Fields",
578
- "Rename this field": "Rename this field",
579
567
  "Delete this field": "Delete this field",
580
568
  "Double click to edit the name": "Double click to edit the name",
581
569
  "Print map": "Print map",
@@ -595,7 +583,6 @@ const locale = {
595
583
  "Transform to regular line": "Transform to regular line",
596
584
  "Transform to route": "Transform to route",
597
585
  "Restore route": "Restore route",
598
- "Compute elevation": "Compute elevation",
599
586
  "Profile": "Profile",
600
587
  "Compute elevations": "Compute elevations",
601
588
  "Route preference": "Route preference",
@@ -615,7 +602,46 @@ const locale = {
615
602
  "Loss:": "Loss:",
616
603
  "Altitude:": "Altitude:",
617
604
  "Right-click to edit": "Right-click to edit",
618
- "Draw along routes": "Draw along routes"
605
+ "Draw along routes": "Draw along routes",
606
+ "Skipping invalid geometry": "Skipping invalid geometry",
607
+ "Add filter": "Add filter",
608
+ "Min/Max": "Min/Max",
609
+ "Multiple choices": "Multiple choices",
610
+ "Exclusive choice": "Exclusive choice",
611
+ "Yes/No": "Yes/No",
612
+ "Add a field prior to create a filter.": "Add a field prior to create a filter.",
613
+ "Edit this filter": "Edit this filter",
614
+ "Remove this filter": "Remove this filter",
615
+ "Apply filter to": "Apply filter to",
616
+ "Filter on": "Filter on",
617
+ "Human readable name of the filter": "Human readable name of the filter",
618
+ "Widget for the filter": "Widget for the filter",
619
+ "Edit filter": "Edit filter",
620
+ "Edit this field": "Edit this field",
621
+ "all layers": "all layers",
622
+ "single layer": "single layer",
623
+ "unset": "unset",
624
+ "Filters will be displayed in the data browser to ease data selection.": "Filters will be displayed in the data browser to ease data selection.",
625
+ "[key=\"value\"][key2=\"value2\"] (eg. [boundary=\"educational\"][name=\"Académie de Lyon\"])": "[key=\"value\"][key2=\"value2\"] (eg. [boundary=\"educational\"][name=\"Académie de Lyon\"])",
626
+ "Field Name": "Field Name",
627
+ "Field Type": "Field Type",
628
+ "Manage field": "Manage field",
629
+ "Add filter for this field": "Add filter for this field",
630
+ "Name cannot be empty.": "Name cannot be empty.",
631
+ "Are you sure you want to delete this field on all the data?": "Are you sure you want to delete this field on all the data?",
632
+ "Anonymous map": "Anonymous map",
633
+ "Edit filter for this field": "Edit filter for this field",
634
+ "Fields, filters and keys": "Fields, filters and keys",
635
+ "Hey, you created a map without an account!": "Hey, you created a map without an account!",
636
+ "This map is anonymous, do you want to attach it to your account?": "This map is anonymous, do you want to attach it to your account?",
637
+ "The map has been upgraded to latest version, please save it.": "The map has been upgraded to latest version, please save it.",
638
+ "Elevation has been added!": "Elevation has been added!",
639
+ "Mapillary": "Mapillary",
640
+ "Wikidata": "Wikidata",
641
+ "Wikipedia contributors": "Wikipedia contributors",
642
+ "See on Wikipedia": "See on Wikipedia",
643
+ "Anonymous map: update who can see and edit it": "Anonymous map: update who can see and edit it",
644
+ "Login": "Login"
619
645
  }
620
646
  L.registerLocale("am_ET", locale)
621
647
  L.setLocale("am_ET")