umap-project 2.7.3__py3-none-any.whl → 2.8.0a0__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.
Files changed (279) hide show
  1. umap/__init__.py +1 -1
  2. umap/forms.py +4 -14
  3. umap/locale/am_ET/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/am_ET/LC_MESSAGES/django.po +278 -151
  5. umap/locale/ar/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/ar/LC_MESSAGES/django.po +335 -141
  7. umap/locale/bg/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/bg/LC_MESSAGES/django.po +279 -152
  9. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/br/LC_MESSAGES/django.po +95 -79
  11. umap/locale/ca/LC_MESSAGES/django.mo +0 -0
  12. umap/locale/ca/LC_MESSAGES/django.po +85 -68
  13. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  14. umap/locale/cs_CZ/LC_MESSAGES/django.po +78 -66
  15. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  16. umap/locale/da/LC_MESSAGES/django.po +280 -153
  17. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  18. umap/locale/de/LC_MESSAGES/django.po +80 -64
  19. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  20. umap/locale/el/LC_MESSAGES/django.po +82 -66
  21. umap/locale/en/LC_MESSAGES/django.po +73 -61
  22. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/es/LC_MESSAGES/django.po +75 -63
  24. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/et/LC_MESSAGES/django.po +280 -153
  26. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/eu/LC_MESSAGES/django.po +82 -66
  28. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/fa_IR/LC_MESSAGES/django.po +80 -64
  30. umap/locale/fi/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/fi/LC_MESSAGES/django.po +278 -151
  32. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/fr/LC_MESSAGES/django.po +75 -63
  34. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/gl/LC_MESSAGES/django.po +280 -153
  36. umap/locale/he/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/he/LC_MESSAGES/django.po +281 -154
  38. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/hu/LC_MESSAGES/django.po +80 -64
  40. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  41. umap/locale/is/LC_MESSAGES/django.po +280 -153
  42. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  43. umap/locale/it/LC_MESSAGES/django.po +82 -66
  44. umap/locale/ja/LC_MESSAGES/django.mo +0 -0
  45. umap/locale/ja/LC_MESSAGES/django.po +280 -153
  46. umap/locale/ko/LC_MESSAGES/django.mo +0 -0
  47. umap/locale/ko/LC_MESSAGES/django.po +280 -153
  48. umap/locale/lt/LC_MESSAGES/django.mo +0 -0
  49. umap/locale/lt/LC_MESSAGES/django.po +280 -153
  50. umap/locale/ms/LC_MESSAGES/django.mo +0 -0
  51. umap/locale/ms/LC_MESSAGES/django.po +82 -66
  52. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  53. umap/locale/nl/LC_MESSAGES/django.po +280 -153
  54. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  55. umap/locale/pl/LC_MESSAGES/django.po +82 -66
  56. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  57. umap/locale/pt/LC_MESSAGES/django.po +75 -63
  58. umap/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  59. umap/locale/pt_BR/LC_MESSAGES/django.po +280 -153
  60. umap/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  61. umap/locale/pt_PT/LC_MESSAGES/django.po +280 -153
  62. umap/locale/ru/LC_MESSAGES/django.mo +0 -0
  63. umap/locale/ru/LC_MESSAGES/django.po +280 -153
  64. umap/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  65. umap/locale/sk_SK/LC_MESSAGES/django.po +280 -153
  66. umap/locale/sl/LC_MESSAGES/django.mo +0 -0
  67. umap/locale/sl/LC_MESSAGES/django.po +280 -153
  68. umap/locale/sr/LC_MESSAGES/django.mo +0 -0
  69. umap/locale/sr/LC_MESSAGES/django.po +280 -153
  70. umap/locale/sv/LC_MESSAGES/django.mo +0 -0
  71. umap/locale/sv/LC_MESSAGES/django.po +81 -65
  72. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  73. umap/locale/th_TH/LC_MESSAGES/django.po +257 -185
  74. umap/locale/tr/LC_MESSAGES/django.mo +0 -0
  75. umap/locale/tr/LC_MESSAGES/django.po +280 -153
  76. umap/locale/uk_UA/LC_MESSAGES/django.mo +0 -0
  77. umap/locale/uk_UA/LC_MESSAGES/django.po +280 -153
  78. umap/locale/vi/LC_MESSAGES/django.mo +0 -0
  79. umap/locale/vi/LC_MESSAGES/django.po +278 -151
  80. umap/locale/zh/LC_MESSAGES/django.mo +0 -0
  81. umap/locale/zh/LC_MESSAGES/django.po +278 -151
  82. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  83. umap/locale/zh_TW/LC_MESSAGES/django.po +97 -81
  84. umap/management/commands/empty_trash.py +32 -0
  85. umap/management/commands/migrate_to_S3.py +29 -0
  86. umap/migrations/0023_alter_datalayer_uuid.py +19 -0
  87. umap/migrations/0024_alter_map_share_status.py +30 -0
  88. umap/migrations/0025_alter_datalayer_geojson.py +24 -0
  89. umap/models.py +68 -116
  90. umap/settings/base.py +22 -2
  91. umap/static/umap/base.css +3 -603
  92. umap/static/umap/content.css +5 -3
  93. umap/static/umap/css/bar.css +202 -0
  94. umap/static/umap/css/form.css +617 -0
  95. umap/static/umap/css/icon.css +21 -1
  96. umap/static/umap/css/popup.css +125 -0
  97. umap/static/umap/img/16-white.svg +16 -4
  98. umap/static/umap/img/16.svg +1 -1
  99. umap/static/umap/img/source/16-white.svg +46 -45
  100. umap/static/umap/img/source/16.svg +1 -753
  101. umap/static/umap/js/components/fragment.js +3 -1
  102. umap/static/umap/js/modules/browser.js +20 -19
  103. umap/static/umap/js/modules/caption.js +21 -22
  104. umap/static/umap/js/modules/data/features.js +101 -74
  105. umap/static/umap/js/modules/data/layer.js +157 -137
  106. umap/static/umap/js/modules/facets.js +9 -9
  107. umap/static/umap/js/modules/formatter.js +5 -5
  108. umap/static/umap/js/modules/global.js +4 -52
  109. umap/static/umap/js/modules/help.js +18 -21
  110. umap/static/umap/js/modules/importer.js +71 -39
  111. umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
  112. umap/static/umap/js/modules/importers/geodatamine.js +3 -3
  113. umap/static/umap/js/modules/importers/overpass.js +5 -0
  114. umap/static/umap/js/modules/permissions.js +85 -87
  115. umap/static/umap/js/modules/rendering/layers/base.js +15 -15
  116. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  117. umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
  118. umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
  119. umap/static/umap/js/modules/rendering/map.js +390 -0
  120. umap/static/umap/js/modules/rendering/popup.js +10 -9
  121. umap/static/umap/js/modules/rendering/template.js +35 -12
  122. umap/static/umap/js/modules/rendering/ui.js +57 -12
  123. umap/static/umap/js/modules/rules.js +22 -25
  124. umap/static/umap/js/modules/saving.js +47 -0
  125. umap/static/umap/js/modules/schema.js +5 -0
  126. umap/static/umap/js/modules/share.js +21 -24
  127. umap/static/umap/js/modules/slideshow.js +24 -20
  128. umap/static/umap/js/modules/sync/updaters.js +7 -9
  129. umap/static/umap/js/modules/tableeditor.js +20 -19
  130. umap/static/umap/js/modules/ui/bar.js +196 -0
  131. umap/static/umap/js/modules/ui/panel.js +10 -9
  132. umap/static/umap/js/modules/umap.js +1668 -0
  133. umap/static/umap/js/modules/urls.js +2 -2
  134. umap/static/umap/js/modules/utils.js +20 -6
  135. umap/static/umap/js/umap.controls.js +74 -301
  136. umap/static/umap/js/umap.core.js +29 -50
  137. umap/static/umap/js/umap.forms.js +34 -27
  138. umap/static/umap/keycloak.png +0 -0
  139. umap/static/umap/locale/am_ET.js +26 -10
  140. umap/static/umap/locale/am_ET.json +26 -10
  141. umap/static/umap/locale/ar.js +26 -10
  142. umap/static/umap/locale/ar.json +26 -10
  143. umap/static/umap/locale/ast.js +26 -10
  144. umap/static/umap/locale/ast.json +26 -10
  145. umap/static/umap/locale/bg.js +26 -10
  146. umap/static/umap/locale/bg.json +26 -10
  147. umap/static/umap/locale/br.js +27 -20
  148. umap/static/umap/locale/br.json +27 -20
  149. umap/static/umap/locale/ca.js +32 -29
  150. umap/static/umap/locale/ca.json +32 -29
  151. umap/static/umap/locale/cs_CZ.js +24 -17
  152. umap/static/umap/locale/cs_CZ.json +24 -17
  153. umap/static/umap/locale/da.js +26 -10
  154. umap/static/umap/locale/da.json +26 -10
  155. umap/static/umap/locale/de.js +21 -14
  156. umap/static/umap/locale/de.json +21 -14
  157. umap/static/umap/locale/el.js +28 -12
  158. umap/static/umap/locale/el.json +28 -12
  159. umap/static/umap/locale/en.js +12 -9
  160. umap/static/umap/locale/en.json +12 -9
  161. umap/static/umap/locale/en_US.json +26 -10
  162. umap/static/umap/locale/es.js +16 -13
  163. umap/static/umap/locale/es.json +16 -13
  164. umap/static/umap/locale/et.js +26 -10
  165. umap/static/umap/locale/et.json +26 -10
  166. umap/static/umap/locale/eu.js +16 -9
  167. umap/static/umap/locale/eu.json +16 -9
  168. umap/static/umap/locale/fa_IR.js +16 -9
  169. umap/static/umap/locale/fa_IR.json +16 -9
  170. umap/static/umap/locale/fi.js +26 -10
  171. umap/static/umap/locale/fi.json +26 -10
  172. umap/static/umap/locale/fr.js +12 -9
  173. umap/static/umap/locale/fr.json +12 -9
  174. umap/static/umap/locale/gl.js +26 -10
  175. umap/static/umap/locale/gl.json +26 -10
  176. umap/static/umap/locale/he.js +26 -10
  177. umap/static/umap/locale/he.json +26 -10
  178. umap/static/umap/locale/hr.js +26 -10
  179. umap/static/umap/locale/hr.json +26 -10
  180. umap/static/umap/locale/hu.js +16 -9
  181. umap/static/umap/locale/hu.json +16 -9
  182. umap/static/umap/locale/id.js +26 -10
  183. umap/static/umap/locale/id.json +26 -10
  184. umap/static/umap/locale/is.js +26 -10
  185. umap/static/umap/locale/is.json +26 -10
  186. umap/static/umap/locale/it.js +26 -10
  187. umap/static/umap/locale/it.json +26 -10
  188. umap/static/umap/locale/ja.js +26 -10
  189. umap/static/umap/locale/ja.json +26 -10
  190. umap/static/umap/locale/ko.js +26 -10
  191. umap/static/umap/locale/ko.json +26 -10
  192. umap/static/umap/locale/lt.js +26 -10
  193. umap/static/umap/locale/lt.json +26 -10
  194. umap/static/umap/locale/ms.js +28 -12
  195. umap/static/umap/locale/ms.json +28 -12
  196. umap/static/umap/locale/nl.js +28 -12
  197. umap/static/umap/locale/nl.json +28 -12
  198. umap/static/umap/locale/no.js +26 -10
  199. umap/static/umap/locale/no.json +26 -10
  200. umap/static/umap/locale/pl.js +28 -12
  201. umap/static/umap/locale/pl.json +28 -12
  202. umap/static/umap/locale/pl_PL.json +26 -10
  203. umap/static/umap/locale/pt.js +16 -9
  204. umap/static/umap/locale/pt.json +16 -9
  205. umap/static/umap/locale/pt_BR.js +26 -10
  206. umap/static/umap/locale/pt_BR.json +26 -10
  207. umap/static/umap/locale/pt_PT.js +16 -9
  208. umap/static/umap/locale/pt_PT.json +16 -9
  209. umap/static/umap/locale/ro.js +26 -10
  210. umap/static/umap/locale/ro.json +26 -10
  211. umap/static/umap/locale/ru.js +26 -10
  212. umap/static/umap/locale/ru.json +26 -10
  213. umap/static/umap/locale/si.js +7 -7
  214. umap/static/umap/locale/si.json +7 -7
  215. umap/static/umap/locale/sk_SK.js +26 -10
  216. umap/static/umap/locale/sk_SK.json +26 -10
  217. umap/static/umap/locale/sl.js +26 -10
  218. umap/static/umap/locale/sl.json +26 -10
  219. umap/static/umap/locale/sr.js +26 -10
  220. umap/static/umap/locale/sr.json +26 -10
  221. umap/static/umap/locale/sv.js +27 -11
  222. umap/static/umap/locale/sv.json +27 -11
  223. umap/static/umap/locale/th_TH.js +28 -12
  224. umap/static/umap/locale/th_TH.json +28 -12
  225. umap/static/umap/locale/tr.js +26 -10
  226. umap/static/umap/locale/tr.json +26 -10
  227. umap/static/umap/locale/uk_UA.js +26 -10
  228. umap/static/umap/locale/uk_UA.json +26 -10
  229. umap/static/umap/locale/vi.js +26 -10
  230. umap/static/umap/locale/vi.json +26 -10
  231. umap/static/umap/locale/vi_VN.json +26 -10
  232. umap/static/umap/locale/zh.js +26 -10
  233. umap/static/umap/locale/zh.json +26 -10
  234. umap/static/umap/locale/zh_CN.json +26 -10
  235. umap/static/umap/locale/zh_TW.Big5.json +26 -10
  236. umap/static/umap/locale/zh_TW.js +34 -27
  237. umap/static/umap/locale/zh_TW.json +34 -27
  238. umap/static/umap/map.css +5 -364
  239. umap/static/umap/unittests/URLs.js +15 -15
  240. umap/static/umap/unittests/utils.js +23 -1
  241. umap/static/umap/vars.css +2 -0
  242. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
  243. umap/storage.py +152 -0
  244. umap/templates/registration/login.html +7 -6
  245. umap/templates/umap/css.html +3 -0
  246. umap/templates/umap/js.html +1 -2
  247. umap/templates/umap/map_init.html +4 -5
  248. umap/templates/umap/user_dashboard.html +18 -19
  249. umap/tests/base.py +5 -1
  250. umap/tests/integration/conftest.py +2 -1
  251. umap/tests/integration/test_anonymous_owned_map.py +18 -10
  252. umap/tests/integration/test_browser.py +16 -1
  253. umap/tests/integration/test_dashboard.py +1 -1
  254. umap/tests/integration/test_edit_datalayer.py +18 -7
  255. umap/tests/integration/test_import.py +8 -5
  256. umap/tests/integration/test_optimistic_merge.py +31 -8
  257. umap/tests/integration/test_owned_map.py +22 -16
  258. umap/tests/integration/test_popup.py +44 -0
  259. umap/tests/integration/test_save.py +35 -0
  260. umap/tests/integration/test_view_marker.py +12 -0
  261. umap/tests/integration/test_view_polyline.py +257 -0
  262. umap/tests/integration/test_websocket_sync.py +81 -9
  263. umap/tests/test_datalayer.py +6 -7
  264. umap/tests/test_datalayer_s3.py +135 -0
  265. umap/tests/test_datalayer_views.py +28 -10
  266. umap/tests/test_empty_trash.py +34 -0
  267. umap/tests/test_map.py +12 -3
  268. umap/tests/test_map_views.py +69 -37
  269. umap/tests/test_views.py +53 -0
  270. umap/urls.py +3 -3
  271. umap/views.py +107 -76
  272. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/METADATA +16 -13
  273. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/RECORD +276 -262
  274. umap/management/commands/purge_purgatory.py +0 -28
  275. umap/static/umap/js/umap.js +0 -1903
  276. umap/tests/test_purge_purgatory.py +0 -25
  277. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/WHEEL +0 -0
  278. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/entry_points.txt +0 -0
  279. {umap_project-2.7.3.dist-info → umap_project-2.8.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,390 @@
1
+ // Goes here all code related to Leaflet, DOM and user interactions.
2
+ import {
3
+ Map as BaseMap,
4
+ DomUtil,
5
+ DomEvent,
6
+ latLngBounds,
7
+ latLng,
8
+ Control,
9
+ setOptions,
10
+ } from '../../../vendors/leaflet/leaflet-src.esm.js'
11
+ import { translate } from '../i18n.js'
12
+ import { uMapAlert as Alert } from '../../components/alerts/alert.js'
13
+ import * as Utils from '../utils.js'
14
+ import * as Icon from './icon.js'
15
+
16
+ // Those options are not saved on the server, so they can live here
17
+ // instead of in umap.properties
18
+ BaseMap.mergeOptions({
19
+ demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' },
20
+ attributionControl: false,
21
+ })
22
+
23
+ const ControlsMixin = {
24
+ HIDDABLE_CONTROLS: [
25
+ 'zoom',
26
+ 'search',
27
+ 'fullscreen',
28
+ 'embed',
29
+ 'datalayers',
30
+ 'caption',
31
+ 'locate',
32
+ 'measure',
33
+ 'editinosm',
34
+ 'star',
35
+ 'tilelayers',
36
+ ],
37
+
38
+ initControls: function () {
39
+ this._controls = {}
40
+
41
+ if (this._umap.hasEditMode() && !this.options.noControl) {
42
+ new U.EditControl(this).addTo(this)
43
+
44
+ new U.DrawToolbar({ map: this }).addTo(this)
45
+ const editActions = [
46
+ U.EditCaptionAction,
47
+ U.ImportAction,
48
+ U.EditLayersAction,
49
+ U.ChangeTileLayerAction,
50
+ U.UpdateExtentAction,
51
+ U.UpdatePermsAction,
52
+ U.EditPropertiesAction,
53
+ ]
54
+ if (this.options.editMode === 'advanced') {
55
+ new U.SettingsToolbar({ actions: editActions }).addTo(this)
56
+ }
57
+ }
58
+ this._controls.zoom = new Control.Zoom({
59
+ zoomInTitle: translate('Zoom in'),
60
+ zoomOutTitle: translate('Zoom out'),
61
+ })
62
+ this._controls.datalayers = new U.DataLayersControl(this._umap)
63
+ this._controls.caption = new U.CaptionControl(this._umap)
64
+ this._controls.locate = new U.Locate(this, {
65
+ strings: {
66
+ title: translate('Center map on your location'),
67
+ },
68
+ showPopup: false,
69
+ // We style this control in our own CSS for consistency with other controls,
70
+ // but the control breaks if we don't specify a class here, so a fake class
71
+ // will do.
72
+ icon: 'umap-fake-class',
73
+ iconLoading: 'umap-fake-class',
74
+ flyTo: this.options.easing,
75
+ onLocationError: (err) => U.Alert.error(err.message),
76
+ })
77
+ this._controls.fullscreen = new Control.Fullscreen({
78
+ title: {
79
+ false: translate('View Fullscreen'),
80
+ true: translate('Exit Fullscreen'),
81
+ },
82
+ })
83
+ this._controls.search = new U.SearchControl()
84
+ this._controls.embed = new Control.Embed(this._umap)
85
+ this._controls.tilelayersChooser = new U.TileLayerChooser(this)
86
+ if (this.options.user?.id) this._controls.star = new U.StarControl(this._umap)
87
+ this._controls.editinosm = new Control.EditInOSM({
88
+ position: 'topleft',
89
+ widgetOptions: {
90
+ helpText: translate(
91
+ 'Open this map extent in a map editor to provide more accurate data to OpenStreetMap'
92
+ ),
93
+ },
94
+ })
95
+ this._controls.measure = new L.MeasureControl().initHandler(this)
96
+ this._controls.more = new U.MoreControls()
97
+ this._controls.scale = L.control.scale()
98
+ this._controls.permanentCredit = new U.PermanentCreditsControl(this)
99
+ this._umap.drop = new U.DropControl(this)
100
+ this._controls.tilelayers = new U.TileLayerControl(this)
101
+ },
102
+
103
+ renderControls: function () {
104
+ for (const control of Object.values(this._controls)) {
105
+ this.removeControl(control)
106
+ }
107
+ if (this.options.noControl) return
108
+
109
+ this._controls.attribution = new U.AttributionControl().addTo(this)
110
+ if (this.options.miniMap) {
111
+ this.whenReady(function () {
112
+ if (this.selectedTilelayer) {
113
+ this._controls.miniMap = new Control.MiniMap(this.selectedTilelayer, {
114
+ aimingRectOptions: {
115
+ color: this._umap.getProperty('color'),
116
+ fillColor: this._umap.getProperty('fillColor'),
117
+ stroke: this._umap.getProperty('stroke'),
118
+ fill: this._umap.getProperty('fill'),
119
+ weight: this._umap.getProperty('weight'),
120
+ opacity: this._umap.getProperty('opacity'),
121
+ fillOpacity: this._umap.getProperty('fillOpacity'),
122
+ },
123
+ }).addTo(this)
124
+ this._controls.miniMap._miniMap.invalidateSize()
125
+ }
126
+ })
127
+ }
128
+ for (const name of this.HIDDABLE_CONTROLS) {
129
+ const status = this._umap.getProperty(`${name}Control`)
130
+ if (status === false) continue
131
+ const control = this._controls[name]
132
+ if (!control) continue
133
+ control.addTo(this)
134
+ if (status === undefined || status === null) {
135
+ DomUtil.addClass(control._container, 'display-on-more')
136
+ } else {
137
+ DomUtil.removeClass(control._container, 'display-on-more')
138
+ }
139
+ }
140
+ if (this._umap.getProperty('permanentCredit'))
141
+ this._controls.permanentCredit.addTo(this)
142
+ if (this._umap.getProperty('moreControl')) this._controls.more.addTo(this)
143
+ if (this._umap.getProperty('scaleControl')) this._controls.scale.addTo(this)
144
+ this._controls.tilelayers.setLayers()
145
+ },
146
+ }
147
+
148
+ const ManageTilelayerMixin = {
149
+ initTileLayers: function () {
150
+ this.tilelayers = []
151
+ for (const props of this.options.tilelayers) {
152
+ const layer = this.createTileLayer(props)
153
+ this.tilelayers.push(layer)
154
+ if (
155
+ this.options.tilelayer &&
156
+ this.options.tilelayer.url_template === props.url_template
157
+ ) {
158
+ // Keep control over the displayed attribution for non custom tilelayers
159
+ this.options.tilelayer.attribution = props.attribution
160
+ }
161
+ }
162
+ if (this.options.tilelayer?.url_template && this.options.tilelayer.attribution) {
163
+ this.customTilelayer = this.createTileLayer(this.options.tilelayer)
164
+ this.selectTileLayer(this.customTilelayer)
165
+ } else {
166
+ this.selectTileLayer(this.tilelayers[0])
167
+ }
168
+ if (this._controls) this._controls.tilelayers.setLayers()
169
+ },
170
+
171
+ createTileLayer: (tilelayer) => new L.TileLayer(tilelayer.url_template, tilelayer),
172
+
173
+ selectTileLayer: function (tilelayer) {
174
+ if (tilelayer === this.selectedTilelayer) {
175
+ return
176
+ }
177
+ try {
178
+ this.addLayer(tilelayer)
179
+ this.fire('baselayerchange', { layer: tilelayer })
180
+ if (this.selectedTilelayer) {
181
+ this.removeLayer(this.selectedTilelayer)
182
+ }
183
+ this.selectedTilelayer = tilelayer
184
+ if (
185
+ !Number.isNaN(this.selectedTilelayer.options.minZoom) &&
186
+ this.getZoom() < this.selectedTilelayer.options.minZoom
187
+ ) {
188
+ this.setZoom(this.selectedTilelayer.options.minZoom)
189
+ }
190
+ if (
191
+ !Number.isNaN(this.selectedTilelayer.options.maxZoom) &&
192
+ this.getZoom() > this.selectedTilelayer.options.maxZoom
193
+ ) {
194
+ this.setZoom(this.selectedTilelayer.options.maxZoom)
195
+ }
196
+ } catch (e) {
197
+ console.error(e)
198
+ this.removeLayer(tilelayer)
199
+ Alert.error(`${translate('Error in the tilelayer URL')}: ${tilelayer._url}`)
200
+ // Users can put tilelayer URLs by hand, and if they add wrong {variable},
201
+ // Leaflet throw an error, and then the map is no more editable
202
+ }
203
+ this.setOverlay()
204
+ },
205
+
206
+ eachTileLayer: function (callback, context) {
207
+ const urls = []
208
+ const callOne = (layer) => {
209
+ // Prevent adding a duplicate background,
210
+ // while adding selected/custom on top of the list
211
+ const url = layer.options.url_template
212
+ if (urls.indexOf(url) !== -1) return
213
+ callback.call(context, layer)
214
+ urls.push(url)
215
+ }
216
+ if (this.selectedTilelayer) callOne(this.selectedTilelayer)
217
+ if (this.customTilelayer) callOne(this.customTilelayer)
218
+ this.tilelayers.forEach(callOne)
219
+ },
220
+
221
+ setOverlay: function () {
222
+ if (!this.options.overlay || !this.options.overlay.url_template) return
223
+ const overlay = this.createTileLayer(this.options.overlay)
224
+ try {
225
+ this.addLayer(overlay)
226
+ if (this.overlay) this.removeLayer(this.overlay)
227
+ this.overlay = overlay
228
+ } catch (e) {
229
+ this.removeLayer(overlay)
230
+ console.error(e)
231
+ Alert.error(`${translate('Error in the overlay URL')}: ${overlay._url}`)
232
+ }
233
+ },
234
+
235
+ updateTileLayers: function () {
236
+ const callback = (tilelayer) => {
237
+ this.options.tilelayer = tilelayer.toJSON()
238
+ this._umap.isDirty = true
239
+ }
240
+ if (this._controls.tilelayersChooser) {
241
+ this._controls.tilelayersChooser.openSwitcher({ callback, edit: true })
242
+ }
243
+ },
244
+ }
245
+
246
+ export const LeafletMap = BaseMap.extend({
247
+ includes: [ControlsMixin, ManageTilelayerMixin],
248
+
249
+ // The initialize and the setup method might seem similar, but they
250
+ // serve two different purposes:
251
+ // initialize is for Leaflet internal, when we do "new LeafletMap",
252
+ // while setup is the public API for the LeafletMap to actually
253
+ // render to the DOM.
254
+ initialize: function (umap, element) {
255
+ this._umap = umap
256
+ const options = this._umap.properties
257
+
258
+ BaseMap.prototype.initialize.call(this, element, options)
259
+
260
+ // After calling parent initialize, as we are doing initCenter our-selves
261
+
262
+ this.loader = new Control.Loading()
263
+ this.loader.onAdd(this)
264
+
265
+ if (!this.options.noControl) {
266
+ DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e))
267
+ DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e))
268
+ this.on('click', this.closeInplaceToolbar)
269
+ }
270
+
271
+ this.on('baselayerchange', (e) => {
272
+ if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
273
+ })
274
+ },
275
+
276
+ setup: function () {
277
+ this.initControls()
278
+ // Needs locate control and hash to exist
279
+ this.initCenter()
280
+ this.initTileLayers()
281
+ this.renderUI()
282
+ },
283
+
284
+ renderUI: function () {
285
+ setOptions(this, this._umap.properties)
286
+ if (this.options.scrollWheelZoom) {
287
+ this.scrollWheelZoom.enable()
288
+ this.dragging.enable()
289
+ } else {
290
+ this.scrollWheelZoom.disable()
291
+ // In mobile, do not let the user move the map
292
+ // when scrolling the main page and touching the
293
+ // map in an iframe. May be a bit dumb, but let's
294
+ // try like this for now.
295
+ if (L.Browser.mobile) this.dragging.disable()
296
+ }
297
+ // Needs tilelayer to exist for minimap
298
+ this.renderControls()
299
+ this.handleLimitBounds()
300
+ },
301
+
302
+ closeInplaceToolbar: function () {
303
+ const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id]
304
+ if (toolbar) toolbar.remove()
305
+ },
306
+
307
+ latLng: (a, b, c) => {
308
+ // manage geojson case and call original method
309
+ if (!(a instanceof L.LatLng) && a.coordinates) {
310
+ // Guess it's a geojson
311
+ a = [a.coordinates[1], a.coordinates[0]]
312
+ }
313
+ return latLng(a, b, c)
314
+ },
315
+
316
+ _setDefaultCenter: function () {
317
+ this.options.center = this.latLng(this.options.center)
318
+ this.setView(this.options.center, this.options.zoom)
319
+ },
320
+
321
+ initCenter: function () {
322
+ this._setDefaultCenter()
323
+ if (this.options.hash) this.addHash()
324
+ if (this.options.hash && this._hash.parseHash(location.hash)) {
325
+ // FIXME An invalid hash will cause the load to fail
326
+ this._hash.update()
327
+ } else if (this.options.defaultView === 'locate' && !this.options.noControl) {
328
+ this._controls.locate.start()
329
+ } else if (this.options.defaultView === 'data') {
330
+ this._umap.onceDataLoaded(this._umap.fitDataBounds)
331
+ } else if (this.options.defaultView === 'latest') {
332
+ this._umap.onceDataLoaded(() => {
333
+ if (!this._umap.hasData()) return
334
+ const datalayer = this._umap.firstVisibleDatalayer()
335
+ let feature
336
+ if (datalayer) {
337
+ const feature = datalayer.getFeatureByIndex(-1)
338
+ if (feature) {
339
+ feature.zoomTo({ callback: this.options.noControl ? null : feature.view })
340
+ return
341
+ }
342
+ }
343
+ })
344
+ }
345
+ },
346
+
347
+ handleLimitBounds: function () {
348
+ const south = Number.parseFloat(this.options.limitBounds.south)
349
+ const west = Number.parseFloat(this.options.limitBounds.west)
350
+ const north = Number.parseFloat(this.options.limitBounds.north)
351
+ const east = Number.parseFloat(this.options.limitBounds.east)
352
+ if (
353
+ !Number.isNaN(south) &&
354
+ !Number.isNaN(west) &&
355
+ !Number.isNaN(north) &&
356
+ !Number.isNaN(east)
357
+ ) {
358
+ const bounds = latLngBounds([
359
+ [south, west],
360
+ [north, east],
361
+ ])
362
+ this.options.minZoom = this.getBoundsZoom(bounds, false)
363
+ try {
364
+ this.setMaxBounds(bounds)
365
+ } catch (e) {
366
+ // Unusable bounds, like -2 -2 -2 -2?
367
+ console.error('Error limiting bounds', e)
368
+ }
369
+ } else {
370
+ this.options.minZoom = 0
371
+ this.setMaxBounds()
372
+ }
373
+ },
374
+
375
+ setMaxBounds: function (bounds) {
376
+ // Hack. Remove me when fix is released:
377
+ // https://github.com/Leaflet/Leaflet/pull/4494
378
+ bounds = latLngBounds(bounds)
379
+
380
+ if (!bounds.isValid()) {
381
+ this.options.maxBounds = null
382
+ return this.off('moveend', this._panInsideMaxBounds)
383
+ }
384
+ return BaseMap.prototype.setMaxBounds.call(this, bounds)
385
+ },
386
+
387
+ initEditTools: function () {
388
+ this.editTools = new U.Editable(this._umap)
389
+ },
390
+ })
@@ -44,7 +44,8 @@ const Popup = BasePopup.extend({
44
44
  DomEvent.on(el, 'load', () => {
45
45
  this._updateLayout()
46
46
  this._updatePosition()
47
- this._adjustPan()
47
+ // Do not call when feature is in cluster.
48
+ if (this._map) this._adjustPan()
48
49
  })
49
50
  },
50
51
  })
