umap-project 1.14.0a1__py3-none-any.whl → 1.14.0a2__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 (182) hide show
  1. umap/__init__.py +1 -1
  2. umap/static/.gitignore +0 -0
  3. umap/static/umap/base.css +994 -0
  4. umap/static/umap/bitbucket.png +0 -0
  5. umap/static/umap/content.css +395 -0
  6. umap/static/umap/favicons/apple-touch-icon.png +0 -0
  7. umap/static/umap/favicons/favicon.ico +0 -0
  8. umap/static/umap/favicons/icon-192.png +0 -0
  9. umap/static/umap/favicons/icon-512.png +0 -0
  10. umap/static/umap/favicons/icon.svg +5 -0
  11. umap/static/umap/font/FiraSans-Light.woff +0 -0
  12. umap/static/umap/font/FiraSans-Light.woff2 +0 -0
  13. umap/static/umap/font/FiraSans-LightItalic.woff +0 -0
  14. umap/static/umap/font/FiraSans-LightItalic.woff2 +0 -0
  15. umap/static/umap/font/FiraSans-SemiBold.woff +0 -0
  16. umap/static/umap/font/FiraSans-SemiBold.woff2 +0 -0
  17. umap/static/umap/font.css +33 -0
  18. umap/static/umap/github.png +0 -0
  19. umap/static/umap/img/16-white.svg +190 -0
  20. umap/static/umap/img/16.svg +182 -0
  21. umap/static/umap/img/24-white.svg +62 -0
  22. umap/static/umap/img/24.svg +90 -0
  23. umap/static/umap/img/edit.svg +7 -0
  24. umap/static/umap/img/icon-bg.png +0 -0
  25. umap/static/umap/img/logo.svg +4 -0
  26. umap/static/umap/img/logo_filigree.png +0 -0
  27. umap/static/umap/img/logo_small.svg +14 -0
  28. umap/static/umap/img/marker.png +0 -0
  29. umap/static/umap/img/opensource.svg +7 -0
  30. umap/static/umap/img/osm.svg +7 -0
  31. umap/static/umap/img/search.gif +0 -0
  32. umap/static/umap/img/source/16-white.svg +980 -0
  33. umap/static/umap/img/source/16.svg +201 -0
  34. umap/static/umap/img/source/24-white.svg +83 -0
  35. umap/static/umap/img/source/24.svg +110 -0
  36. umap/static/umap/js/components/fragment.js +13 -0
  37. umap/static/umap/js/modules/global.js +8 -0
  38. umap/static/umap/js/modules/urls.js +29 -0
  39. umap/static/umap/js/umap.autocomplete.js +336 -0
  40. umap/static/umap/js/umap.browser.js +148 -0
  41. umap/static/umap/js/umap.controls.js +1542 -0
  42. umap/static/umap/js/umap.core.js +851 -0
  43. umap/static/umap/js/umap.datalayer.permissions.js +72 -0
  44. umap/static/umap/js/umap.features.js +1216 -0
  45. umap/static/umap/js/umap.forms.js +1267 -0
  46. umap/static/umap/js/umap.icon.js +234 -0
  47. umap/static/umap/js/umap.importer.js +166 -0
  48. umap/static/umap/js/umap.js +2010 -0
  49. umap/static/umap/js/umap.layer.js +1636 -0
  50. umap/static/umap/js/umap.permissions.js +212 -0
  51. umap/static/umap/js/umap.popup.js +340 -0
  52. umap/static/umap/js/umap.share.js +254 -0
  53. umap/static/umap/js/umap.slideshow.js +165 -0
  54. umap/static/umap/js/umap.tableeditor.js +120 -0
  55. umap/static/umap/js/umap.ui.js +240 -0
  56. umap/static/umap/js/umap.xhr.js +304 -0
  57. umap/static/umap/locale/am_ET.js +447 -0
  58. umap/static/umap/locale/am_ET.json +445 -0
  59. umap/static/umap/locale/ar.js +447 -0
  60. umap/static/umap/locale/ar.json +445 -0
  61. umap/static/umap/locale/ast.js +447 -0
  62. umap/static/umap/locale/ast.json +445 -0
  63. umap/static/umap/locale/bg.js +447 -0
  64. umap/static/umap/locale/bg.json +445 -0
  65. umap/static/umap/locale/br.js +447 -0
  66. umap/static/umap/locale/br.json +445 -0
  67. umap/static/umap/locale/ca.js +447 -0
  68. umap/static/umap/locale/ca.json +445 -0
  69. umap/static/umap/locale/cs_CZ.js +447 -0
  70. umap/static/umap/locale/cs_CZ.json +445 -0
  71. umap/static/umap/locale/da.js +447 -0
  72. umap/static/umap/locale/da.json +445 -0
  73. umap/static/umap/locale/de.js +447 -0
  74. umap/static/umap/locale/de.json +445 -0
  75. umap/static/umap/locale/el.js +447 -0
  76. umap/static/umap/locale/el.json +445 -0
  77. umap/static/umap/locale/en.js +447 -0
  78. umap/static/umap/locale/en.json +445 -0
  79. umap/static/umap/locale/en_US.json +445 -0
  80. umap/static/umap/locale/es.js +447 -0
  81. umap/static/umap/locale/es.json +445 -0
  82. umap/static/umap/locale/et.js +447 -0
  83. umap/static/umap/locale/et.json +445 -0
  84. umap/static/umap/locale/eu.js +413 -0
  85. umap/static/umap/locale/eu.json +411 -0
  86. umap/static/umap/locale/fa_IR.js +447 -0
  87. umap/static/umap/locale/fa_IR.json +445 -0
  88. umap/static/umap/locale/fi.js +447 -0
  89. umap/static/umap/locale/fi.json +445 -0
  90. umap/static/umap/locale/fr.js +447 -0
  91. umap/static/umap/locale/fr.json +445 -0
  92. umap/static/umap/locale/gl.js +447 -0
  93. umap/static/umap/locale/gl.json +445 -0
  94. umap/static/umap/locale/he.js +447 -0
  95. umap/static/umap/locale/he.json +445 -0
  96. umap/static/umap/locale/hr.js +447 -0
  97. umap/static/umap/locale/hr.json +445 -0
  98. umap/static/umap/locale/hu.js +447 -0
  99. umap/static/umap/locale/hu.json +445 -0
  100. umap/static/umap/locale/id.js +447 -0
  101. umap/static/umap/locale/id.json +445 -0
  102. umap/static/umap/locale/is.js +447 -0
  103. umap/static/umap/locale/is.json +445 -0
  104. umap/static/umap/locale/it.js +447 -0
  105. umap/static/umap/locale/it.json +445 -0
  106. umap/static/umap/locale/ja.js +447 -0
  107. umap/static/umap/locale/ja.json +445 -0
  108. umap/static/umap/locale/ko.js +447 -0
  109. umap/static/umap/locale/ko.json +445 -0
  110. umap/static/umap/locale/lt.js +447 -0
  111. umap/static/umap/locale/lt.json +445 -0
  112. umap/static/umap/locale/ms.js +447 -0
  113. umap/static/umap/locale/ms.json +445 -0
  114. umap/static/umap/locale/nl.js +447 -0
  115. umap/static/umap/locale/nl.json +445 -0
  116. umap/static/umap/locale/no.js +447 -0
  117. umap/static/umap/locale/no.json +445 -0
  118. umap/static/umap/locale/pl.js +447 -0
  119. umap/static/umap/locale/pl.json +445 -0
  120. umap/static/umap/locale/pl_PL.json +445 -0
  121. umap/static/umap/locale/pt.js +447 -0
  122. umap/static/umap/locale/pt.json +445 -0
  123. umap/static/umap/locale/pt_BR.js +447 -0
  124. umap/static/umap/locale/pt_BR.json +445 -0
  125. umap/static/umap/locale/pt_PT.js +447 -0
  126. umap/static/umap/locale/pt_PT.json +445 -0
  127. umap/static/umap/locale/ro.js +447 -0
  128. umap/static/umap/locale/ro.json +445 -0
  129. umap/static/umap/locale/ru.js +447 -0
  130. umap/static/umap/locale/ru.json +445 -0
  131. umap/static/umap/locale/si.js +439 -0
  132. umap/static/umap/locale/si.json +437 -0
  133. umap/static/umap/locale/sk_SK.js +447 -0
  134. umap/static/umap/locale/sk_SK.json +445 -0
  135. umap/static/umap/locale/sl.js +447 -0
  136. umap/static/umap/locale/sl.json +445 -0
  137. umap/static/umap/locale/sr.js +447 -0
  138. umap/static/umap/locale/sr.json +445 -0
  139. umap/static/umap/locale/sv.js +447 -0
  140. umap/static/umap/locale/sv.json +445 -0
  141. umap/static/umap/locale/th_TH.js +447 -0
  142. umap/static/umap/locale/th_TH.json +445 -0
  143. umap/static/umap/locale/tr.js +447 -0
  144. umap/static/umap/locale/tr.json +445 -0
  145. umap/static/umap/locale/uk_UA.js +447 -0
  146. umap/static/umap/locale/uk_UA.json +445 -0
  147. umap/static/umap/locale/vi.js +447 -0
  148. umap/static/umap/locale/vi.json +445 -0
  149. umap/static/umap/locale/vi_VN.json +445 -0
  150. umap/static/umap/locale/zh.js +447 -0
  151. umap/static/umap/locale/zh.json +445 -0
  152. umap/static/umap/locale/zh_CN.json +445 -0
  153. umap/static/umap/locale/zh_TW.Big5.json +445 -0
  154. umap/static/umap/locale/zh_TW.js +447 -0
  155. umap/static/umap/locale/zh_TW.json +445 -0
  156. umap/static/umap/map.css +1843 -0
  157. umap/static/umap/nav.css +81 -0
  158. umap/static/umap/openstreetmap.png +0 -0
  159. umap/static/umap/test/.eslintrc +22 -0
  160. umap/static/umap/test/Choropleth.js +243 -0
  161. umap/static/umap/test/Controls.js +100 -0
  162. umap/static/umap/test/DataLayer.js +495 -0
  163. umap/static/umap/test/Feature.js +382 -0
  164. umap/static/umap/test/Map.Export.js +106 -0
  165. umap/static/umap/test/Map.Init.js +46 -0
  166. umap/static/umap/test/Map.js +342 -0
  167. umap/static/umap/test/Marker.js +122 -0
  168. umap/static/umap/test/Permissions.js +74 -0
  169. umap/static/umap/test/Polygon.js +367 -0
  170. umap/static/umap/test/Polyline.js +402 -0
  171. umap/static/umap/test/TableEditor.js +100 -0
  172. umap/static/umap/test/URLs.js +54 -0
  173. umap/static/umap/test/Util.js +549 -0
  174. umap/static/umap/test/_pre.js +460 -0
  175. umap/static/umap/test/index.html +135 -0
  176. umap/static/umap/theme.css +1 -0
  177. umap/static/umap/twitter.png +0 -0
  178. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/METADATA +1 -1
  179. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/RECORD +182 -6
  180. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/WHEEL +0 -0
  181. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/entry_points.txt +0 -0
  182. {umap_project-1.14.0a1.dist-info → umap_project-1.14.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1216 @@
1
+ L.U.FeatureMixin = {
2
+ staticOptions: { mainColor: 'color' },
3
+
4
+ initialize: function (map, latlng, options) {
5
+ this.map = map
6
+ if (typeof options === 'undefined') {
7
+ options = {}
8
+ }
9
+ // DataLayer the marker belongs to
10
+ this.datalayer = options.datalayer || null
11
+ this.properties = { _umap_options: {} }
12
+ if (options.geojson) {
13
+ this.populate(options.geojson)
14
+ }
15
+ let isDirty = false
16
+ const self = this
17
+ try {
18
+ Object.defineProperty(this, 'isDirty', {
19
+ get: function () {
20
+ return isDirty
21
+ },
22
+ set: function (status) {
23
+ if (!isDirty && status) {
24
+ self.fire('isdirty')
25
+ }
26
+ isDirty = status
27
+ if (self.datalayer) {
28
+ self.datalayer.isDirty = status
29
+ }
30
+ },
31
+ })
32
+ } catch (e) {
33
+ // Certainly IE8, which has a limited version of defineProperty
34
+ }
35
+ this.preInit()
36
+ this.addInteractions()
37
+ this.parentClass.prototype.initialize.call(this, latlng, options)
38
+ },
39
+
40
+ preInit: function () {},
41
+
42
+ isReadOnly: function () {
43
+ return this.datalayer && this.datalayer.isDataReadOnly()
44
+ },
45
+
46
+ getSlug: function () {
47
+ return this.properties[this.map.options.slugKey || 'name'] || ''
48
+ },
49
+
50
+ getPermalink: function () {
51
+ const slug = this.getSlug()
52
+ if (slug)
53
+ return `${L.Util.getBaseUrl()}?${L.Util.buildQueryString({ feature: slug })}${
54
+ window.location.hash
55
+ }`
56
+ },
57
+
58
+ view: function (e) {
59
+ if (this.map.editEnabled) return
60
+ const outlink = this.getOption('outlink'),
61
+ target = this.getOption('outlinkTarget')
62
+ if (outlink) {
63
+ switch (target) {
64
+ case 'self':
65
+ window.location = outlink
66
+ break
67
+ case 'parent':
68
+ window.top.location = outlink
69
+ break
70
+ default:
71
+ const win = window.open(this.properties._umap_options.outlink)
72
+ }
73
+ return
74
+ }
75
+ // TODO deal with an event instead?
76
+ if (this.map.slideshow) this.map.slideshow.current = this
77
+ this.map.currentFeature = this
78
+ this.attachPopup()
79
+ this.openPopup((e && e.latlng) || this.getCenter())
80
+ },
81
+
82
+ openPopup: function () {
83
+ if (this.map.editEnabled) return
84
+ this.parentClass.prototype.openPopup.apply(this, arguments)
85
+ },
86
+
87
+ edit: function (e) {
88
+ if (!this.map.editEnabled || this.isReadOnly()) return
89
+ const container = L.DomUtil.create('div', 'umap-feature-container')
90
+ L.DomUtil.add(
91
+ 'h3',
92
+ `umap-feature-properties ${this.getClassName()}`,
93
+ container,
94
+ L._('Feature properties')
95
+ )
96
+
97
+ let builder = new L.U.FormBuilder(this, ['datalayer'], {
98
+ callback: function () {
99
+ this.edit(e)
100
+ }, // removeLayer step will close the edit panel, let's reopen it
101
+ })
102
+ container.appendChild(builder.build())
103
+
104
+ const properties = []
105
+ let property
106
+ for (let i = 0; i < this.datalayer._propertiesIndex.length; i++) {
107
+ property = this.datalayer._propertiesIndex[i]
108
+ if (L.Util.indexOf(['name', 'description'], property) !== -1) {
109
+ continue
110
+ }
111
+ properties.push([`properties.${property}`, { label: property }])
112
+ }
113
+ // We always want name and description for now (properties management to come)
114
+ properties.unshift('properties.description')
115
+ properties.unshift('properties.name')
116
+ builder = new L.U.FormBuilder(this, properties, {
117
+ id: 'umap-feature-properties',
118
+ callback: this._redraw, // In case we have dynamic options…
119
+ })
120
+ container.appendChild(builder.build())
121
+ this.map.ui.once('panel:ready', () => {
122
+ builder.helpers['properties.name'].input.focus()
123
+ })
124
+ this.appendEditFieldsets(container)
125
+ const advancedActions = L.DomUtil.createFieldset(container, L._('Advanced actions'))
126
+ this.getAdvancedEditActions(advancedActions)
127
+ this.map.ui.openPanel({ data: { html: container }, className: 'dark' })
128
+ this.map.editedFeature = this
129
+ if (!this.isOnScreen()) this.zoomTo(e)
130
+ },
131
+
132
+ getAdvancedEditActions: function (container) {
133
+ const deleteButton = L.DomUtil.createButton(
134
+ 'button umap-delete',
135
+ container,
136
+ L._('Delete'),
137
+ function (e) {
138
+ L.DomEvent.stop(e)
139
+ if (this.confirmDelete()) this.map.ui.closePanel()
140
+ },
141
+ this
142
+ )
143
+ },
144
+
145
+ appendEditFieldsets: function (container) {
146
+ const optionsFields = this.getShapeOptions()
147
+ let builder = new L.U.FormBuilder(this, optionsFields, {
148
+ id: 'umap-feature-shape-properties',
149
+ callback: this._redraw,
150
+ })
151
+ const shapeProperties = L.DomUtil.createFieldset(container, L._('Shape properties'))
152
+ shapeProperties.appendChild(builder.build())
153
+
154
+ const advancedOptions = this.getAdvancedOptions()
155
+ builder = new L.U.FormBuilder(this, advancedOptions, {
156
+ id: 'umap-feature-advanced-properties',
157
+ callback: this._redraw,
158
+ })
159
+ const advancedProperties = L.DomUtil.createFieldset(
160
+ container,
161
+ L._('Advanced properties')
162
+ )
163
+ advancedProperties.appendChild(builder.build())
164
+
165
+ const interactionOptions = this.getInteractionOptions()
166
+ builder = new L.U.FormBuilder(this, interactionOptions, {
167
+ callback: this._redraw,
168
+ })
169
+ const popupFieldset = L.DomUtil.createFieldset(
170
+ container,
171
+ L._('Interaction options')
172
+ )
173
+ popupFieldset.appendChild(builder.build())
174
+ },
175
+
176
+ getInteractionOptions: function () {
177
+ return [
178
+ 'properties._umap_options.popupShape',
179
+ 'properties._umap_options.popupTemplate',
180
+ 'properties._umap_options.showLabel',
181
+ 'properties._umap_options.labelDirection',
182
+ 'properties._umap_options.labelInteractive',
183
+ 'properties._umap_options.outlink',
184
+ 'properties._umap_options.outlinkTarget',
185
+ ]
186
+ },
187
+
188
+ endEdit: function () {},
189
+
190
+ getDisplayName: function (fallback) {
191
+ if (fallback === undefined) fallback = this.datalayer.options.name
192
+ const key = this.getOption('labelKey') || 'name'
193
+ // Variables mode.
194
+ if (L.Util.hasVar(key)) return L.Util.greedyTemplate(key, this.extendedProperties())
195
+ // Simple mode.
196
+ return this.properties[key] || this.properties.title || fallback
197
+ },
198
+
199
+ hasPopupFooter: function () {
200
+ if (L.Browser.ielt9) return false
201
+ if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic)
202
+ return false
203
+ return this.map.options.displayPopupFooter
204
+ },
205
+
206
+ getPopupClass: function () {
207
+ const old = this.getOption('popupTemplate') // Retrocompat.
208
+ return L.U.Popup[this.getOption('popupShape') || old] || L.U.Popup
209
+ },
210
+
211
+ attachPopup: function () {
212
+ const Class = this.getPopupClass()
213
+ this.bindPopup(new Class(this))
214
+ },
215
+
216
+ confirmDelete: function () {
217
+ if (confirm(L._('Are you sure you want to delete the feature?'))) {
218
+ this.del()
219
+ return true
220
+ }
221
+ return false
222
+ },
223
+
224
+ del: function () {
225
+ this.isDirty = true
226
+ this.map.closePopup()
227
+ if (this.datalayer) {
228
+ this.datalayer.removeLayer(this)
229
+ this.disconnectFromDataLayer(this.datalayer)
230
+ }
231
+ },
232
+
233
+ connectToDataLayer: function (datalayer) {
234
+ this.datalayer = datalayer
235
+ this.options.renderer = this.datalayer.renderer
236
+ },
237
+
238
+ disconnectFromDataLayer: function (datalayer) {
239
+ if (this.datalayer === datalayer) {
240
+ this.datalayer = null
241
+ }
242
+ },
243
+
244
+ cleanProperty: function ([key, value]) {
245
+ // dot in key will break the dot based property access
246
+ // while editing the feature
247
+ key = key.replace('.', '_')
248
+ return [key, value]
249
+ },
250
+
251
+ populate: function (feature) {
252
+ this.properties = Object.fromEntries(
253
+ Object.entries(feature.properties || {}).map(this.cleanProperty)
254
+ )
255
+ this.properties._umap_options = L.extend(
256
+ {},
257
+ this.properties._storage_options,
258
+ this.properties._umap_options
259
+ )
260
+ // Retrocompat
261
+ if (this.properties._umap_options.clickable === false) {
262
+ this.properties._umap_options.interactive = false
263
+ delete this.properties._umap_options.clickable
264
+ }
265
+ },
266
+
267
+ changeDataLayer: function (datalayer) {
268
+ if (this.datalayer) {
269
+ this.datalayer.isDirty = true
270
+ this.datalayer.removeLayer(this)
271
+ }
272
+ datalayer.addLayer(this)
273
+ datalayer.isDirty = true
274
+ this._redraw()
275
+ },
276
+
277
+ getOption: function (option, fallback) {
278
+ let value = fallback
279
+ if (typeof this.staticOptions[option] !== 'undefined') {
280
+ value = this.staticOptions[option]
281
+ } else if (L.Util.usableOption(this.properties._umap_options, option)) {
282
+ value = this.properties._umap_options[option]
283
+ } else if (this.datalayer) {
284
+ value = this.datalayer.getOption(option, this)
285
+ } else {
286
+ value = this.map.getOption(option)
287
+ }
288
+ return value
289
+ },
290
+
291
+ getDynamicOption: function (option, fallback) {
292
+ let value = this.getOption(option, fallback)
293
+ // There is a variable inside.
294
+ if (L.Util.hasVar(value)) {
295
+ value = L.Util.greedyTemplate(value, this.properties, true)
296
+ if (L.Util.hasVar(value)) value = this.map.getDefaultOption(option)
297
+ }
298
+ return value
299
+ },
300
+
301
+ zoomTo: function (e) {
302
+ e = e || {}
303
+ const easing = e.easing !== undefined ? e.easing : this.map.options.easing
304
+ if (easing) {
305
+ this.map.flyTo(this.getCenter(), this.getBestZoom())
306
+ } else {
307
+ const latlng = e.latlng || this.getCenter()
308
+ this.map.setView(latlng, this.getBestZoom() || this.map.getZoom())
309
+ }
310
+ if (e.callback) e.callback.call(this)
311
+ },
312
+
313
+ getBestZoom: function () {
314
+ return this.getOption('zoomTo')
315
+ },
316
+
317
+ getNext: function () {
318
+ return this.datalayer.getNextFeature(this)
319
+ },
320
+
321
+ getPrevious: function () {
322
+ return this.datalayer.getPreviousFeature(this)
323
+ },
324
+
325
+ cloneProperties: function () {
326
+ const properties = L.extend({}, this.properties)
327
+ properties._umap_options = L.extend({}, properties._umap_options)
328
+ if (Object.keys && Object.keys(properties._umap_options).length === 0) {
329
+ delete properties._umap_options // It can make a difference on big data sets
330
+ }
331
+ return properties
332
+ },
333
+
334
+ deleteProperty: function (property) {
335
+ delete this.properties[property]
336
+ this.makeDirty()
337
+ },
338
+
339
+ renameProperty: function (from, to) {
340
+ this.properties[to] = this.properties[from]
341
+ this.deleteProperty(from)
342
+ },
343
+
344
+ toGeoJSON: function () {
345
+ const geojson = this.parentClass.prototype.toGeoJSON.call(this)
346
+ geojson.properties = this.cloneProperties()
347
+ delete geojson.properties._storage_options
348
+ return geojson
349
+ },
350
+
351
+ addInteractions: function () {
352
+ this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu, this)
353
+ this.on('click', this._onClick)
354
+ },
355
+
356
+ _onClick: function (e) {
357
+ if (this.map.measureTools && this.map.measureTools.enabled()) return
358
+ this._popupHandlersAdded = true // Prevent leaflet from managing event
359
+ if (!this.map.editEnabled) {
360
+ this.view(e)
361
+ } else if (!this.isReadOnly()) {
362
+ if (e.originalEvent.shiftKey) {
363
+ if (e.originalEvent.ctrlKey || e.originalEvent.metaKey) {
364
+ this.datalayer.edit(e)
365
+ } else {
366
+ if (this._toggleEditing) this._toggleEditing(e)
367
+ else this.edit(e)
368
+ }
369
+ } else {
370
+ new L.Toolbar.Popup(e.latlng, {
371
+ className: 'leaflet-inplace-toolbar',
372
+ anchor: this.getPopupToolbarAnchor(),
373
+ actions: this.getInplaceToolbarActions(e),
374
+ }).addTo(this.map, this, e.latlng)
375
+ }
376
+ }
377
+ L.DomEvent.stop(e)
378
+ },
379
+
380
+ getPopupToolbarAnchor: function () {
381
+ return [0, 0]
382
+ },
383
+
384
+ getInplaceToolbarActions: function (e) {
385
+ return [L.U.ToggleEditAction, L.U.DeleteFeatureAction]
386
+ },
387
+
388
+ _showContextMenu: function (e) {
389
+ L.DomEvent.stop(e)
390
+ const pt = this.map.mouseEventToContainerPoint(e.originalEvent)
391
+ e.relatedTarget = this
392
+ this.map.contextmenu.showAt(pt, e)
393
+ },
394
+
395
+ makeDirty: function () {
396
+ this.isDirty = true
397
+ },
398
+
399
+ getMap: function () {
400
+ return this.map
401
+ },
402
+
403
+ getContextMenuItems: function (e) {
404
+ const permalink = this.getPermalink()
405
+ let items = []
406
+ if (permalink)
407
+ items.push({
408
+ text: L._('Permalink'),
409
+ callback: function () {
410
+ window.open(permalink)
411
+ },
412
+ })
413
+ if (this.map.editEnabled && !this.isReadOnly()) {
414
+ items = items.concat(this.getContextMenuEditItems(e))
415
+ }
416
+ return items
417
+ },
418
+
419
+ getContextMenuEditItems: function () {
420
+ let items = ['-']
421
+ if (this.map.editedFeature !== this) {
422
+ items.push({
423
+ text: L._('Edit this feature') + ' (⇧+Click)',
424
+ callback: this.edit,
425
+ context: this,
426
+ iconCls: 'umap-edit',
427
+ })
428
+ }
429
+ items = items.concat(
430
+ {
431
+ text: L._("Edit feature's layer") + ' (Ctrl+⇧+Click)',
432
+ callback: this.datalayer.edit,
433
+ context: this.datalayer,
434
+ iconCls: 'umap-edit',
435
+ },
436
+ {
437
+ text: L._('Delete this feature'),
438
+ callback: this.confirmDelete,
439
+ context: this,
440
+ iconCls: 'umap-delete',
441
+ },
442
+ {
443
+ text: L._('Clone this feature'),
444
+ callback: this.clone,
445
+ context: this,
446
+ }
447
+ )
448
+ return items
449
+ },
450
+
451
+ onRemove: function (map) {
452
+ this.parentClass.prototype.onRemove.call(this, map)
453
+ if (this.map.editedFeature === this) {
454
+ this.endEdit()
455
+ this.map.ui.closePanel()
456
+ }
457
+ },
458
+
459
+ resetTooltip: function () {
460
+ if (!this.hasGeom()) return
461
+ const displayName = this.getDisplayName(null)
462
+ let showLabel = this.getOption('showLabel')
463
+ const oldLabelHover = this.getOption('labelHover')
464
+
465
+ const options = {
466
+ direction: this.getOption('labelDirection'),
467
+ interactive: this.getOption('labelInteractive'),
468
+ }
469
+
470
+ if (oldLabelHover && showLabel) showLabel = null // Retrocompat.
471
+ options.permanent = showLabel === true
472
+ this.unbindTooltip()
473
+ if ((showLabel === true || showLabel === null) && displayName)
474
+ this.bindTooltip(L.Util.escapeHTML(displayName), options)
475
+ },
476
+
477
+ matchFilter: function (filter, keys) {
478
+ filter = filter.toLowerCase()
479
+ for (let i = 0, value; i < keys.length; i++) {
480
+ value = (this.properties[keys[i]] || '') + ''
481
+ if (value.toLowerCase().indexOf(filter) !== -1) return true
482
+ }
483
+ return false
484
+ },
485
+
486
+ matchFacets: function () {
487
+ const facets = this.map.facets
488
+ for (const [property, expected] of Object.entries(facets)) {
489
+ if (expected.length) {
490
+ let value = this.properties[property]
491
+ if (!value || !expected.includes(value)) return false
492
+ }
493
+ }
494
+ return true
495
+ },
496
+
497
+ onVertexRawClick: function (e) {
498
+ new L.Toolbar.Popup(e.latlng, {
499
+ className: 'leaflet-inplace-toolbar',
500
+ actions: this.getVertexActions(e),
501
+ }).addTo(this.map, this, e.latlng, e.vertex)
502
+ },
503
+
504
+ getVertexActions: function () {
505
+ return [L.U.DeleteVertexAction]
506
+ },
507
+
508
+ isMulti: function () {
509
+ return false
510
+ },
511
+
512
+ clone: function () {
513
+ const layer = this.datalayer.geojsonToFeatures(this.toGeoJSON())
514
+ layer.isDirty = true
515
+ layer.edit()
516
+ return layer
517
+ },
518
+
519
+ extendedProperties: function () {
520
+ // Include context properties
521
+ properties = this.map.getGeoContext()
522
+ if (L.locale) properties.locale = L.locale
523
+ if (L.lang) properties.lang = L.lang
524
+ properties.rank = this.getRank() + 1
525
+ if (this.hasGeom()) {
526
+ center = this.getCenter()
527
+ properties.lat = center.lat
528
+ properties.lon = center.lng
529
+ properties.lng = center.lng
530
+ if (typeof this.getMeasure !== 'undefined') {
531
+ properties.measure = this.getMeasure()
532
+ }
533
+ }
534
+ return L.extend(properties, this.properties)
535
+ },
536
+
537
+ getRank: function () {
538
+ return this.datalayer._index.indexOf(L.stamp(this))
539
+ },
540
+ }
541
+
542
+ L.U.Marker = L.Marker.extend({
543
+ parentClass: L.Marker,
544
+ includes: [L.U.FeatureMixin],
545
+
546
+ preInit: function () {
547
+ this.setIcon(this.getIcon())
548
+ },
549
+
550
+ highlight: function () {
551
+ L.DomUtil.addClass(this.options.icon.elements.main, 'umap-icon-active')
552
+ },
553
+
554
+ resetHighlight: function () {
555
+ L.DomUtil.removeClass(this.options.icon.elements.main, 'umap-icon-active')
556
+ },
557
+
558
+ addInteractions: function () {
559
+ L.U.FeatureMixin.addInteractions.call(this)
560
+ this.on(
561
+ 'dragend',
562
+ function (e) {
563
+ this.isDirty = true
564
+ this.edit(e)
565
+ },
566
+ this
567
+ )
568
+ if (!this.isReadOnly()) this.on('mouseover', this._enableDragging)
569
+ this.on('mouseout', this._onMouseOut)
570
+ this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
571
+ this.on('popupopen', this.highlight)
572
+ this.on('popupclose', this.resetHighlight)
573
+ },
574
+
575
+ hasGeom: function () {
576
+ return !!this._latlng
577
+ },
578
+
579
+ _onMouseOut: function () {
580
+ if (
581
+ this.dragging &&
582
+ this.dragging._draggable &&
583
+ !this.dragging._draggable._moving
584
+ ) {
585
+ // Do not disable if the mouse went out while dragging
586
+ this._disableDragging()
587
+ }
588
+ },
589
+
590
+ _enableDragging: function () {
591
+ // TODO: start dragging after 1 second on mouse down
592
+ if (this.map.editEnabled) {
593
+ if (!this.editEnabled()) this.enableEdit()
594
+ // Enabling dragging on the marker override the Draggable._OnDown
595
+ // event, which, as it stopPropagation, refrain the call of
596
+ // _onDown with map-pane element, which is responsible to
597
+ // set the _moved to false, and thus to enable the click.
598
+ // We should find a cleaner way to handle this.
599
+ this.map.dragging._draggable._moved = false
600
+ }
601
+ },
602
+
603
+ _disableDragging: function () {
604
+ if (this.map.editEnabled) {
605
+ if (this.editor && this.editor.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
606
+ // do not listen to them
607
+ this.disableEdit()
608
+ }
609
+ },
610
+
611
+ _redraw: function () {
612
+ if (this.datalayer && this.datalayer.isVisible()) {
613
+ this._initIcon()
614
+ this.update()
615
+ }
616
+ },
617
+
618
+ _initIcon: function () {
619
+ this.options.icon = this.getIcon()
620
+ L.Marker.prototype._initIcon.call(this)
621
+ // Allow to run code when icon is actually part of the DOM
622
+ this.options.icon.onAdd()
623
+ this.resetTooltip()
624
+ },
625
+
626
+ _getTooltipAnchor: function () {
627
+ const anchor = this.options.icon.options.tooltipAnchor.clone(),
628
+ direction = this.getOption('labelDirection')
629
+ if (direction === 'left') {
630
+ anchor.x *= -1
631
+ } else if (direction === 'bottom') {
632
+ anchor.x = 0
633
+ anchor.y = 0
634
+ } else if (direction === 'top') {
635
+ anchor.x = 0
636
+ }
637
+ return anchor
638
+ },
639
+
640
+ disconnectFromDataLayer: function (datalayer) {
641
+ this.options.icon.datalayer = null
642
+ L.U.FeatureMixin.disconnectFromDataLayer.call(this, datalayer)
643
+ },
644
+
645
+ _getIconUrl: function (name) {
646
+ if (typeof name === 'undefined') name = 'icon'
647
+ return this.getOption(`${name}Url`)
648
+ },
649
+
650
+ getIconClass: function () {
651
+ return this.getOption('iconClass')
652
+ },
653
+
654
+ getIcon: function () {
655
+ const Class = L.U.Icon[this.getIconClass()] || L.U.Icon.Default
656
+ return new Class(this.map, { feature: this })
657
+ },
658
+
659
+ getCenter: function () {
660
+ return this._latlng
661
+ },
662
+
663
+ getClassName: function () {
664
+ return 'marker'
665
+ },
666
+
667
+ getShapeOptions: function () {
668
+ return [
669
+ 'properties._umap_options.color',
670
+ 'properties._umap_options.iconClass',
671
+ 'properties._umap_options.iconUrl',
672
+ 'properties._umap_options.iconOpacity',
673
+ ]
674
+ },
675
+
676
+ getAdvancedOptions: function () {
677
+ return ['properties._umap_options.zoomTo']
678
+ },
679
+
680
+ appendEditFieldsets: function (container) {
681
+ L.U.FeatureMixin.appendEditFieldsets.call(this, container)
682
+ const coordinatesOptions = [
683
+ ['_latlng.lat', { handler: 'FloatInput', label: L._('Latitude') }],
684
+ ['_latlng.lng', { handler: 'FloatInput', label: L._('Longitude') }],
685
+ ]
686
+ const builder = new L.U.FormBuilder(this, coordinatesOptions, {
687
+ callback: function () {
688
+ if (!this._latlng.isValid()) {
689
+ this.map.ui.alert({
690
+ content: L._('Invalid latitude or longitude'),
691
+ level: 'error',
692
+ })
693
+ builder.resetField('_latlng.lat')
694
+ builder.resetField('_latlng.lng')
695
+ }
696
+ this._redraw()
697
+ this.zoomTo({ easing: false })
698
+ },
699
+ callbackContext: this,
700
+ })
701
+ const fieldset = L.DomUtil.createFieldset(container, L._('Coordinates'))
702
+ fieldset.appendChild(builder.build())
703
+ },
704
+
705
+ zoomTo: function (e) {
706
+ if (this.datalayer.isClustered() && !this._icon) {
707
+ // callback is mandatory for zoomToShowLayer
708
+ this.datalayer.layer.zoomToShowLayer(this, e.callback || (() => {}))
709
+ } else {
710
+ L.U.FeatureMixin.zoomTo.call(this, e)
711
+ }
712
+ },
713
+
714
+ isOnScreen: function (bounds) {
715
+ bounds = bounds || this.map.getBounds()
716
+ return bounds.contains(this._latlng)
717
+ },
718
+
719
+ getPopupToolbarAnchor: function () {
720
+ return this.options.icon.options.popupAnchor
721
+ },
722
+ })
723
+
724
+ L.U.PathMixin = {
725
+ hasGeom: function () {
726
+ return !this.isEmpty()
727
+ },
728
+
729
+ connectToDataLayer: function (datalayer) {
730
+ L.U.FeatureMixin.connectToDataLayer.call(this, datalayer)
731
+ // We keep markers on their own layer on top of the paths.
732
+ this.options.pane = this.datalayer.pane
733
+ },
734
+
735
+ edit: function (e) {
736
+ if (this.map.editEnabled) {
737
+ if (!this.editEnabled()) this.enableEdit()
738
+ L.U.FeatureMixin.edit.call(this, e)
739
+ }
740
+ },
741
+
742
+ _toggleEditing: function (e) {
743
+ if (this.map.editEnabled) {
744
+ if (this.editEnabled()) {
745
+ this.endEdit()
746
+ this.map.ui.closePanel()
747
+ } else {
748
+ this.edit(e)
749
+ }
750
+ }
751
+ // FIXME: disable when disabling global edit
752
+ L.DomEvent.stop(e)
753
+ },
754
+
755
+ styleOptions: [
756
+ 'smoothFactor',
757
+ 'color',
758
+ 'opacity',
759
+ 'stroke',
760
+ 'weight',
761
+ 'fill',
762
+ 'fillColor',
763
+ 'fillOpacity',
764
+ 'dashArray',
765
+ 'interactive',
766
+ ],
767
+
768
+ getShapeOptions: function () {
769
+ return [
770
+ 'properties._umap_options.color',
771
+ 'properties._umap_options.opacity',
772
+ 'properties._umap_options.weight',
773
+ ]
774
+ },
775
+
776
+ getAdvancedOptions: function () {
777
+ return [
778
+ 'properties._umap_options.smoothFactor',
779
+ 'properties._umap_options.dashArray',
780
+ 'properties._umap_options.zoomTo',
781
+ ]
782
+ },
783
+
784
+ setStyle: function (options) {
785
+ options = options || {}
786
+ let option
787
+ for (const idx in this.styleOptions) {
788
+ option = this.styleOptions[idx]
789
+ options[option] = this.getDynamicOption(option)
790
+ }
791
+ if (options.interactive) this.options.pointerEvents = 'visiblePainted'
792
+ else this.options.pointerEvents = 'stroke'
793
+ this.parentClass.prototype.setStyle.call(this, options)
794
+ },
795
+
796
+ _redraw: function () {
797
+ this.setStyle()
798
+ this.resetTooltip()
799
+ },
800
+
801
+ onAdd: function (map) {
802
+ this._container = null
803
+ this.setStyle()
804
+ // Show tooltip again when Leaflet.label allow static label on path.
805
+ // cf https://github.com/Leaflet/Leaflet/pull/3952
806
+ // this.map.on('showmeasure', this.showMeasureTooltip, this);
807
+ // this.map.on('hidemeasure', this.removeTooltip, this);
808
+ this.parentClass.prototype.onAdd.call(this, map)
809
+ if (this.editing && this.editing.enabled()) this.editing.addHooks()
810
+ this.resetTooltip()
811
+ },
812
+
813
+ onRemove: function (map) {
814
+ // this.map.off('showmeasure', this.showMeasureTooltip, this);
815
+ // this.map.off('hidemeasure', this.removeTooltip, this);
816
+ if (this.editing && this.editing.enabled()) this.editing.removeHooks()
817
+ L.U.FeatureMixin.onRemove.call(this, map)
818
+ },
819
+
820
+ getBestZoom: function () {
821
+ return this.getOption('zoomTo') || this.map.getBoundsZoom(this.getBounds(), true)
822
+ },
823
+
824
+ endEdit: function () {
825
+ this.disableEdit()
826
+ L.U.FeatureMixin.endEdit.call(this)
827
+ },
828
+
829
+ highlightPath: function () {
830
+ this.parentClass.prototype.setStyle.call(this, {
831
+ fillOpacity: Math.sqrt(this.getDynamicOption('fillOpacity', 1.0)),
832
+ opacity: 1.0,
833
+ weight: 1.3 * this.getDynamicOption('weight'),
834
+ })
835
+ },
836
+
837
+ _onMouseOver: function () {
838
+ if (this.map.measureTools && this.map.measureTools.enabled()) {
839
+ this.map.ui.tooltip({ content: this.getMeasure(), anchor: this })
840
+ } else if (this.map.editEnabled && !this.map.editedFeature) {
841
+ this.map.ui.tooltip({ content: L._('Click to edit'), anchor: this })
842
+ }
843
+ },
844
+
845
+ addInteractions: function () {
846
+ L.U.FeatureMixin.addInteractions.call(this)
847
+ this.on('mouseover', this._onMouseOver)
848
+ this.on('edit', this.makeDirty)
849
+ this.on('drag editable:drag', this._onDrag)
850
+ this.on('popupopen', this.highlightPath)
851
+ this.on('popupclose', this._redraw)
852
+ },
853
+
854
+ _onDrag: function () {
855
+ if (this._tooltip) this._tooltip.setLatLng(this.getCenter())
856
+ },
857
+
858
+ transferShape: function (at, to) {
859
+ const shape = this.enableEdit().deleteShapeAt(at)
860
+ this.disableEdit()
861
+ if (!shape) return
862
+ to.enableEdit().appendShape(shape)
863
+ if (!this._latlngs.length || !this._latlngs[0].length) this.del()
864
+ },
865
+
866
+ isolateShape: function (at) {
867
+ if (!this.isMulti()) return
868
+ const shape = this.enableEdit().deleteShapeAt(at)
869
+ this.disableEdit()
870
+ if (!shape) return
871
+ const properties = this.cloneProperties()
872
+ const other = new (this instanceof L.U.Polyline ? L.U.Polyline : L.U.Polygon)(
873
+ this.map,
874
+ shape,
875
+ { geojson: { properties: properties } }
876
+ )
877
+ this.datalayer.addLayer(other)
878
+ other.edit()
879
+ return other
880
+ },
881
+
882
+ getContextMenuItems: function (e) {
883
+ let items = L.U.FeatureMixin.getContextMenuItems.call(this, e)
884
+ items.push({
885
+ text: L._('Display measure'),
886
+ callback: function () {
887
+ this.map.ui.alert({ content: this.getMeasure(), level: 'info' })
888
+ },
889
+ context: this,
890
+ })
891
+ if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
892
+ items = items.concat(this.getContextMenuMultiItems(e))
893
+ }
894
+ return items
895
+ },
896
+
897
+ getContextMenuMultiItems: function (e) {
898
+ const items = [
899
+ '-',
900
+ {
901
+ text: L._('Remove shape from the multi'),
902
+ callback: function () {
903
+ this.enableEdit().deleteShapeAt(e.latlng)
904
+ },
905
+ context: this,
906
+ },
907
+ ]
908
+ const shape = this.shapeAt(e.latlng)
909
+ if (this._latlngs.indexOf(shape) > 0) {
910
+ items.push({
911
+ text: L._('Make main shape'),
912
+ callback: function () {
913
+ this.enableEdit().deleteShape(shape)
914
+ this.editor.prependShape(shape)
915
+ },
916
+ context: this,
917
+ })
918
+ }
919
+ return items
920
+ },
921
+
922
+ getContextMenuEditItems: function (e) {
923
+ const items = L.U.FeatureMixin.getContextMenuEditItems.call(this, e)
924
+ if (
925
+ this.map.editedFeature &&
926
+ this.isSameClass(this.map.editedFeature) &&
927
+ this.map.editedFeature !== this
928
+ ) {
929
+ items.push({
930
+ text: L._('Transfer shape to edited feature'),
931
+ callback: function () {
932
+ this.transferShape(e.latlng, this.map.editedFeature)
933
+ },
934
+ context: this,
935
+ })
936
+ }
937
+ if (this.isMulti()) {
938
+ items.push({
939
+ text: L._('Extract shape to separate feature'),
940
+ callback: function () {
941
+ this.isolateShape(e.latlng, this.map.editedFeature)
942
+ },
943
+ context: this,
944
+ })
945
+ }
946
+ return items
947
+ },
948
+
949
+ getInplaceToolbarActions: function (e) {
950
+ const items = L.U.FeatureMixin.getInplaceToolbarActions.call(this, e)
951
+ if (this.isMulti()) {
952
+ items.push(L.U.DeleteShapeAction)
953
+ items.push(L.U.ExtractShapeFromMultiAction)
954
+ }
955
+ return items
956
+ },
957
+
958
+ isOnScreen: function (bounds) {
959
+ bounds = bounds || this.map.getBounds()
960
+ return bounds.overlaps(this.getBounds())
961
+ },
962
+
963
+ zoomTo: function (e) {
964
+ // Use bounds instead of centroid for paths.
965
+ e = e || {}
966
+ const easing = e.easing !== undefined ? e.easing : this.map.options.easing
967
+ if (easing) {
968
+ this.map.flyToBounds(this.getBounds(), this.getBestZoom())
969
+ } else {
970
+ this.map.fitBounds(this.getBounds(), this.getBestZoom() || this.map.getZoom())
971
+ }
972
+ if (e.callback) e.callback.call(this)
973
+ },
974
+ }
975
+
976
+ L.U.Polyline = L.Polyline.extend({
977
+ parentClass: L.Polyline,
978
+ includes: [L.U.FeatureMixin, L.U.PathMixin],
979
+
980
+ staticOptions: {
981
+ stroke: true,
982
+ fill: false,
983
+ mainColor: 'color',
984
+ },
985
+
986
+ isSameClass: function (other) {
987
+ return other instanceof L.U.Polyline
988
+ },
989
+
990
+ getClassName: function () {
991
+ return 'polyline'
992
+ },
993
+
994
+ getMeasure: function (shape) {
995
+ const length = L.GeoUtil.lineLength(this.map, shape || this._defaultShape())
996
+ return L.GeoUtil.readableDistance(length, this.map.measureTools.getMeasureUnit())
997
+ },
998
+
999
+ getContextMenuEditItems: function (e) {
1000
+ const items = L.U.PathMixin.getContextMenuEditItems.call(this, e)
1001
+ const vertexClicked = e.vertex
1002
+ let index
1003
+ if (!this.isMulti()) {
1004
+ items.push({
1005
+ text: L._('Transform to polygon'),
1006
+ callback: this.toPolygon,
1007
+ context: this,
1008
+ })
1009
+ }
1010
+ if (vertexClicked) {
1011
+ index = e.vertex.getIndex()
1012
+ if (index !== 0 && index !== e.vertex.getLastIndex()) {
1013
+ items.push({
1014
+ text: L._('Split line'),
1015
+ callback: e.vertex.split,
1016
+ context: e.vertex,
1017
+ })
1018
+ } else if (index === 0 || index === e.vertex.getLastIndex()) {
1019
+ items.push({
1020
+ text: L._('Continue line (Ctrl+Click)'),
1021
+ callback: e.vertex.continue,
1022
+ context: e.vertex.continue,
1023
+ })
1024
+ }
1025
+ }
1026
+ return items
1027
+ },
1028
+
1029
+ getContextMenuMultiItems: function (e) {
1030
+ const items = L.U.PathMixin.getContextMenuMultiItems.call(this, e)
1031
+ items.push({
1032
+ text: L._('Merge lines'),
1033
+ callback: this.mergeShapes,
1034
+ context: this,
1035
+ })
1036
+ return items
1037
+ },
1038
+
1039
+ toPolygon: function () {
1040
+ const geojson = this.toGeoJSON()
1041
+ geojson.geometry.type = 'Polygon'
1042
+ geojson.geometry.coordinates = [
1043
+ L.Util.flattenCoordinates(geojson.geometry.coordinates),
1044
+ ]
1045
+ const polygon = this.datalayer.geojsonToFeatures(geojson)
1046
+ polygon.edit()
1047
+ this.del()
1048
+ },
1049
+
1050
+ getAdvancedEditActions: function (container) {
1051
+ L.U.FeatureMixin.getAdvancedEditActions.call(this, container)
1052
+ const toPolygon = L.DomUtil.createButton(
1053
+ 'button umap-to-polygon',
1054
+ container,
1055
+ L._('Transform to polygon'),
1056
+ this.toPolygon,
1057
+ this
1058
+ )
1059
+ },
1060
+
1061
+ _mergeShapes: function (from, to) {
1062
+ const toLeft = to[0]
1063
+ const toRight = to[to.length - 1]
1064
+ const fromLeft = from[0]
1065
+ const fromRight = from[from.length - 1]
1066
+ const l2ldistance = toLeft.distanceTo(fromLeft)
1067
+ const l2rdistance = toLeft.distanceTo(fromRight)
1068
+ const r2ldistance = toRight.distanceTo(fromLeft)
1069
+ const r2rdistance = toRight.distanceTo(fromRight)
1070
+ let toMerge
1071
+ if (l2rdistance < Math.min(l2ldistance, r2ldistance, r2rdistance)) {
1072
+ toMerge = [from, to]
1073
+ } else if (r2ldistance < Math.min(l2ldistance, l2rdistance, r2rdistance)) {
1074
+ toMerge = [to, from]
1075
+ } else if (r2rdistance < Math.min(l2ldistance, l2rdistance, r2ldistance)) {
1076
+ from.reverse()
1077
+ toMerge = [to, from]
1078
+ } else {
1079
+ from.reverse()
1080
+ toMerge = [from, to]
1081
+ }
1082
+ const a = toMerge[0],
1083
+ b = toMerge[1],
1084
+ p1 = this.map.latLngToContainerPoint(a[a.length - 1]),
1085
+ p2 = this.map.latLngToContainerPoint(b[0]),
1086
+ tolerance = 5 // px on screen
1087
+ if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
1088
+ a.pop()
1089
+ }
1090
+ return a.concat(b)
1091
+ },
1092
+
1093
+ mergeShapes: function () {
1094
+ if (!this.isMulti()) return
1095
+ const latlngs = this.getLatLngs()
1096
+ if (!latlngs.length) return
1097
+ while (latlngs.length > 1) {
1098
+ latlngs.splice(0, 2, this._mergeShapes(latlngs[1], latlngs[0]))
1099
+ }
1100
+ this.setLatLngs(latlngs[0])
1101
+ if (!this.editEnabled()) this.edit()
1102
+ this.editor.reset()
1103
+ this.isDirty = true
1104
+ },
1105
+
1106
+ isMulti: function () {
1107
+ return !L.LineUtil.isFlat(this._latlngs) && this._latlngs.length > 1
1108
+ },
1109
+
1110
+ getVertexActions: function (e) {
1111
+ const actions = L.U.FeatureMixin.getVertexActions.call(this, e),
1112
+ index = e.vertex.getIndex()
1113
+ if (index === 0 || index === e.vertex.getLastIndex())
1114
+ actions.push(L.U.ContinueLineAction)
1115
+ else actions.push(L.U.SplitLineAction)
1116
+ return actions
1117
+ },
1118
+ })
1119
+
1120
+ L.U.Polygon = L.Polygon.extend({
1121
+ parentClass: L.Polygon,
1122
+ includes: [L.U.FeatureMixin, L.U.PathMixin],
1123
+ staticOptions: {
1124
+ mainColor: 'fillColor',
1125
+ },
1126
+
1127
+ isSameClass: function (other) {
1128
+ return other instanceof L.U.Polygon
1129
+ },
1130
+
1131
+ getClassName: function () {
1132
+ return 'polygon'
1133
+ },
1134
+
1135
+ getShapeOptions: function () {
1136
+ const options = L.U.PathMixin.getShapeOptions()
1137
+ options.push(
1138
+ 'properties._umap_options.stroke',
1139
+ 'properties._umap_options.fill',
1140
+ 'properties._umap_options.fillColor',
1141
+ 'properties._umap_options.fillOpacity'
1142
+ )
1143
+ return options
1144
+ },
1145
+
1146
+ getInteractionOptions: function () {
1147
+ const options = L.U.FeatureMixin.getInteractionOptions()
1148
+ options.push('properties._umap_options.interactive')
1149
+ return options
1150
+ },
1151
+
1152
+ getMeasure: function (shape) {
1153
+ const area = L.GeoUtil.geodesicArea(shape || this._defaultShape())
1154
+ return L.GeoUtil.readableArea(area, this.map.measureTools.getMeasureUnit())
1155
+ },
1156
+
1157
+ getContextMenuEditItems: function (e) {
1158
+ const items = L.U.PathMixin.getContextMenuEditItems.call(this, e),
1159
+ shape = this.shapeAt(e.latlng)
1160
+ // No multi and no holes.
1161
+ if (shape && !this.isMulti() && (L.LineUtil.isFlat(shape) || shape.length === 1)) {
1162
+ items.push({
1163
+ text: L._('Transform to lines'),
1164
+ callback: this.toPolyline,
1165
+ context: this,
1166
+ })
1167
+ }
1168
+ items.push({
1169
+ text: L._('Start a hole here'),
1170
+ callback: this.startHole,
1171
+ context: this,
1172
+ })
1173
+ return items
1174
+ },
1175
+
1176
+ startHole: function (e) {
1177
+ this.enableEdit().newHole(e.latlng)
1178
+ },
1179
+
1180
+ toPolyline: function () {
1181
+ const geojson = this.toGeoJSON()
1182
+ geojson.geometry.type = 'LineString'
1183
+ geojson.geometry.coordinates = L.Util.flattenCoordinates(
1184
+ geojson.geometry.coordinates
1185
+ )
1186
+ const polyline = this.datalayer.geojsonToFeatures(geojson)
1187
+ polyline.edit()
1188
+ this.del()
1189
+ },
1190
+
1191
+ getAdvancedEditActions: function (container) {
1192
+ L.U.FeatureMixin.getAdvancedEditActions.call(this, container)
1193
+ const toPolyline = L.DomUtil.createButton(
1194
+ 'button umap-to-polyline',
1195
+ container,
1196
+ L._('Transform to lines'),
1197
+ this.toPolyline,
1198
+ this
1199
+ )
1200
+ },
1201
+
1202
+ isMulti: function () {
1203
+ // Change me when Leaflet#3279 is merged.
1204
+ return (
1205
+ !L.LineUtil.isFlat(this._latlngs) &&
1206
+ !L.LineUtil.isFlat(this._latlngs[0]) &&
1207
+ this._latlngs.length > 1
1208
+ )
1209
+ },
1210
+
1211
+ getInplaceToolbarActions: function (e) {
1212
+ const items = L.U.PathMixin.getInplaceToolbarActions.call(this, e)
1213
+ items.push(L.U.CreateHoleAction)
1214
+ return items
1215
+ },
1216
+ })