umap-project 2.6.3__py3-none-any.whl → 2.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (137) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +64 -1
  3. umap/asgi.py +15 -0
  4. umap/context_processors.py +1 -0
  5. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +19 -18
  9. umap/locale/en/LC_MESSAGES/django.po +47 -43
  10. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/es/LC_MESSAGES/django.po +134 -128
  12. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/fr/LC_MESSAGES/django.po +51 -47
  14. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/pt/LC_MESSAGES/django.po +64 -60
  16. umap/management/commands/clean_tilelayer.py +152 -0
  17. umap/management/commands/purge_purgatory.py +28 -0
  18. umap/models.py +27 -2
  19. umap/settings/base.py +3 -1
  20. umap/static/umap/base.css +4 -4
  21. umap/static/umap/css/contextmenu.css +6 -1
  22. umap/static/umap/css/icon.css +7 -2
  23. umap/static/umap/css/importers.css +4 -0
  24. umap/static/umap/img/16-white.svg +9 -2
  25. umap/static/umap/img/16.svg +1 -181
  26. umap/static/umap/img/24-white.svg +1 -0
  27. umap/static/umap/img/24.svg +1 -0
  28. umap/static/umap/img/importers/cadastrefr.svg +23 -0
  29. umap/static/umap/img/source/16-white.svg +10 -3
  30. umap/static/umap/img/source/16.svg +753 -197
  31. umap/static/umap/img/source/24-white.svg +3 -2
  32. umap/static/umap/img/source/24.svg +3 -2
  33. umap/static/umap/js/modules/autocomplete.js +7 -3
  34. umap/static/umap/js/modules/browser.js +54 -1
  35. umap/static/umap/js/modules/caption.js +16 -5
  36. umap/static/umap/js/modules/data/features.js +176 -2
  37. umap/static/umap/js/modules/data/layer.js +57 -40
  38. umap/static/umap/js/modules/formatter.js +3 -2
  39. umap/static/umap/js/modules/global.js +2 -0
  40. umap/static/umap/js/modules/importer.js +3 -0
  41. umap/static/umap/js/modules/importers/cadastrefr.js +62 -0
  42. umap/static/umap/js/modules/importers/communesfr.js +15 -3
  43. umap/static/umap/js/modules/permissions.js +123 -93
  44. umap/static/umap/js/modules/rendering/layers/classified.js +2 -0
  45. umap/static/umap/js/modules/rendering/ui.js +60 -213
  46. umap/static/umap/js/modules/share.js +1 -3
  47. umap/static/umap/js/modules/slideshow.js +1 -1
  48. umap/static/umap/js/modules/sync/engine.js +371 -14
  49. umap/static/umap/js/modules/sync/hlc.js +106 -0
  50. umap/static/umap/js/modules/sync/updaters.js +18 -6
  51. umap/static/umap/js/modules/sync/websocket.js +1 -1
  52. umap/static/umap/js/modules/tableeditor.js +1 -1
  53. umap/static/umap/js/modules/ui/base.js +2 -2
  54. umap/static/umap/js/modules/ui/contextmenu.js +51 -18
  55. umap/static/umap/js/modules/urls.js +5 -1
  56. umap/static/umap/js/modules/utils.js +28 -4
  57. umap/static/umap/js/umap.controls.js +73 -52
  58. umap/static/umap/js/umap.core.js +3 -3
  59. umap/static/umap/js/umap.forms.js +3 -1
  60. umap/static/umap/js/umap.js +115 -124
  61. umap/static/umap/locale/br.js +13 -4
  62. umap/static/umap/locale/br.json +13 -4
  63. umap/static/umap/locale/ca.js +28 -15
  64. umap/static/umap/locale/ca.json +28 -15
  65. umap/static/umap/locale/cs_CZ.js +87 -78
  66. umap/static/umap/locale/cs_CZ.json +87 -78
  67. umap/static/umap/locale/de.js +17 -8
  68. umap/static/umap/locale/de.json +17 -8
  69. umap/static/umap/locale/en.js +13 -2
  70. umap/static/umap/locale/en.json +13 -2
  71. umap/static/umap/locale/es.js +330 -319
  72. umap/static/umap/locale/es.json +330 -319
  73. umap/static/umap/locale/eu.js +10 -3
  74. umap/static/umap/locale/eu.json +10 -3
  75. umap/static/umap/locale/fa_IR.js +11 -4
  76. umap/static/umap/locale/fa_IR.json +11 -4
  77. umap/static/umap/locale/fr.js +15 -4
  78. umap/static/umap/locale/fr.json +15 -4
  79. umap/static/umap/locale/hu.js +10 -3
  80. umap/static/umap/locale/hu.json +10 -3
  81. umap/static/umap/locale/pt.js +17 -8
  82. umap/static/umap/locale/pt.json +17 -8
  83. umap/static/umap/locale/pt_PT.js +13 -4
  84. umap/static/umap/locale/pt_PT.json +13 -4
  85. umap/static/umap/locale/zh_TW.js +13 -4
  86. umap/static/umap/locale/zh_TW.json +13 -4
  87. umap/static/umap/map.css +44 -29
  88. umap/static/umap/unittests/hlc.js +165 -0
  89. umap/static/umap/unittests/sync.js +321 -15
  90. umap/static/umap/unittests/utils.js +47 -0
  91. umap/static/umap/vars.css +2 -1
  92. umap/static/umap/vendors/colorbrewer/colorbrewer.js +309 -317
  93. umap/static/umap/vendors/dompurify/purify.es.js +15 -16
  94. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  95. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  96. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +2 -2
  97. umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +1 -1
  98. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js +1 -1
  99. umap/static/umap/vendors/simple-statistics/simple-statistics.min.js.map +1 -1
  100. umap/templates/umap/css.html +0 -2
  101. umap/templates/umap/dashboard_menu.html +4 -2
  102. umap/templates/umap/js.html +0 -5
  103. umap/templates/umap/map_detail.html +2 -2
  104. umap/tests/fixtures/test_upload_data.csv +2 -2
  105. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  106. umap/tests/integration/test_basics.py +1 -1
  107. umap/tests/integration/test_browser.py +69 -7
  108. umap/tests/integration/test_caption.py +3 -3
  109. umap/tests/integration/test_circles_layer.py +12 -0
  110. umap/tests/integration/test_datalayer.py +2 -1
  111. umap/tests/integration/test_draw_polygon.py +17 -9
  112. umap/tests/integration/test_draw_polyline.py +12 -8
  113. umap/tests/integration/test_edit_datalayer.py +5 -8
  114. umap/tests/integration/test_edit_map.py +2 -2
  115. umap/tests/integration/test_edit_marker.py +1 -1
  116. umap/tests/integration/test_facets_browser.py +3 -3
  117. umap/tests/integration/test_import.py +1 -0
  118. umap/tests/integration/test_map.py +1 -0
  119. umap/tests/integration/test_owned_map.py +1 -1
  120. umap/tests/integration/test_view_marker.py +63 -0
  121. umap/tests/integration/test_view_polygon.py +12 -12
  122. umap/tests/integration/test_websocket_sync.py +65 -3
  123. umap/tests/test_clean_tilelayer.py +83 -0
  124. umap/tests/test_datalayer.py +24 -0
  125. umap/tests/test_map_views.py +20 -0
  126. umap/tests/test_purge_purgatory.py +25 -0
  127. umap/tests/test_websocket_server.py +22 -0
  128. umap/urls.py +5 -1
  129. umap/views.py +6 -3
  130. umap/websocket_server.py +130 -27
  131. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/METADATA +18 -14
  132. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/RECORD +135 -127
  133. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  134. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  135. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/WHEEL +0 -0
  136. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/entry_points.txt +0 -0
  137. {umap_project-2.6.3.dist-info → umap_project-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- /* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */
1
+ /* Uses globals for: csv2geojson, osmtogeojson (not available as ESM) */
2
2
  import { translate } from './i18n.js'
3
3
 
4
4
  export const EXPORT_FORMATS = {
@@ -115,7 +115,8 @@ export class Formatter {
115
115
  }
116
116
 
117
117
  async fromGeoRSS(str) {
118
- return GeoRSSToGeoJSON(this.toDom(str))
118
+ const GeoRSSToGeoJSON = await import('../../vendors/georsstogeojson/GeoRSSToGeoJSON.js')
119
+ return GeoRSSToGeoJSON.parse(this.toDom(str))
119
120
  }
120
121
 
121
122
  toDom(x) {
@@ -9,6 +9,7 @@ import {
9
9
  } from './autocomplete.js'
10
10
  import Browser from './browser.js'
11
11
  import Caption from './caption.js'
12
+ import ContextMenu from './ui/contextmenu.js'
12
13
  import Facets from './facets.js'
13
14
  import { Formatter } from './formatter.js'
14
15
  import Help from './help.js'
@@ -43,6 +44,7 @@ window.U = {
43
44
  AutocompleteDatalist,
44
45
  Browser,
45
46
  Caption,
47
+ ContextMenu,
46
48
  DataLayer,
47
49
  DataLayerPermissions,
48
50
  Dialog,
@@ -69,6 +69,9 @@ export default class Importer {
69
69
  case 'communesfr':
70
70
  import('./importers/communesfr.js').then(register)
71
71
  break
72
+ case 'cadastrefr':
73
+ import('./importers/cadastrefr.js').then(register)
74
+ break
72
75
  case 'overpass':
73
76
  import('./importers/overpass.js').then(register)
74
77
  break
@@ -0,0 +1,62 @@
1
+ import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
+ import { BaseAjax, SingleMixin } from '../autocomplete.js'
3
+ import * as Util from '../utils.js'
4
+ import { AutocompleteCommunes } from './communesfr.js'
5
+
6
+ const TEMPLATE = `
7
+ <h3>Cadastre</h3>
8
+ <p>Importer les données cadastrales d’une commune française.</p>
9
+ <select name="theme">
10
+ <option value="batiments">Bâtiments</option>
11
+ <option value="communes">Communes</option>
12
+ <option value="feuilles">Feuilles</option>
13
+ <option value="lieux_dits">Lieux dits</option>
14
+ <option value="parcelles" selected>Parcelles</option>
15
+ <option value="prefixes_sections">Préfixes sections</option>
16
+ <option value="sections">Sections</option>
17
+ <option value="subdivisions_fiscales">Subdivisions fiscales</option>
18
+ </select>
19
+ <label id="boundary">
20
+ </label>
21
+ `
22
+
23
+ export class Importer {
24
+ constructor(map, options) {
25
+ this.name = options.name || 'Cadastre'
26
+ this.id = 'cadastrefr'
27
+ }
28
+
29
+ async open(importer) {
30
+ let boundary = null
31
+ let boundaryName = null
32
+ const container = DomUtil.create('div')
33
+ container.innerHTML = TEMPLATE
34
+ const select = container.querySelector('select')
35
+ const options = {
36
+ placeholder: 'Nom ou code INSEE…',
37
+ url: 'https://geo.api.gouv.fr/communes?nom={q}&limit=5',
38
+ on_select: (choice) => {
39
+ boundary = choice.item.value
40
+ boundaryName = choice.item.label
41
+ },
42
+ }
43
+ this.autocomplete = new AutocompleteCommunes(container, options)
44
+
45
+ const confirm = (form) => {
46
+ if (!boundary || !form.theme) {
47
+ Alert.error(translate('Please choose a theme and a boundary first.'))
48
+ return
49
+ }
50
+ importer.url = `https://cadastre.data.gouv.fr/bundler/cadastre-etalab/communes/${boundary}/geojson/${form.theme}`
51
+ importer.format = 'geojson'
52
+ importer.layerName = `${boundaryName} — ${select.options[select.selectedIndex].textContent}`
53
+ }
54
+
55
+ importer.dialog
56
+ .open({
57
+ template: container,
58
+ className: `${this.id} importer dark`,
59
+ })
60
+ .then(confirm)
61
+ }
62
+ }
@@ -1,13 +1,25 @@
1
1
  import { DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
2
  import { BaseAjax, SingleMixin } from '../autocomplete.js'
3
+ import * as Util from '../utils.js'
3
4
 
4
- class Autocomplete extends SingleMixin(BaseAjax) {
5
+ export class AutocompleteCommunes extends SingleMixin(BaseAjax) {
5
6
  createResult(item) {
6
7
  return super.createResult({
7
8
  value: item.code,
8
9
  label: `${item.nom} (${item.code})`,
9
10
  })
10
11
  }
12
+
13
+ buildUrl(value) {
14
+ let url = this.url
15
+ let options = { q: encodeURIComponent(value) }
16
+ const re = /^(0[1-9]|[1-9][ABab\d])\d{3}$/gm
17
+ if (re.test(value)) {
18
+ url = "https://geo.api.gouv.fr/communes?code={code}&limit=5"
19
+ options = { code: encodeURIComponent(value) }
20
+ }
21
+ return Util.template(url, options)
22
+ }
11
23
  }
12
24
 
13
25
  export class Importer {
@@ -25,7 +37,7 @@ export class Importer {
25
37
  textContent: "Importer les contours d'une commune française.",
26
38
  })
27
39
  const options = {
28
- placeholder: 'Commune…',
40
+ placeholder: 'Nom ou code INSEE…',
29
41
  url: 'https://geo.api.gouv.fr/communes?nom={q}&limit=5',
30
42
  on_select: (choice) => {
31
43
  importer.url = `https://geo.api.gouv.fr/communes?code=${choice.item.value}&format=geojson&geometry=contour`
@@ -34,7 +46,7 @@ export class Importer {
34
46
  importer.dialog.close()
35
47
  },
36
48
  }
37
- this.autocomplete = new Autocomplete(container, options)
49
+ this.autocomplete = new AutocompleteCommunes(container, options)
38
50
 
39
51
  importer.dialog.open({
40
52
  template: container,
@@ -42,101 +42,129 @@ export class MapPermissions {
42
42
  return !this.map.options.permissions.owner
43
43
  }
44
44
 
45
- getMap() {
46
- return this.map
47
- }
48
-
49
- edit() {
50
- if (this.map.options.editMode !== 'advanced') return
51
- if (!this.map.options.umap_id) {
52
- return Alert.info(translate('Please save the map first'))
53
- }
54
- const container = DomUtil.create('div', 'permissions-panel')
45
+ _editAnonymous(container) {
55
46
  const fields = []
56
- DomUtil.createTitle(container, translate('Update permissions'), 'icon-key')
57
- if (this.isAnonymousMap()) {
47
+ if (this.isOwner()) {
48
+ fields.push([
49
+ 'options.edit_status',
50
+ {
51
+ handler: 'IntSelect',
52
+ label: translate('Who can edit'),
53
+ selectOptions: this.map.options.edit_statuses,
54
+ },
55
+ ])
56
+ const builder = new U.FormBuilder(this, fields)
57
+ const form = builder.build()
58
+ container.appendChild(form)
59
+
58
60
  if (this.options.anonymous_edit_url) {
59
- const helpText = `${translate('Secret edit link:')}<br>${
61
+ DomUtil.createCopiableInput(
62
+ container,
63
+ translate('Secret edit link:'),
60
64
  this.options.anonymous_edit_url
61
- }`
62
- DomUtil.element({
63
- tagName: 'p',
64
- className: 'help-text',
65
- innerHTML: helpText,
66
- parent: container,
67
- })
68
- fields.push([
69
- 'options.edit_status',
70
- {
71
- handler: 'IntSelect',
72
- label: translate('Who can edit'),
73
- selectOptions: this.map.options.edit_statuses,
74
- helpText: helpText,
75
- },
76
- ])
65
+ )
77
66
  }
78
- } else {
79
- if (this.isOwner()) {
80
- fields.push([
81
- 'options.edit_status',
82
- {
83
- handler: 'IntSelect',
84
- label: translate('Who can edit'),
85
- selectOptions: this.map.options.edit_statuses,
86
- },
87
- ])
88
- fields.push([
89
- 'options.share_status',
67
+
68
+ if (this.map.options.user?.id) {
69
+ // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
70
+ // Note: real check is made on the back office anyway.
71
+ const advancedActions = DomUtil.createFieldset(
72
+ container,
73
+ translate('Advanced actions')
74
+ )
75
+ const advancedButtons = DomUtil.create('div', 'button-bar', advancedActions)
76
+ DomUtil.createButton(
77
+ 'button',
78
+ advancedButtons,
79
+ translate('Attach the map to my account'),
80
+ this.attach,
81
+ this
82
+ )
83
+ }
84
+ }
85
+ }
86
+
87
+ _editWithOwner(container) {
88
+ const topFields = []
89
+ const collaboratorsFields = []
90
+ const fieldset = Utils.loadTemplate(
91
+ `<fieldset class="separator"><legend>${translate('Map')}</legend></fieldset>`
92
+ )
93
+ container.appendChild(fieldset)
94
+ if (this.isOwner()) {
95
+ topFields.push([
96
+ 'options.edit_status',
97
+ {
98
+ handler: 'IntSelect',
99
+ label: translate('Who can edit'),
100
+ selectOptions: this.map.options.edit_statuses,
101
+ },
102
+ ])
103
+ topFields.push([
104
+ 'options.share_status',
105
+ {
106
+ handler: 'IntSelect',
107
+ label: translate('Who can view'),
108
+ selectOptions: this.map.options.share_statuses,
109
+ },
110
+ ])
111
+ collaboratorsFields.push([
112
+ 'options.owner',
113
+ { handler: 'ManageOwner', label: translate("Map's owner") },
114
+ ])
115
+ if (this.map.options.user?.teams?.length) {
116
+ collaboratorsFields.push([
117
+ 'options.team',
90
118
  {
91
- handler: 'IntSelect',
92
- label: translate('Who can view'),
93
- selectOptions: this.map.options.share_statuses,
119
+ handler: 'ManageTeam',
120
+ label: translate('Attach map to a team'),
121
+ teams: this.map.options.user.teams,
94
122
  },
95
123
  ])
96
- fields.push([
97
- 'options.owner',
98
- { handler: 'ManageOwner', label: translate("Map's owner") },
99
- ])
100
- if (this.map.options.user?.teams?.length) {
101
- fields.push([
102
- 'options.team',
103
- {
104
- handler: 'ManageTeam',
105
- label: translate('Attach map to a team'),
106
- teams: this.map.options.user.teams,
107
- },
108
- ])
109
- }
110
124
  }
111
- fields.push([
112
- 'options.editors',
113
- { handler: 'ManageEditors', label: translate("Map's editors") },
114
- ])
115
125
  }
126
+ collaboratorsFields.push([
127
+ 'options.editors',
128
+ { handler: 'ManageEditors', label: translate("Map's editors") },
129
+ ])
116
130
 
117
- const builder = new U.FormBuilder(this, fields)
131
+ const builder = new U.FormBuilder(this, topFields)
118
132
  const form = builder.build()
119
133
  container.appendChild(form)
120
- if (this.isAnonymousMap() && this.map.options.user) {
121
- // We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
122
- // Note: real check is made on the back office anyway.
123
- const advancedActions = DomUtil.createFieldset(
124
- container,
125
- translate('Advanced actions')
134
+ if (collaboratorsFields.length) {
135
+ const fieldset = Utils.loadTemplate(
136
+ `<fieldset class="separator"><legend>${translate('Manage collaborators')}</legend></fieldset>`
126
137
  )
127
- const advancedButtons = DomUtil.create('div', 'button-bar', advancedActions)
128
- DomUtil.createButton(
129
- 'button',
130
- advancedButtons,
131
- translate('Attach the map to my account'),
132
- this.attach,
133
- this
138
+ container.appendChild(fieldset)
139
+ const builder = new U.FormBuilder(this, collaboratorsFields)
140
+ const form = builder.build()
141
+ container.appendChild(form)
142
+ }
143
+ }
144
+
145
+ _editDatalayers(container) {
146
+ if (this.map.hasLayers()) {
147
+ const fieldset = Utils.loadTemplate(
148
+ `<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
134
149
  )
150
+ container.appendChild(fieldset)
151
+ this.map.eachDataLayer((datalayer) => {
152
+ datalayer.permissions.edit(fieldset)
153
+ })
135
154
  }
136
- DomUtil.add('h4', '', container, translate('Datalayers'))
137
- this.map.eachDataLayer((datalayer) => {
138
- datalayer.permissions.edit(container)
139
- })
155
+ }
156
+
157
+ edit() {
158
+ if (this.map.options.editMode !== 'advanced') return
159
+ if (!this.map.options.umap_id) {
160
+ Alert.info(translate('Please save the map first'))
161
+ return
162
+ }
163
+ const container = DomUtil.create('div', 'permissions-panel')
164
+ DomUtil.createTitle(container, translate('Update permissions'), 'icon-key')
165
+ if (this.isAnonymousMap()) this._editAnonymous(container)
166
+ else this._editWithOwner(container)
167
+ this._editDatalayers(container)
140
168
  this.map.editPanel.open({ content: container, className: 'dark' })
141
169
  }
142
170
 
@@ -150,15 +178,16 @@ export class MapPermissions {
150
178
  }
151
179
 
152
180
  async save() {
153
- if (!this.isDirty) return this.map.continueSaving()
181
+ if (!this.isDirty) return
154
182
  const formData = new FormData()
155
183
  if (!this.isAnonymousMap() && this.options.editors) {
156
184
  const editors = this.options.editors.map((u) => u.id)
157
185
  for (let i = 0; i < this.options.editors.length; i++)
158
186
  formData.append('editors', this.options.editors[i].id)
159
187
  }
160
- if (this.isOwner() || this.isAnonymousMap())
188
+ if (this.isOwner() || this.isAnonymousMap()) {
161
189
  formData.append('edit_status', this.options.edit_status)
190
+ }
162
191
  if (this.isOwner()) {
163
192
  formData.append('owner', this.options.owner?.id)
164
193
  formData.append('team', this.options.team?.id || '')
@@ -172,7 +201,6 @@ export class MapPermissions {
172
201
  if (!error) {
173
202
  this.commit()
174
203
  this.isDirty = false
175
- this.map.continueSaving()
176
204
  this.map.fire('postsync')
177
205
  }
178
206
  }
@@ -197,9 +225,11 @@ export class MapPermissions {
197
225
  }
198
226
 
199
227
  getShareStatusDisplay() {
200
- return Object.fromEntries(this.map.options.share_statuses)[
201
- this.options.share_status
202
- ]
228
+ if (this.map.options.share_statuses) {
229
+ return Object.fromEntries(this.map.options.share_statuses)[
230
+ this.options.share_status
231
+ ]
232
+ }
203
233
  }
204
234
  }
205
235
 
@@ -225,7 +255,7 @@ export class DataLayerPermissions {
225
255
  return this._isDirty
226
256
  }
227
257
 
228
- getMap() {
258
+ get map() {
229
259
  return this.datalayer.map
230
260
  }
231
261
 
@@ -238,7 +268,7 @@ export class DataLayerPermissions {
238
268
  label: translate('Who can edit "{layer}"', {
239
269
  layer: this.datalayer.getName(),
240
270
  }),
241
- selectOptions: this.datalayer.map.options.datalayer_edit_statuses,
271
+ selectOptions: this.map.options.datalayer_edit_statuses,
242
272
  },
243
273
  ],
244
274
  ]
@@ -250,16 +280,17 @@ export class DataLayerPermissions {
250
280
  }
251
281
 
252
282
  getUrl() {
253
- return Utils.template(this.datalayer.map.options.urls.datalayer_permissions, {
254
- map_id: this.datalayer.map.options.umap_id,
283
+ return this.map.urls.get('datalayer_permissions', {
284
+ map_id: this.map.options.umap_id,
255
285
  pk: this.datalayer.umap_id,
256
286
  })
257
287
  }
288
+
258
289
  async save() {
259
- if (!this.isDirty) return this.datalayer.map.continueSaving()
290
+ if (!this.isDirty) return
260
291
  const formData = new FormData()
261
292
  formData.append('edit_status', this.options.edit_status)
262
- const [data, response, error] = await this.datalayer.map.server.post(
293
+ const [data, response, error] = await this.map.server.post(
263
294
  this.getUrl(),
264
295
  {},
265
296
  formData
@@ -267,7 +298,6 @@ export class DataLayerPermissions {
267
298
  if (!error) {
268
299
  this.commit()
269
300
  this.isDirty = false
270
- this.datalayer.map.continueSaving()
271
301
  }
272
302
  }
273
303
 
@@ -3,6 +3,7 @@ import { translate } from '../../i18n.js'
3
3
  import { LayerMixin } from './base.js'
4
4
  import * as Utils from '../../utils.js'
5
5
  import { CircleMarker } from '../ui.js'
6
+ import colorbrewer from '../../../../vendors/colorbrewer/colorbrewer.js'
6
7
 
7
8
  // Layer where each feature color is relative to the others,
8
9
  // so we need all features before behing able to set one
@@ -74,6 +75,7 @@ const ClassifiedMixin = {
74
75
  },
75
76
 
76
77
  renderLegend: function (container) {
78
+ if (!this.datalayer.hasDataLoaded()) return
77
79
  const parent = DomUtil.create('ul', '', container)
78
80
  const items = this.getLegendItems()
79
81
  for (const [color, label] of items) {