@@ -61,15 +62,15 @@ const Panel = Popup.extend({
61
62
  zoomAnimation: false,
62
63
  },
63
64
 
64
- onAdd: function (map) {
65
- map.panel.setDefaultMode('expanded')
66
- map.panel.open({
65
+ onAdd: function (leafletMap) {
66
+ leafletMap._umap.panel.setDefaultMode('expanded')
67
+ leafletMap._umap.panel.open({
67
68
  content: this._content,
68
- actions: [Browser.backButton(map)],
69
+ actions: [Browser.backButton(leafletMap._umap)],
69
70
  })
70
71
 
71
72
  // fire events as in base class Popup.js:onAdd
72
- map.fire('popupopen', { popup: this })
73
+ leafletMap.fire('popupopen', { popup: this })
73
74
  if (this._source) {
74
75
  this._source.fire('popupopen', { popup: this }, true)
75
76
  if (!(this._source instanceof Path)) {
@@ -78,11 +79,11 @@ const Panel = Popup.extend({
78
79
  }
79
80
  },
80
81
 
81
- onRemove: function (map) {
82
- map.panel.close()
82
+ onRemove: function (leafletMap) {
83
+ leafletMap._umap.panel.close()
83
84
 
84
85
  // fire events as in base class Popup.js:onRemove
85
- map.fire('popupclose', { popup: this })
86
+ leafletMap.fire('popupclose', { popup: this })
86
87
  if (this._source) {
87
88
  this._source.fire('popupclose', { popup: this }, true)
88
89
  if (!(this._source instanceof Path)) {
@@ -35,6 +35,7 @@ class PopupTemplate {
35
35
  feature.properties.description || '',
36
36
  properties
37
37
  )
38
+ properties.name = properties.name ?? feature.getDisplayName()
38
39
  let content = Utils.greedyTemplate(template, properties)
39
40
  content = Utils.toHTML(content, { target: target })
40
41
  return Utils.loadTemplate(`<div class="umap-popup-container text">${content}</div>`)
@@ -59,7 +60,7 @@ class PopupTemplate {
59
60
  feature: nextFeature.properties.name || translate('next'),
60
61
  })
61
62
  DomEvent.on(next, 'click', () => {
62
- nextFeature.zoomTo({ callback: nextFeature.view })
63
+ nextFeature.zoomTo({ callback: (event) => nextFeature.view(event) })
63
64
  })
64
65
  }
65
66
  if (previousFeature) {
@@ -67,7 +68,7 @@ class PopupTemplate {
67
68
  feature: previousFeature.properties.name || translate('previous'),
68
69
  })
69
70
  DomEvent.on(previous, 'click', () => {
70
- previousFeature.zoomTo({ callback: previousFeature.view })
71
+ previousFeature.zoomTo({ callback: (event) => previousFeature.view(event) })
71
72
  })
72
73
  }
73
74
  DomEvent.on(zoom, 'click', () => feature.zoomTo())
@@ -114,7 +115,9 @@ class Table extends TitleMixin(PopupTemplate) {
114
115
  const table = document.createElement('table')
115
116
 
116
117
  for (const key in feature.properties) {
117
- if (typeof feature.properties[key] === 'object' || key === 'name') continue
118
+ if (typeof feature.properties[key] === 'object' || U.LABEL_KEYS.includes(key)) {
119
+ continue
120
+ }
118
121
  table.appendChild(this.makeRow(feature, key))
119
122
  }
120
123
  return table
@@ -149,6 +152,19 @@ class GeoRSSLink extends PopupTemplate {
149
152
  }
150
153
 
151
154
  class OSM extends TitleMixin(PopupTemplate) {
155
+ renderTitle(feature) {
156
+ const title = DomUtil.add('h3', 'popup-title')
157
+ const color = feature.getPreviewColor()
158
+ title.style.backgroundColor = color
159
+ const iconUrl = feature.getDynamicOption('iconUrl')
160
+ const icon = Icon.makeElement(iconUrl, title)
161
+ DomUtil.addClass(icon, 'icon')
162
+ Icon.setContrast(icon, title, iconUrl, color)
163
+ if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
164
+ DomUtil.add('span', '', title, this.getName(feature))
165
+ return title
166
+ }
167
+
152
168
  getName(feature) {
153
169
  const props = feature.properties
154
170
  const locale = getLocale()
@@ -159,15 +175,7 @@ class OSM extends TitleMixin(PopupTemplate) {
159
175
  renderBody(feature) {
160
176
  const props = feature.properties
161
177
  const body = document.createElement('div')
162
- const title = DomUtil.add('h3', 'popup-title', container)
163
- const color = feature.getPreviewColor()
164
- title.style.backgroundColor = color
165
- const iconUrl = feature.getDynamicOption('iconUrl')
166
- const icon = Icon.makeElement(iconUrl, title)
167
- DomUtil.addClass(icon, 'icon')
168
- Icon.setContrast(icon, title, iconUrl, color)
169
- if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
170
- DomUtil.add('span', '', title, this.getName(feature))
178
+ const locale = getLocale()
171
179
  const street = props['addr:street']
172
180
  if (street) {
173
181
  const row = DomUtil.add('address', 'address', body)
@@ -204,6 +212,21 @@ class OSM extends TitleMixin(PopupTemplate) {
204
212
  Utils.loadTemplate(`<div><a href="mailto:${email}">${email}</a></div>`)
205
213
  )
206
214
  }
215
+ if (props.panoramax) {
216
+ body.appendChild(
217
+ Utils.loadTemplate(
218
+ `<div><img src="https://api.panoramax.xyz/api/pictures/${props.panoramax}/sd.jpg" /></div>`
219
+ )
220
+ )
221
+ }
222
+ const wikipedia = props[`wikipedia:${locale}`] || props.wikipedia
223
+ if (wikipedia) {
224
+ body.appendChild(
225
+ Utils.loadTemplate(
226
+ `<div class="wikipedia-link"><a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">${translate('Wikipedia')}</a></div>`
227
+ )
228
+ )
229
+ }
207
230
  const id = props['@id'] || props.id
208
231
  if (id) {
209
232
  body.appendChild(
@@ -52,7 +52,7 @@ const FeatureMixin = {
52
52
  onClick: function (event) {
53
53
  if (this._map.measureTools?.enabled()) return
54
54
  this._popupHandlersAdded = true // Prevent leaflet from managing event
55
- if (!this._map.editEnabled) {
55
+ if (!this._map._umap.editEnabled) {
56
56
  this.feature.view(event)
57
57
  } else if (!this.feature.isReadOnly()) {
58
58
  if (event.originalEvent.shiftKey) {
@@ -75,7 +75,7 @@ const FeatureMixin = {
75
75
 
76
76
  resetTooltip: function () {
77
77
  if (!this.feature.hasGeom()) return
78
- const displayName = this.feature.getDisplayName(null)
78
+ const displayName = this.feature.getDisplayName()
79
79
  let showLabel = this.feature.getOption('showLabel')
80
80
  const oldLabelHover = this.feature.getOption('labelHover')
81
81
 
@@ -96,8 +96,8 @@ const FeatureMixin = {
96
96
  DomEvent.stop(event)
97
97
  const items = this.feature
98
98
  .getContextMenuItems(event)
99
- .concat(this._map.getContextMenuItems(event))
100
- this._map.contextmenu.open(event.originalEvent, items)
99
+ .concat(this._map._umap.getSharedContextMenuItems(event))
100
+ this._map._umap.contextmenu.open(event.originalEvent, items)
101
101
  },
102
102
 
103
103
  onCommit: function () {
@@ -134,7 +134,7 @@ const PointMixin = {
134
134
 
135
135
  _enableDragging: function () {
136
136
  // TODO: start dragging after 1 second on mouse down
137
- if (this._map.editEnabled) {
137
+ if (this._map._umap.editEnabled) {
138
138
  if (!this.editEnabled()) this.enableEdit()
139
139
  // Enabling dragging on the marker override the Draggable._OnDown
140
140
  // event, which, as it stopPropagation, refrain the call of
@@ -146,7 +146,7 @@ const PointMixin = {
146
146
  },
147
147
 
148
148
  _disableDragging: function () {
149
- if (this._map.editEnabled) {
149
+ if (this._map._umap.editEnabled) {
150
150
  if (this.editor?.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
151
151
  // do not listen to them
152
152
  this.disableEdit()
@@ -253,21 +253,26 @@ export const LeafletMarker = Marker.extend({
253
253
  const PathMixin = {
254
254
  _onMouseOver: function () {
255
255
  if (this._map.measureTools?.enabled()) {
256
- this._map.tooltip.open({ content: this.getMeasure(), anchor: this })
257
- } else if (this._map.editEnabled && !this._map.editedFeature) {
258
- this._map.tooltip.open({ content: translate('Click to edit'), anchor: this })
256
+ this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this })
257
+ } else if (this._map._umap.editEnabled && !this._map._umap.editedFeature) {
258
+ this._map._umap.tooltip.open({
259
+ content: translate('Click to edit'),
260
+ anchor: this,
261
+ })
259
262
  }
260
263
  },
261
264
 
262
265
  makeGeometryEditable: function () {
263
- if (this._map.editedFeature !== this.feature) {
266
+ if (this._map._umap.editedFeature !== this.feature) {
264
267
  this.disableEdit()
265
268
  return
266
269
  }
267
270
  this._map.once('moveend', this.makeGeometryEditable, this)
268
271
  const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
269
272
  if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) {
270
- this._map.tooltip.open({ content: L._('Please zoom in to edit the geometry') })
273
+ this._map._umap.tooltip.open({
274
+ content: L._('Please zoom in to edit the geometry'),
275
+ })
271
276
  this.disableEdit()
272
277
  } else {
273
278
  this.enableEdit()
@@ -380,10 +385,50 @@ export const LeafletPolyline = Polyline.extend({
380
385
  },
381
386
 
382
387
  getMeasure: function (shape) {
388
+ let shapes
389
+ if (shape) {
390
+ shapes = [shape]
391
+ } else if (LineUtil.isFlat(this._latlngs)) {
392
+ shapes = [this._latlngs]
393
+ } else {
394
+ shapes = this._latlngs
395
+ }
383
396
  // FIXME: compute from data in feature (with TurfJS)
384
- const length = L.GeoUtil.lineLength(this._map, shape || this._defaultShape())
397
+ const length = shapes.reduce(
398
+ (acc, shape) => acc + L.GeoUtil.lineLength(this._map, shape),
399
+ 0
400
+ )
385
401
  return L.GeoUtil.readableDistance(length, this._map.measureTools.getMeasureUnit())
386
402
  },
403
+
404
+ getElevation: function () {
405
+ const lineElevation = (latlngs) => {
406
+ let gain = 0
407
+ let loss = 0
408
+ for (let i = 0, n = latlngs.length - 1; i < n; i++) {
409
+ const fromAlt = latlngs[i].alt
410
+ const toAlt = latlngs[i + 1].alt
411
+ if (fromAlt === undefined || toAlt === undefined) continue
412
+ if (fromAlt > toAlt) loss += fromAlt - toAlt
413
+ else gain += toAlt - fromAlt
414
+ }
415
+ return [gain, loss]
416
+ }
417
+ let shapes
418
+ if (LineUtil.isFlat(this._latlngs)) {
419
+ shapes = [this._latlngs]
420
+ } else {
421
+ shapes = this._latlngs
422
+ }
423
+ let totalGain = 0
424
+ let totalLoss = 0
425
+ for (const shape of shapes) {
426
+ const [gain, loss] = lineElevation(shape)
427
+ totalGain += gain
428
+ totalLoss += loss
429
+ }
430
+ return [Math.round(totalGain), Math.round(totalLoss)]
431
+ },
387
432
  })
388
433
 
389
434
  export const LeafletPolygon = Polygon.extend({