umap-project 2.6.3__py3-none-any.whl → 2.7.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 (104) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +64 -1
  3. umap/context_processors.py +1 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/cs_CZ/LC_MESSAGES/django.po +96 -92
  6. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/de/LC_MESSAGES/django.po +19 -18
  8. umap/locale/en/LC_MESSAGES/django.po +47 -43
  9. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/fr/LC_MESSAGES/django.po +51 -47
  11. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/pt/LC_MESSAGES/django.po +64 -60
  13. umap/management/commands/clean_tilelayer.py +152 -0
  14. umap/management/commands/purge_purgatory.py +28 -0
  15. umap/models.py +27 -2
  16. umap/settings/base.py +2 -0
  17. umap/static/umap/base.css +4 -4
  18. umap/static/umap/css/contextmenu.css +5 -0
  19. umap/static/umap/css/icon.css +7 -2
  20. umap/static/umap/img/16-white.svg +9 -2
  21. umap/static/umap/img/16.svg +3 -0
  22. umap/static/umap/img/source/16-white.svg +10 -3
  23. umap/static/umap/img/source/16.svg +4 -1
  24. umap/static/umap/js/modules/autocomplete.js +7 -3
  25. umap/static/umap/js/modules/browser.js +7 -1
  26. umap/static/umap/js/modules/caption.js +6 -1
  27. umap/static/umap/js/modules/data/features.js +176 -2
  28. umap/static/umap/js/modules/data/layer.js +31 -26
  29. umap/static/umap/js/modules/formatter.js +3 -2
  30. umap/static/umap/js/modules/global.js +2 -0
  31. umap/static/umap/js/modules/importers/communesfr.js +13 -1
  32. umap/static/umap/js/modules/permissions.js +123 -93
  33. umap/static/umap/js/modules/rendering/ui.js +37 -212
  34. umap/static/umap/js/modules/sync/engine.js +365 -14
  35. umap/static/umap/js/modules/sync/hlc.js +106 -0
  36. umap/static/umap/js/modules/sync/updaters.js +4 -4
  37. umap/static/umap/js/modules/sync/websocket.js +1 -1
  38. umap/static/umap/js/modules/ui/base.js +2 -2
  39. umap/static/umap/js/modules/ui/contextmenu.js +34 -17
  40. umap/static/umap/js/modules/urls.js +5 -1
  41. umap/static/umap/js/modules/utils.js +5 -1
  42. umap/static/umap/js/umap.controls.js +47 -47
  43. umap/static/umap/js/umap.core.js +3 -3
  44. umap/static/umap/js/umap.forms.js +3 -1
  45. umap/static/umap/js/umap.js +95 -112
  46. umap/static/umap/locale/br.js +13 -4
  47. umap/static/umap/locale/br.json +13 -4
  48. umap/static/umap/locale/ca.js +21 -12
  49. umap/static/umap/locale/ca.json +21 -12
  50. umap/static/umap/locale/cs_CZ.js +87 -78
  51. umap/static/umap/locale/cs_CZ.json +87 -78
  52. umap/static/umap/locale/de.js +17 -8
  53. umap/static/umap/locale/de.json +17 -8
  54. umap/static/umap/locale/en.js +9 -2
  55. umap/static/umap/locale/en.json +9 -2
  56. umap/static/umap/locale/eu.js +10 -3
  57. umap/static/umap/locale/eu.json +10 -3
  58. umap/static/umap/locale/fa_IR.js +11 -4
  59. umap/static/umap/locale/fa_IR.json +11 -4
  60. umap/static/umap/locale/fr.js +11 -4
  61. umap/static/umap/locale/fr.json +11 -4
  62. umap/static/umap/locale/hu.js +10 -3
  63. umap/static/umap/locale/hu.json +10 -3
  64. umap/static/umap/locale/pt.js +17 -8
  65. umap/static/umap/locale/pt.json +17 -8
  66. umap/static/umap/locale/pt_PT.js +13 -4
  67. umap/static/umap/locale/pt_PT.json +13 -4
  68. umap/static/umap/locale/zh_TW.js +13 -4
  69. umap/static/umap/locale/zh_TW.json +13 -4
  70. umap/static/umap/map.css +7 -22
  71. umap/static/umap/unittests/hlc.js +158 -0
  72. umap/static/umap/unittests/sync.js +321 -15
  73. umap/static/umap/unittests/utils.js +23 -0
  74. umap/static/umap/vendors/georsstogeojson/GeoRSSToGeoJSON.js +111 -80
  75. umap/templates/umap/dashboard_menu.html +4 -2
  76. umap/templates/umap/js.html +0 -4
  77. umap/tests/integration/test_anonymous_owned_map.py +1 -0
  78. umap/tests/integration/test_basics.py +1 -1
  79. umap/tests/integration/test_circles_layer.py +12 -0
  80. umap/tests/integration/test_datalayer.py +5 -0
  81. umap/tests/integration/test_draw_polygon.py +17 -9
  82. umap/tests/integration/test_draw_polyline.py +12 -8
  83. umap/tests/integration/test_edit_datalayer.py +4 -6
  84. umap/tests/integration/test_edit_map.py +1 -1
  85. umap/tests/integration/test_import.py +5 -0
  86. umap/tests/integration/test_map.py +5 -0
  87. umap/tests/integration/test_owned_map.py +1 -1
  88. umap/tests/integration/test_view_polygon.py +12 -12
  89. umap/tests/integration/test_websocket_sync.py +65 -3
  90. umap/tests/test_clean_tilelayer.py +83 -0
  91. umap/tests/test_datalayer.py +24 -0
  92. umap/tests/test_map_views.py +1 -0
  93. umap/tests/test_purge_purgatory.py +25 -0
  94. umap/tests/test_websocket_server.py +22 -0
  95. umap/urls.py +5 -1
  96. umap/views.py +6 -3
  97. umap/websocket_server.py +130 -27
  98. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/METADATA +9 -9
  99. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/RECORD +102 -97
  100. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.css +0 -1
  101. umap/static/umap/vendors/contextmenu/leaflet.contextmenu.min.js +0 -7
  102. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/WHEEL +0 -0
  103. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/entry_points.txt +0 -0
  104. {umap_project-2.6.3.dist-info → umap_project-2.7.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -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
 
@@ -9,6 +9,7 @@ import {
9
9
  latLng,
10
10
  LatLng,
11
11
  LatLngBounds,
12
+ DomEvent,
12
13
  } from '../../../vendors/leaflet/leaflet-src.esm.js'
13
14
  import { translate } from '../i18n.js'
14
15
  import { uMapAlert as Alert } from '../../components/alerts/alert.js'
@@ -36,7 +37,7 @@ const FeatureMixin = {
36
37
  },
37
38
 
38
39
  addInteractions: function () {
39
- this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu)
40
+ this.on('contextmenu editable:vertex:contextmenu', this.onContextMenu)
40
41
  this.on('click', this.onClick)
41
42
  },
