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
@@ -0,0 +1,610 @@
1
+ // Goes here all code related to Leaflet, DOM and user interactions.
2
+ import {
3
+ Marker,
4
+ Polyline,
5
+ Polygon,
6
+ CircleMarker as BaseCircleMarker,
7
+ DomUtil,
8
+ LineUtil,
9
+ latLng,
10
+ LatLng,
11
+ LatLngBounds,
12
+ } from '../../../vendors/leaflet/leaflet-src.esm.js'
13
+ import { translate } from '../i18n.js'
14
+ import { uMapAlert as Alert } from '../../components/alerts/alert.js'
15
+ import * as Utils from '../utils.js'
16
+ import * as Icon from './icon.js'
17
+
18
+ const FeatureMixin = {
19
+ initialize: function (feature, latlngs) {
20
+ this.feature = feature
21
+ this.parentClass.prototype.initialize.call(this, latlngs)
22
+ },
23
+
24
+ onAdd: function (map) {
25
+ this.addInteractions()
26
+ return this.parentClass.prototype.onAdd.call(this, map)
27
+ },
28
+
29
+ onRemove: function (map) {
30
+ this.parentClass.prototype.onRemove.call(this, map)
31
+ if (map.editedFeature === this.feature) {
32
+ this.feature._marked_for_deletion = true
33
+ this.feature.endEdit()
34
+ map.editPanel.close()
35
+ }
36
+ },
37
+
38
+ addInteractions: function () {
39
+ this.on('contextmenu editable:vertex:contextmenu', this._showContextMenu)
40
+ this.on('click', this.onClick)
41
+ },
42
+
43
+ onClick: function (event) {
44
+ if (this._map.measureTools?.enabled()) return
45
+ this._popupHandlersAdded = true // Prevent leaflet from managing event
46
+ if (!this._map.editEnabled) {
47
+ this.feature.view(event)
48
+ } else if (!this.feature.isReadOnly()) {
49
+ if (event.originalEvent.shiftKey) {
50
+ if (event.originalEvent.ctrlKey || event.originalEvent.metaKey) {
51
+ this.feature.datalayer.edit(event)
52
+ } else {
53
+ if (this.feature._toggleEditing) this.feature._toggleEditing(event)
54
+ else this.feature.edit(event)
55
+ }
56
+ } else if (!this._map.editTools?.drawing()) {
57
+ new L.Toolbar.Popup(event.latlng, {
58
+ className: 'leaflet-inplace-toolbar',
59
+ anchor: this.getPopupToolbarAnchor(),
60
+ actions: this.feature.getInplaceToolbarActions(event),
61
+ }).addTo(this._map, this.feature, event.latlng)
62
+ }
63
+ }
64
+ L.DomEvent.stop(event)
65
+ },
66
+
67
+ resetTooltip: function () {
68
+ if (!this.feature.hasGeom()) return
69
+ const displayName = this.feature.getDisplayName(null)
70
+ let showLabel = this.feature.getOption('showLabel')
71
+ const oldLabelHover = this.feature.getOption('labelHover')
72
+
73
+ const options = {
74
+ direction: this.feature.getOption('labelDirection'),
75
+ interactive: this.feature.getOption('labelInteractive'),
76
+ }
77
+
78
+ if (oldLabelHover && showLabel) showLabel = null // Retrocompat.
79
+ options.permanent = showLabel === true
80
+ this.unbindTooltip()
81
+ if ((showLabel === true || showLabel === null) && displayName) {
82
+ this.bindTooltip(Utils.escapeHTML(displayName), options)
83
+ }
84
+ },
85
+
86
+ _showContextMenu: function (event) {
87
+ L.DomEvent.stop(event)
88
+ const pt = this._map.mouseEventToContainerPoint(event.originalEvent)
89
+ event.relatedTarget = this
90
+ this._map.contextmenu.showAt(pt, event)
91
+ },
92
+
93
+ getContextMenuItems: function (event) {
94
+ const permalink = this.feature.getPermalink()
95
+ let items = []
96
+ if (permalink) {
97
+ items.push({
98
+ text: translate('Permalink'),
99
+ callback: () => {
100
+ window.open(permalink)
101
+ },
102
+ })
103
+ }
104
+ items.push({
105
+ text: translate('Copy as GeoJSON'),
106
+ callback: () => {
107
+ L.Util.copyToClipboard(JSON.stringify(this.feature.toGeoJSON()))
108
+ this._map.tooltip.open({ content: L._('✅ Copied!') })
109
+ },
110
+ })
111
+ if (this._map.editEnabled && !this.feature.isReadOnly()) {
112
+ items = items.concat(this.getContextMenuEditItems(event))
113
+ }
114
+ return items
115
+ },
116
+
117
+ getContextMenuEditItems: function () {
118
+ let items = ['-']
119
+ if (this._map.editedFeature !== this) {
120
+ items.push({
121
+ text: `${translate('Edit this feature')} (⇧+Click)`,
122
+ callback: this.feature.edit,
123
+ context: this.feature,
124
+ iconCls: 'umap-edit',
125
+ })
126
+ }
127
+ items = items.concat(
128
+ {
129
+ text: this._map.help.displayLabel('EDIT_FEATURE_LAYER'),
130
+ callback: this.feature.datalayer.edit,
131
+ context: this.feature.datalayer,
132
+ iconCls: 'umap-edit',
133
+ },
134
+ {
135
+ text: translate('Delete this feature'),
136
+ callback: this.feature.confirmDelete,
137
+ context: this.feature,
138
+ iconCls: 'umap-delete',
139
+ },
140
+ {
141
+ text: translate('Clone this feature'),
142
+ callback: this.feature.clone,
143
+ context: this.feature,
144
+ }
145
+ )
146
+ return items
147
+ },
148
+
149
+ onCommit: function () {
150
+ this.feature.pullGeometry(false)
151
+ this.feature.onCommit()
152
+ },
153
+
154
+ getPopupToolbarAnchor: () => [0, 0],
155
+ }
156
+
157
+ const PointMixin = {
158
+ isOnScreen: function (bounds) {
159
+ return bounds.contains(this.getCenter())
160
+ },
161
+ }
162
+
163
+ export const LeafletMarker = Marker.extend({
164
+ parentClass: Marker,
165
+ includes: [FeatureMixin, PointMixin],
166
+
167
+ initialize: function (feature, latlng) {
168
+ FeatureMixin.initialize.call(this, feature, latlng)
169
+ this.setIcon(this.getIcon())
170
+ },
171
+
172
+ getClass: () => LeafletMarker,
173
+
174
+ // Make API consistent with path
175
+ getLatLngs: function () {
176
+ return this.getLatLng()
177
+ },
178
+
179
+ setLatLngs: function (latlng) {
180
+ return this.setLatLng(latlng)
181
+ },
182
+
183
+ addInteractions() {
184
+ FeatureMixin.addInteractions.call(this)
185
+ this.on('dragend', (event) => {
186
+ this.isDirty = true
187
+ this.feature.edit(event)
188
+ this.feature.pullGeometry()
189
+ })
190
+ this.on('editable:drawing:commit', this.onCommit)
191
+ if (!this.feature.isReadOnly()) this.on('mouseover', this._enableDragging)
192
+ this.on('mouseout', this._onMouseOut)
193
+ this._popupHandlersAdded = true // prevent Leaflet from binding event on bindPopup
194
+ this.on('popupopen', this.highlight)
195
+ this.on('popupclose', this.resetHighlight)
196
+ },
197
+
198
+ _onMouseOut: function () {
199
+ if (this.dragging?._draggable && !this.dragging._draggable._moving) {
200
+ // Do not disable if the mouse went out while dragging
201
+ this._disableDragging()
202
+ }
203
+ },
204
+
205
+ _enableDragging: function () {
206
+ // TODO: start dragging after 1 second on mouse down
207
+ if (this._map.editEnabled) {
208
+ if (!this.editEnabled()) this.enableEdit()
209
+ // Enabling dragging on the marker override the Draggable._OnDown
210
+ // event, which, as it stopPropagation, refrain the call of
211
+ // _onDown with map-pane element, which is responsible to
212
+ // set the _moved to false, and thus to enable the click.
213
+ // We should find a cleaner way to handle this.
214
+ this._map.dragging._draggable._moved = false
215
+ }
216
+ },
217
+
218
+ _disableDragging: function () {
219
+ if (this._map.editEnabled) {
220
+ if (this.editor?.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
221
+ // do not listen to them
222
+ this.disableEdit()
223
+ }
224
+ },
225
+
226
+ _initIcon: function () {
227
+ this.options.icon = this.getIcon()
228
+ Marker.prototype._initIcon.call(this)
229
+ // Allow to run code when icon is actually part of the DOM
230
+ this.options.icon.onAdd()
231
+ this.resetTooltip()
232
+ },
233
+
234
+ getIconClass: function () {
235
+ return this.feature.getOption('iconClass')
236
+ },
237
+
238
+ getIcon: function () {
239
+ const Class = Icon.getClass(this.getIconClass())
240
+ return new Class({ feature: this.feature })
241
+ },
242
+
243
+ _getTooltipAnchor: function () {
244
+ const anchor = this.options.icon.options.tooltipAnchor.clone()
245
+ const direction = this.feature.getOption('labelDirection')
246
+ if (direction === 'left') {
247
+ anchor.x *= -1
248
+ } else if (direction === 'bottom') {
249
+ anchor.x = 0
250
+ anchor.y = 0
251
+ } else if (direction === 'top') {
252
+ anchor.x = 0
253
+ }
254
+ return anchor
255
+ },
256
+
257
+ _redraw: function () {
258
+ // May no be on the map when in a cluster.
259
+ if (this._map) {
260
+ this._initIcon()
261
+ this.update()
262
+ }
263
+ },
264
+
265
+ getCenter: function () {
266
+ return this._latlng
267
+ },
268
+
269
+ highlight: function () {
270
+ DomUtil.addClass(this.options.icon.elements.main, 'umap-icon-active')
271
+ },
272
+
273
+ resetHighlight: function () {
274
+ DomUtil.removeClass(this.options.icon.elements.main, 'umap-icon-active')
275
+ },
276
+
277
+ getPopupToolbarAnchor: function () {
278
+ return this.options.icon.options.popupAnchor
279
+ },
280
+ })
281
+
282
+ const PathMixin = {
283
+ _onMouseOver: function () {
284
+ if (this._map.measureTools?.enabled()) {
285
+ this._map.tooltip.open({ content: this.getMeasure(), anchor: this })
286
+ } else if (this._map.editEnabled && !this._map.editedFeature) {
287
+ this._map.tooltip.open({ content: translate('Click to edit'), anchor: this })
288
+ }
289
+ },
290
+
291
+ makeGeometryEditable: function () {
292
+ if (this._map.editedFeature !== this.feature) {
293
+ this.disableEdit()
294
+ return
295
+ }
296
+ this._map.once('moveend', this.makeGeometryEditable, this)
297
+ const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
298
+ if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) {
299
+ this._map.tooltip.open({ content: L._('Please zoom in to edit the geometry') })
300
+ this.disableEdit()
301
+ } else {
302
+ this.enableEdit()
303
+ }
304
+ },
305
+
306
+ addInteractions: function () {
307
+ FeatureMixin.addInteractions.call(this)
308
+ this.on('editable:disable', this.onCommit)
309
+ this.on('mouseover', this._onMouseOver)
310
+ this.on('drag editable:drag', this._onDrag)
311
+ this.on('popupopen', this.highlightPath)
312
+ this.on('popupclose', this._redraw)
313
+ },
314
+
315
+ highlightPath: function () {
316
+ this.parentClass.prototype.setStyle.call(this, {
317
+ fillOpacity: Math.sqrt(this.feature.getDynamicOption('fillOpacity', 1.0)),
318
+ opacity: 1.0,
319
+ weight: 1.3 * this.feature.getDynamicOption('weight'),
320
+ })
321
+ },
322
+
323
+ _onDrag: function () {
324
+ if (this._tooltip) this._tooltip.setLatLng(this.getCenter())
325
+ },
326
+
327
+ onAdd: function (map) {
328
+ this._container = null
329
+ FeatureMixin.onAdd.call(this, map)
330
+ this.setStyle()
331
+ if (this.editing?.enabled()) this.editing.addHooks()
332
+ this.resetTooltip()
333
+ this._path.dataset.feature = this.feature.id
334
+ },
335
+
336
+ onRemove: function (map) {
337
+ if (this.editing?.enabled()) this.editing.removeHooks()
338
+ FeatureMixin.onRemove.call(this, map)
339
+ },
340
+
341
+ setStyle: function (options = {}) {
342
+ for (const option of this.getStyleOptions()) {
343
+ options[option] = this.feature.getDynamicOption(option)
344
+ }
345
+ options.pointerEvents = options.interactive ? 'visiblePainted' : 'stroke'
346
+ this.parentClass.prototype.setStyle.call(this, options)
347
+ },
348
+
349
+ _redraw: function () {
350
+ this.setStyle()
351
+ this.resetTooltip()
352
+ },
353
+
354
+ getVertexActions: () => [U.DeleteVertexAction],
355
+
356
+ onVertexRawClick: function (event) {
357
+ new L.Toolbar.Popup(event.latlng, {
358
+ className: 'leaflet-inplace-toolbar',
359
+ actions: this.getVertexActions(event),
360
+ }).addTo(this._map, this, event.latlng, event.vertex)
361
+ },
362
+
363
+ getContextMenuItems: function (event) {
364
+ let items = FeatureMixin.getContextMenuItems.call(this, event)
365
+ items.push({
366
+ text: translate('Display measure'),
367
+ callback: () => Alert.info(this.getMeasure()),
368
+ })
369
+ if (this._map.editEnabled && !this.feature.isReadOnly() && this.feature.isMulti()) {
370
+ items = items.concat(this.getContextMenuMultiItems(event))
371
+ }
372
+ return items
373
+ },
374
+
375
+ getContextMenuMultiItems: function (event) {
376
+ const items = [
377
+ '-',
378
+ {
379
+ text: translate('Remove shape from the multi'),
380
+ callback: () => {
381
+ this.enableEdit().deleteShapeAt(event.latlng)
382
+ },
383
+ },
384
+ ]
385
+ const shape = this.shapeAt(event.latlng)
386
+ if (this._latlngs.indexOf(shape) > 0) {
387
+ items.push({
388
+ text: translate('Make main shape'),
389
+ callback: () => {
390
+ this.enableEdit().deleteShape(shape)
391
+ this.editor.prependShape(shape)
392
+ },
393
+ })
394
+ }
395
+ return items
396
+ },
397
+
398
+ getContextMenuEditItems: function (event) {
399
+ const items = FeatureMixin.getContextMenuEditItems.call(this, event)
400
+ if (
401
+ this._map?.editedFeature !== this.feature &&
402
+ this.feature.isSameClass(this._map.editedFeature)
403
+ ) {
404
+ items.push({
405
+ text: translate('Transfer shape to edited feature'),
406
+ callback: () => {
407
+ this.feature.transferShape(event.latlng, this._map.editedFeature)
408
+ },
409
+ })
410
+ }
411
+ if (this.feature.isMulti()) {
412
+ items.push({
413
+ text: translate('Extract shape to separate feature'),
414
+ callback: () => {
415
+ this.isolateShape(event.latlng)
416
+ },
417
+ })
418
+ }
419
+ return items
420
+ },
421
+
422
+ isolateShape: function (atLatLng) {
423
+ if (!this.feature.isMulti()) return
424
+ const shape = this.enableEdit().deleteShapeAt(atLatLng)
425
+ this.feature.pullGeometry()
426
+ this.disableEdit()
427
+ if (!shape) return
428
+ return this.feature.isolateShape(shape)
429
+ },
430
+
431
+ getStyleOptions: () => [
432
+ 'smoothFactor',
433
+ 'color',
434
+ 'opacity',
435
+ 'stroke',
436
+ 'weight',
437
+ 'fill',
438
+ 'fillColor',
439
+ 'fillOpacity',
440
+ 'dashArray',
441
+ 'interactive',
442
+ ],
443
+
444
+ isOnScreen: function (bounds) {
445
+ return bounds.overlaps(this.getBounds())
446
+ },
447
+ }
448
+
449
+ export const LeafletPolyline = Polyline.extend({
450
+ parentClass: Polyline,
451
+ includes: [FeatureMixin, PathMixin],
452
+
453
+ getClass: () => LeafletPolyline,
454
+
455
+ getVertexActions: function (event) {
456
+ const actions = PathMixin.getVertexActions.call(this, event)
457
+ const index = event.vertex.getIndex()
458
+ if (index === 0 || index === event.vertex.getLastIndex()) {
459
+ actions.push(U.ContinueLineAction)
460
+ } else {
461
+ actions.push(U.SplitLineAction)
462
+ }
463
+ return actions
464
+ },
465
+
466
+ getContextMenuEditItems: function (event) {
467
+ const items = PathMixin.getContextMenuEditItems.call(this, event)
468
+ const vertexClicked = event.vertex
469
+ let index
470
+ if (!this.feature.isMulti()) {
471
+ items.push({
472
+ text: translate('Transform to polygon'),
473
+ callback: this.feature.toPolygon,
474
+ context: this.feature,
475
+ })
476
+ }
477
+ if (vertexClicked) {
478
+ index = event.vertex.getIndex()
479
+ if (index !== 0 && index !== event.vertex.getLastIndex()) {
480
+ items.push({
481
+ text: translate('Split line'),
482
+ callback: event.vertex.split,
483
+ context: event.vertex,
484
+ })
485
+ } else if (index === 0 || index === event.vertex.getLastIndex()) {
486
+ items.push({
487
+ text: this._map.help.displayLabel('CONTINUE_LINE'),
488
+ callback: event.vertex.continue,
489
+ context: event.vertex.continue,
490
+ })
491
+ }
492
+ }
493
+ return items
494
+ },
495
+
496
+ getContextMenuMultiItems: function (event) {
497
+ const items = PathMixin.getContextMenuMultiItems.call(this, event)
498
+ items.push({
499
+ text: translate('Merge lines'),
500
+ callback: this.feature.mergeShapes,
501
+ context: this.feature,
502
+ })
503
+ return items
504
+ },
505
+
506
+ getMeasure: function (shape) {
507
+ // FIXME: compute from data in feature (with TurfJS)
508
+ const length = L.GeoUtil.lineLength(this._map, shape || this._defaultShape())
509
+ return L.GeoUtil.readableDistance(length, this._map.measureTools.getMeasureUnit())
510
+ },
511
+ })
512
+
513
+ export const LeafletPolygon = Polygon.extend({
514
+ parentClass: Polygon,
515
+ includes: [FeatureMixin, PathMixin],
516
+
517
+ getClass: () => LeafletPolygon,
518
+
519
+ getContextMenuEditItems: function (event) {
520
+ const items = PathMixin.getContextMenuEditItems.call(this, event)
521
+ const shape = this.shapeAt(event.latlng)
522
+ // No multi and no holes.
523
+ if (
524
+ shape &&
525
+ !this.feature.isMulti() &&
526
+ (LineUtil.isFlat(shape) || shape.length === 1)
527
+ ) {
528
+ items.push({
529
+ text: translate('Transform to lines'),
530
+ callback: this.feature.toLineString,
531
+ context: this.feature,
532
+ })
533
+ }
534
+ items.push({
535
+ text: translate('Start a hole here'),
536
+ callback: this.startHole,
537
+ context: this,
538
+ })
539
+ return items
540
+ },
541
+
542
+ startHole: function (event) {
543
+ this.enableEdit().newHole(event.latlng)
544
+ },
545
+
546
+ getMeasure: function (shape) {
547
+ const area = L.GeoUtil.geodesicArea(shape || this._defaultShape())
548
+ return L.GeoUtil.readableArea(area, this._map.measureTools.getMeasureUnit())
549
+ },
550
+ })
551
+ const WORLD = [
552
+ latLng([90, 180]),
553
+ latLng([90, -180]),
554
+ latLng([-90, -180]),
555
+ latLng([-90, 180]),
556
+ ]
557
+
558
+ export const MaskPolygon = LeafletPolygon.extend({
559
+ getClass: () => MaskPolygon,
560
+
561
+ getLatLngs: function () {
562
+ // Exclude World coordinates.
563
+ return LeafletPolygon.prototype.getLatLngs.call(this).slice(1)
564
+ },
565
+
566
+ _setLatLngs: function (latlngs) {
567
+ const newLatLngs = []
568
+ newLatLngs.push(WORLD)
569
+
570
+ if (!this.feature.isMulti()) {
571
+ latlngs = [latlngs]
572
+ }
573
+ for (const ring of latlngs) {
574
+ newLatLngs.push(ring)
575
+ }
576
+ LeafletPolygon.prototype._setLatLngs.call(this, newLatLngs)
577
+ this._bounds = new LatLngBounds(latlngs)
578
+ },
579
+
580
+ _defaultShape: function () {
581
+ // Do not compute with world coordinates (eg. for centering the popup).
582
+ return this._latlngs[1]
583
+ },
584
+ })
585
+
586
+ export const CircleMarker = BaseCircleMarker.extend({
587
+ parentClass: BaseCircleMarker,
588
+ includes: [FeatureMixin, PathMixin, PointMixin],
589
+ initialize: function (feature, latlng) {
590
+ if (Array.isArray(latlng) && !(latlng[0] instanceof Number)) {
591
+ // Must be a line or polygon
592
+ const bounds = new LatLngBounds(latlng)
593
+ latlng = bounds.getCenter()
594
+ }
595
+ FeatureMixin.initialize.call(this, feature, latlng)
596
+ },
597
+ getClass: () => CircleMarker,
598
+ getStyleOptions: function () {
599
+ const options = PathMixin.getStyleOptions.call(this)
600
+ options.push('radius')
601
+ return options
602
+ },
603
+ getCenter: function () {
604
+ return this._latlng
605
+ },
606
+ // FIXME when Leaflet.Editable knows about CircleMarker
607
+ editEnabled: () => false,
608
+ enableEdit: () => {}, // No-op
609
+ disableEdit: () => {}, // No-op
610
+ })
@@ -3,6 +3,8 @@ import { translate } from './i18n.js'
3
3
  import * as Utils from './utils.js'
