umap-project 2.5.0__py3-none-any.whl → 2.6.0__py3-none-any.whl

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

Potentially problematic release.


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

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