42
43
 
@@ -61,7 +62,7 @@ const FeatureMixin = {
61
62
  }).addTo(this._map, this.feature, event.latlng)
62
63
  }
63
64
  }
64
- L.DomEvent.stop(event)
65
+ DomEvent.stop(event)
65
66
  },
66
67
 
67
68
  resetTooltip: function () {
@@ -83,67 +84,14 @@ const FeatureMixin = {
83
84
  }
84
85
  },
85
86
 
86
- _showContextMenu: function (event) {
87
- L.DomEvent.stop(event)
88
- const pt = this._map.mouseEventToContainerPoint(event.originalEvent)
89
- event.relatedTarget = this
90
- this._map.contextmenu.showAt(pt, event)
91
- },
92
-
93
- getContextMenuItems: function (event) {
94
- const permalink = this.feature.getPermalink()
95
- let items = []
96
- if (permalink) {
97
- items.push({
98
- text: translate('Permalink'),
99
- callback: () => {
100
- window.open(permalink)
101
- },
102
- })
103
- }
104
- items.push({
105
- text: translate('Copy as GeoJSON'),
106
- callback: () => {
107
- L.Util.copyToClipboard(JSON.stringify(this.feature.toGeoJSON()))
108
- this._map.tooltip.open({ content: L._('✅ Copied!') })
109
- },
110
- })
111
- if (this._map.editEnabled && !this.feature.isReadOnly()) {
112
- items = items.concat(this.getContextMenuEditItems(event))
113
- }
114
- return items
115
- },
116
-
117
- getContextMenuEditItems: function () {
118
- let items = ['-']
119
- if (this._map.editedFeature !== this) {
120
- items.push({
121
- text: `${translate('Edit this feature')} (⇧+Click)`,
122
- callback: this.feature.edit,
123
- context: this.feature,
124
- iconCls: 'umap-edit',
125
- })
126
- }
127
- items = items.concat(
128
- {
129
- text: this._map.help.displayLabel('EDIT_FEATURE_LAYER'),
130
- callback: this.feature.datalayer.edit,
131
- context: this.feature.datalayer,
132
- iconCls: 'umap-edit',
133
- },
134
- {
135
- text: translate('Delete this feature'),
136
- callback: this.feature.confirmDelete,
137
- context: this.feature,
138
- iconCls: 'umap-delete',
139
- },
140
- {
141
- text: translate('Clone this feature'),
142
- callback: this.feature.clone,
143
- context: this.feature,
144
- }
87
+ onContextMenu: function (event) {
88
+ DomEvent.stop(event)
89
+ const items = this._map.getContextMenuItems(event)
90
+ items.push('-', ...this.feature.getContextMenuItems(event))
91
+ this._map.contextmenu.open(
92
+ [event.originalEvent.clientX, event.originalEvent.clientY],
93
+ items
145
94
  )
146
- return items
147
95
  },
148
96
 
149
97
  onCommit: function () {
@@ -158,27 +106,6 @@ const PointMixin = {
158
106
  isOnScreen: function (bounds) {
159
107
  return bounds.contains(this.getCenter())
160
108
  },
161
- }
162
-
163
- export const LeafletMarker = Marker.extend({
164
- parentClass: Marker,
165
- includes: [FeatureMixin, PointMixin],
166
-
167
- initialize: function (feature, latlng) {
168
- FeatureMixin.initialize.call(this, feature, latlng)
169
- this.setIcon(this.getIcon())
170
- },
171
-
172
- getClass: () => LeafletMarker,
173
-
174
- // Make API consistent with path
175
- getLatLngs: function () {
176
- return this.getLatLng()
177
- },
178
-
179
- setLatLngs: function (latlng) {
180
- return this.setLatLng(latlng)
181
- },
182
109
 
183
110
  addInteractions() {
184
111
  FeatureMixin.addInteractions.call(this)
@@ -190,9 +117,6 @@ export const LeafletMarker = Marker.extend({
190
117
  this.on('editable:drawing:commit', this.onCommit)
191
118
  if (!this.feature.isReadOnly()) this.on('mouseover', this._enableDragging)
192
119
  this.on('mouseout', this._onMouseOut)
193
- this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
194
- this.on('popupopen', this.highlight)
195
- this.on('popupclose', this.resetHighlight)
196
120
  },
197
121
 
198
122
  _onMouseOut: function () {
@@ -222,6 +146,29 @@ export const LeafletMarker = Marker.extend({
222
146
  this.disableEdit()
223
147
  }
224
148
  },
149
+ }
150
+
151
+ export const LeafletMarker = Marker.extend({
152
+ parentClass: Marker,
153
+ includes: [FeatureMixin, PointMixin],
154
+
155
+ initialize: function (feature, latlng) {
156
+ FeatureMixin.initialize.call(this, feature, latlng)
157
+ this.setIcon(this.getIcon())
158
+ },
159
+
160
+ getClass: () => LeafletMarker,
161
+
162
+ setLatLngs: function (latlng) {
163
+ return this.setLatLng(latlng)
164
+ },
165
+
166
+ addInteractions() {
167
+ PointMixin.addInteractions.call(this)
168
+ this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
169
+ this.on('popupopen', this.highlight)
170
+ this.on('popupclose', this.resetHighlight)
171
+ },
225
172
 
226
173
  _initIcon: function () {
227
174
  this.options.icon = this.getIcon()
@@ -344,6 +291,10 @@ const PathMixin = {
344
291
  }
345
292
  options.pointerEvents = options.interactive ? 'visiblePainted' : 'stroke'
346
293
  this.parentClass.prototype.setStyle.call(this, options)
294
+ // TODO remove me when this gets merged and released:
295
+ // https://github.com/Leaflet/Leaflet/pull/9475
296
+
297
+ this._path.classList.toggle('leaflet-interactive', options.interactive)
347
298
  },
348
299
 
349
300
  _redraw: function () {
@@ -360,65 +311,6 @@ const PathMixin = {
360
311
  }).addTo(this._map, this, event.latlng, event.vertex)
361
312
  },
362
313
 
363
- getContextMenuItems: function (event) {
364
- let items = FeatureMixin.getContextMenuItems.call(this, event)
365
- items.push({
366
- text: translate('Display measure'),
367
- callback: () => Alert.info(this.getMeasure()),
368
- })
369
- if (this._map.editEnabled && !this.feature.isReadOnly() && this.feature.isMulti()) {
370
- items = items.concat(this.getContextMenuMultiItems(event))
371
- }
372
- return items
373
- },
374
-
375
- getContextMenuMultiItems: function (event) {
376
- const items = [
377
- '-',
378
- {
379
- text: translate('Remove shape from the multi'),
380
- callback: () => {
381
- this.enableEdit().deleteShapeAt(event.latlng)
382
- },
383
- },
384
- ]
385
- const shape = this.shapeAt(event.latlng)
386
- if (this._latlngs.indexOf(shape) > 0) {
387
- items.push({
388
- text: translate('Make main shape'),
389
- callback: () => {
390
- this.enableEdit().deleteShape(shape)
391
- this.editor.prependShape(shape)
392
- },
393
- })
394
- }
395
- return items
396
- },
397
-
398
- getContextMenuEditItems: function (event) {
399
- const items = FeatureMixin.getContextMenuEditItems.call(this, event)
400
- if (
401
- this._map?.editedFeature !== this.feature &&
402
- this.feature.isSameClass(this._map.editedFeature)
403
- ) {
404
- items.push({
405
- text: translate('Transfer shape to edited feature'),
406
- callback: () => {
407
- this.feature.transferShape(event.latlng, this._map.editedFeature)
408
- },
409
- })
410
- }
411
- if (this.feature.isMulti()) {
412
- items.push({
413
- text: translate('Extract shape to separate feature'),
414
- callback: () => {
415
- this.isolateShape(event.latlng)
416
- },
417
- })
418
- }
419
- return items
420
- },
421
-
422
314
  isolateShape: function (atLatLng) {
423
315
  if (!this.feature.isMulti()) return
424
316
  const shape = this.enableEdit().deleteShapeAt(atLatLng)
@@ -463,46 +355,6 @@ export const LeafletPolyline = Polyline.extend({
463
355
  return actions
464
356
  },
465
357
 
466
- getContextMenuEditItems: function (event) {
467
- const items = PathMixin.getContextMenuEditItems.call(this, event)
468
- const vertexClicked = event.vertex
469
- let index
470
- if (!this.feature.isMulti()) {
471
- items.push({
472
- text: translate('Transform to polygon'),
473
- callback: this.feature.toPolygon,
474
- context: this.feature,
475
- })
476
- }
477
- if (vertexClicked) {
478
- index = event.vertex.getIndex()
479
- if (index !== 0 && index !== event.vertex.getLastIndex()) {
480
- items.push({
481
- text: translate('Split line'),
482
- callback: event.vertex.split,
483
- context: event.vertex,
484
- })
485
- } else if (index === 0 || index === event.vertex.getLastIndex()) {
486
- items.push({
487
- text: this._map.help.displayLabel('CONTINUE_LINE'),
488
- callback: event.vertex.continue,
489
- context: event.vertex.continue,
490
- })
491
- }
492
- }
493
- return items
494
- },
495
-
496
- getContextMenuMultiItems: function (event) {
497
- const items = PathMixin.getContextMenuMultiItems.call(this, event)
498
- items.push({
499
- text: translate('Merge lines'),
500
- callback: this.feature.mergeShapes,
501
- context: this.feature,
502
- })
503
- return items
504
- },
505
-
506
358
  getMeasure: function (shape) {
507
359
  // FIXME: compute from data in feature (with TurfJS)
508
360
  const length = L.GeoUtil.lineLength(this._map, shape || this._defaultShape())
@@ -516,29 +368,6 @@ export const LeafletPolygon = Polygon.extend({
516
368
 
517
369
  getClass: () => LeafletPolygon,
518
370
 
519
- getContextMenuEditItems: function (event) {
520
- const items = PathMixin.getContextMenuEditItems.call(this, event)
521
- const shape = this.shapeAt(event.latlng)
522
- // No multi and no holes.
523
- if (
524
- shape &&
525
- !this.feature.isMulti() &&
526
- (LineUtil.isFlat(shape) || shape.length === 1)
527
- ) {
528
- items.push({
529
- text: translate('Transform to lines'),
530
- callback: this.feature.toLineString,
531
- context: this.feature,
532
- })
533
- }
534
- items.push({
535
- text: translate('Start a hole here'),
536
- callback: this.startHole,
537
- context: this,
538
- })
539
- return items
540
- },
541
-
542
371
  startHole: function (event) {
543
372
  this.enableEdit().newHole(event.latlng)
544
373
  },
@@ -603,8 +432,4 @@ export const CircleMarker = BaseCircleMarker.extend({
603
432
  getCenter: function () {
604
433
  return this._latlng
605
434
  },
606
- // FIXME when Leaflet.Editable knows about CircleMarker
607
- editEnabled: () => false,
608
- enableEdit: () => {}, // No-op
609
- disableEdit: () => {}, // No-op
610
435
  })