4
4
  import { AutocompleteDatalist } from './autocomplete.js'
5
5
 
6
+ const EMPTY_VALUES = ['', undefined, null]
7
+
6
8
  class Rule {
7
9
  get condition() {
8
10
  return this._condition
@@ -75,13 +77,22 @@ class Rule {
75
77
  if (vars.length !== 2) return
76
78
  this.key = vars[0]
77
79
  this.expected = vars[1]
80
+ if (EMPTY_VALUES.includes(this.expected)) {
81
+ this.cast = (v) => EMPTY_VALUES.includes(v)
82
+ }
78
83
  // Special cases where we want to be lousy when checking isNaN without
79
84
  // coercing to a Number first because we handle multiple types.
80
85
  // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/
81
86
  // Reference/Global_Objects/Number/isNaN
82
87
  // biome-ignore lint/suspicious/noGlobalIsNan: expected might not be a number.
83
- if (!isNaN(this.expected)) this.cast = Number.parseFloat
84
- else if (['true', 'false'].includes(this.expected)) this.cast = (v) => !!v
88
+ else if (!isNaN(this.expected)) {
89
+ this.cast = Number.parseFloat
90
+ } else if (['true', 'false'].includes(this.expected)) {
91
+ this.cast = (v) => {
92
+ if (`${v}`.toLowerCase() === 'true') return true
93
+ if (`${v}`.toLowerCase() === 'false') return false
94
+ }
95
+ }
85
96
  this.expected = this.cast(this.expected)
86
97
  }
87
98
 
@@ -133,7 +144,9 @@ class Rule {
133
144
  autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
134
145
  } else if (value.endsWith('=')) {
135
146
  const key = value.split('!')[0].split('=')[0]
136
- autocomplete.suggestions = this.map.sortedValues(key).map((str) => `${value}${str || ''}`)
147
+ autocomplete.suggestions = this.map
148
+ .sortedValues(key)
149
+ .map((str) => `${value}${str || ''}`)
137
150
  }
138
151
  })
139
152
  this.map.editPanel.open({ content: container })