umap-project 2.7.3__py3-none-any.whl → 2.8.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.
Files changed (292) 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 +35 -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 +23 -3
  91. umap/settings/local_s3.py +45 -0
  92. umap/static/umap/base.css +3 -603
  93. umap/static/umap/content.css +5 -3
  94. umap/static/umap/css/bar.css +202 -0
  95. umap/static/umap/css/form.css +620 -0
  96. umap/static/umap/css/icon.css +21 -1
  97. umap/static/umap/css/popup.css +125 -0
  98. umap/static/umap/img/16-white.svg +16 -4
  99. umap/static/umap/img/16.svg +1 -1
  100. umap/static/umap/img/source/16-white.svg +46 -45
  101. umap/static/umap/img/source/16.svg +1 -753
  102. umap/static/umap/js/components/fragment.js +3 -1
  103. umap/static/umap/js/modules/browser.js +20 -19
  104. umap/static/umap/js/modules/caption.js +21 -22
  105. umap/static/umap/js/modules/data/features.js +120 -78
  106. umap/static/umap/js/modules/data/layer.js +195 -153
  107. umap/static/umap/js/modules/facets.js +9 -9
  108. umap/static/umap/js/modules/formatter.js +5 -5
  109. umap/static/umap/js/modules/global.js +4 -52
  110. umap/static/umap/js/modules/help.js +18 -21
  111. umap/static/umap/js/modules/importer.js +133 -56
  112. umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
  113. umap/static/umap/js/modules/importers/geodatamine.js +3 -3
  114. umap/static/umap/js/modules/importers/overpass.js +5 -0
  115. umap/static/umap/js/modules/permissions.js +85 -87
  116. umap/static/umap/js/modules/rendering/icon.js +2 -1
  117. umap/static/umap/js/modules/rendering/layers/base.js +15 -15
  118. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  119. umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
  120. umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
  121. umap/static/umap/js/modules/rendering/map.js +390 -0
  122. umap/static/umap/js/modules/rendering/popup.js +19 -19
  123. umap/static/umap/js/modules/rendering/template.js +88 -21
  124. umap/static/umap/js/modules/rendering/ui.js +63 -14
  125. umap/static/umap/js/modules/request.js +2 -2
  126. umap/static/umap/js/modules/rules.js +22 -25
  127. umap/static/umap/js/modules/saving.js +47 -0
  128. umap/static/umap/js/modules/schema.js +6 -0
  129. umap/static/umap/js/modules/share.js +21 -24
  130. umap/static/umap/js/modules/slideshow.js +24 -20
  131. umap/static/umap/js/modules/sync/updaters.js +7 -9
  132. umap/static/umap/js/modules/tableeditor.js +20 -19
  133. umap/static/umap/js/modules/ui/bar.js +196 -0
  134. umap/static/umap/js/modules/ui/dialog.js +5 -0
  135. umap/static/umap/js/modules/ui/panel.js +10 -9
  136. umap/static/umap/js/modules/umap.js +1691 -0
  137. umap/static/umap/js/modules/urls.js +2 -2
  138. umap/static/umap/js/modules/utils.js +22 -6
  139. umap/static/umap/js/umap.controls.js +81 -305
  140. umap/static/umap/js/umap.core.js +29 -50
  141. umap/static/umap/js/umap.forms.js +78 -27
  142. umap/static/umap/keycloak.png +0 -0
  143. umap/static/umap/locale/am_ET.js +26 -10
  144. umap/static/umap/locale/am_ET.json +26 -10
  145. umap/static/umap/locale/ar.js +26 -10
  146. umap/static/umap/locale/ar.json +26 -10
  147. umap/static/umap/locale/ast.js +26 -10
  148. umap/static/umap/locale/ast.json +26 -10
  149. umap/static/umap/locale/bg.js +26 -10
  150. umap/static/umap/locale/bg.json +26 -10
  151. umap/static/umap/locale/br.js +27 -20
  152. umap/static/umap/locale/br.json +27 -20
  153. umap/static/umap/locale/ca.js +32 -29
  154. umap/static/umap/locale/ca.json +32 -29
  155. umap/static/umap/locale/cs_CZ.js +24 -17
  156. umap/static/umap/locale/cs_CZ.json +24 -17
  157. umap/static/umap/locale/da.js +26 -10
  158. umap/static/umap/locale/da.json +26 -10
  159. umap/static/umap/locale/de.js +21 -14
  160. umap/static/umap/locale/de.json +21 -14
  161. umap/static/umap/locale/el.js +28 -12
  162. umap/static/umap/locale/el.json +28 -12
  163. umap/static/umap/locale/en.js +14 -9
  164. umap/static/umap/locale/en.json +14 -9
  165. umap/static/umap/locale/en_US.json +26 -10
  166. umap/static/umap/locale/es.js +16 -13
  167. umap/static/umap/locale/es.json +16 -13
  168. umap/static/umap/locale/et.js +26 -10
  169. umap/static/umap/locale/et.json +26 -10
  170. umap/static/umap/locale/eu.js +16 -9
  171. umap/static/umap/locale/eu.json +16 -9
  172. umap/static/umap/locale/fa_IR.js +16 -9
  173. umap/static/umap/locale/fa_IR.json +16 -9
  174. umap/static/umap/locale/fi.js +26 -10
  175. umap/static/umap/locale/fi.json +26 -10
  176. umap/static/umap/locale/fr.js +14 -9
  177. umap/static/umap/locale/fr.json +14 -9
  178. umap/static/umap/locale/gl.js +26 -10
  179. umap/static/umap/locale/gl.json +26 -10
  180. umap/static/umap/locale/he.js +26 -10
  181. umap/static/umap/locale/he.json +26 -10
  182. umap/static/umap/locale/hr.js +26 -10
  183. umap/static/umap/locale/hr.json +26 -10
  184. umap/static/umap/locale/hu.js +16 -9
  185. umap/static/umap/locale/hu.json +16 -9
  186. umap/static/umap/locale/id.js +26 -10
  187. umap/static/umap/locale/id.json +26 -10
  188. umap/static/umap/locale/is.js +26 -10
  189. umap/static/umap/locale/is.json +26 -10
  190. umap/static/umap/locale/it.js +26 -10
  191. umap/static/umap/locale/it.json +26 -10
  192. umap/static/umap/locale/ja.js +26 -10
  193. umap/static/umap/locale/ja.json +26 -10
  194. umap/static/umap/locale/ko.js +26 -10
  195. umap/static/umap/locale/ko.json +26 -10
  196. umap/static/umap/locale/lt.js +26 -10
  197. umap/static/umap/locale/lt.json +26 -10
  198. umap/static/umap/locale/ms.js +28 -12
  199. umap/static/umap/locale/ms.json +28 -12
  200. umap/static/umap/locale/nl.js +28 -12
  201. umap/static/umap/locale/nl.json +28 -12
  202. umap/static/umap/locale/no.js +26 -10
  203. umap/static/umap/locale/no.json +26 -10
  204. umap/static/umap/locale/pl.js +28 -12
  205. umap/static/umap/locale/pl.json +28 -12
  206. umap/static/umap/locale/pl_PL.json +26 -10
  207. umap/static/umap/locale/pt.js +16 -9
  208. umap/static/umap/locale/pt.json +16 -9
  209. umap/static/umap/locale/pt_BR.js +26 -10
  210. umap/static/umap/locale/pt_BR.json +26 -10
  211. umap/static/umap/locale/pt_PT.js +16 -9
  212. umap/static/umap/locale/pt_PT.json +16 -9
  213. umap/static/umap/locale/ro.js +26 -10
  214. umap/static/umap/locale/ro.json +26 -10
  215. umap/static/umap/locale/ru.js +26 -10
  216. umap/static/umap/locale/ru.json +26 -10
  217. umap/static/umap/locale/si.js +7 -7
  218. umap/static/umap/locale/si.json +7 -7
  219. umap/static/umap/locale/sk_SK.js +26 -10
  220. umap/static/umap/locale/sk_SK.json +26 -10
  221. umap/static/umap/locale/sl.js +26 -10
  222. umap/static/umap/locale/sl.json +26 -10
  223. umap/static/umap/locale/sr.js +26 -10
  224. umap/static/umap/locale/sr.json +26 -10
  225. umap/static/umap/locale/sv.js +27 -11
  226. umap/static/umap/locale/sv.json +27 -11
  227. umap/static/umap/locale/th_TH.js +28 -12
  228. umap/static/umap/locale/th_TH.json +28 -12
  229. umap/static/umap/locale/tr.js +26 -10
  230. umap/static/umap/locale/tr.json +26 -10
  231. umap/static/umap/locale/uk_UA.js +26 -10
  232. umap/static/umap/locale/uk_UA.json +26 -10
  233. umap/static/umap/locale/vi.js +26 -10
  234. umap/static/umap/locale/vi.json +26 -10
  235. umap/static/umap/locale/vi_VN.json +26 -10
  236. umap/static/umap/locale/zh.js +26 -10
  237. umap/static/umap/locale/zh.json +26 -10
  238. umap/static/umap/locale/zh_CN.json +26 -10
  239. umap/static/umap/locale/zh_TW.Big5.json +26 -10
  240. umap/static/umap/locale/zh_TW.js +34 -27
  241. umap/static/umap/locale/zh_TW.json +34 -27
  242. umap/static/umap/map.css +39 -530
  243. umap/static/umap/unittests/URLs.js +15 -15
  244. umap/static/umap/unittests/utils.js +23 -1
  245. umap/static/umap/vars.css +2 -1
  246. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
  247. umap/storage/__init__.py +3 -0
  248. umap/storage/fs.py +101 -0
  249. umap/storage/s3.py +61 -0
  250. umap/templates/base.html +2 -0
  251. umap/templates/registration/login.html +7 -6
  252. umap/templates/umap/components/alerts/alert.html +4 -0
  253. umap/templates/umap/css.html +6 -0
  254. umap/templates/umap/js.html +3 -2
  255. umap/templates/umap/map_init.html +6 -5
  256. umap/templates/umap/user_dashboard.html +20 -19
  257. umap/tests/base.py +5 -1
  258. umap/tests/fixtures/test_upload_simple_marker.json +19 -0
  259. umap/tests/integration/conftest.py +2 -1
  260. umap/tests/integration/test_anonymous_owned_map.py +18 -10
  261. umap/tests/integration/test_browser.py +16 -1
  262. umap/tests/integration/test_dashboard.py +1 -1
  263. umap/tests/integration/test_edit_datalayer.py +29 -7
  264. umap/tests/integration/test_import.py +28 -6
  265. umap/tests/integration/test_optimistic_merge.py +31 -8
  266. umap/tests/integration/test_owned_map.py +22 -16
  267. umap/tests/integration/test_popup.py +44 -0
  268. umap/tests/integration/test_save.py +35 -0
  269. umap/tests/integration/test_view_marker.py +12 -0
  270. umap/tests/integration/test_view_polyline.py +257 -0
  271. umap/tests/integration/test_websocket_sync.py +81 -9
  272. umap/tests/test_dashboard.py +82 -0
  273. umap/tests/test_datalayer.py +6 -7
  274. umap/tests/test_datalayer_s3.py +135 -0
  275. umap/tests/test_datalayer_views.py +28 -10
  276. umap/tests/test_empty_trash.py +34 -0
  277. umap/tests/test_map.py +12 -3
  278. umap/tests/test_map_views.py +69 -37
  279. umap/tests/test_statics.py +1 -1
  280. umap/tests/test_team_views.py +35 -1
  281. umap/tests/test_views.py +31 -52
  282. umap/urls.py +3 -3
  283. umap/views.py +126 -90
  284. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/METADATA +16 -13
  285. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/RECORD +289 -269
  286. umap/management/commands/purge_purgatory.py +0 -28
  287. umap/static/umap/js/umap.js +0 -1903
  288. umap/tests/test_purge_purgatory.py +0 -25
  289. /umap/{storage.py → storage/staticfiles.py} +0 -0
  290. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/WHEEL +0 -0
  291. {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/entry_points.txt +0 -0
  292. {umap_project-2.7.3.dist-info → umap_project-2.8.0.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
+ if (this._controls.tilelayersChooser) {
237
+ this._controls.tilelayersChooser.openSwitcher({ edit: true })
238
+ }
239
+ },
240
+ }
241
+
242
+ export const LeafletMap = BaseMap.extend({
243
+ includes: [ControlsMixin, ManageTilelayerMixin],
244
+
245
+ // The initialize and the setup method might seem similar, but they
246
+ // serve two different purposes:
247
+ // initialize is for Leaflet internal, when we do "new LeafletMap",
248
+ // while setup is the public API for the LeafletMap to actually
249
+ // render to the DOM.
250
+ initialize: function (umap, element) {
251
+ this._umap = umap
252
+ const options = this._umap.properties
253
+
254
+ BaseMap.prototype.initialize.call(this, element, options)
255
+
256
+ // After calling parent initialize, as we are doing initCenter our-selves
257
+
258
+ this.loader = new Control.Loading()
259
+ this.loader.onAdd(this)
260
+
261
+ if (!this.options.noControl) {
262
+ DomEvent.on(document.body, 'dataloading', (event) =>
263
+ this.fire('dataloading', event.detail)
264
+ )
265
+ DomEvent.on(document.body, 'dataload', (event) =>
266
+ this.fire('dataload', event.detail)
267
+ )
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
+ })
@@ -21,30 +21,30 @@ export default function loadPopup(name) {
21
21
  const Popup = BasePopup.extend({
22
22
  initialize: function (feature) {
23
23
  this.feature = feature
24
- this.container = DomUtil.create('div', 'umap-popup')
25
- this.format()
26
- BasePopup.prototype.initialize.call(this, {}, feature)
27
- this.setContent(this.container)
24
+ BasePopup.prototype.initialize.call(this, {}, feature.ui)
28
25
  },
29
26
 
30
- format: function () {
27
+ loadContent: async function () {
28
+ const container = DomUtil.create('div', 'umap-popup')
31
29
  const name = this.feature.getOption('popupTemplate')
32
- this.content = loadTemplate(name, this.feature, this.container)
33
- const elements = this.container.querySelectorAll('img,iframe')
30
+ this.content = await loadTemplate(name, this.feature, container)
31
+ const elements = container.querySelectorAll('img,iframe')
34
32
  for (const element of elements) {
35
33
  this.onElementLoaded(element)
36
34
  }
37
- if (!elements.length && this.container.textContent.replace('\n', '') === '') {
38
- this.container.innerHTML = ''
39
- DomUtil.add('h3', '', this.container, this.feature.getDisplayName())
35
+ if (!elements.length && container.textContent.replace('\n', '') === '') {
36
+ container.innerHTML = ''
37
+ DomUtil.add('h3', '', container, this.feature.getDisplayName())
40
38
  }
39
+ this.setContent(container)
41
40
  },
42
41
 
43
42
  onElementLoaded: function (el) {
44
43
  DomEvent.on(el, 'load', () => {
45
44
  this._updateLayout()
46
45
  this._updatePosition()
47
- this._adjustPan()
46
+ // Do not call when feature is in cluster.
47
+ if (this._map) this._adjustPan()
48
48
  })
49
49
  },
50
50
  })
@@ -61,15 +61,15 @@ const Panel = Popup.extend({
61
61
  zoomAnimation: false,
62
62
  },
63
63
 
64
- onAdd: function (map) {
65
- map.panel.setDefaultMode('expanded')
66
- map.panel.open({
64
+ onAdd: function (leafletMap) {
65
+ leafletMap._umap.panel.setDefaultMode('expanded')
66
+ leafletMap._umap.panel.open({
67
67
  content: this._content,
68
- actions: [Browser.backButton(map)],
68
+ actions: [Browser.backButton(leafletMap._umap)],
69
69
  })
70
70
 
71
71
  // fire events as in base class Popup.js:onAdd
72
- map.fire('popupopen', { popup: this })
72
+ leafletMap.fire('popupopen', { popup: this })
73
73
  if (this._source) {
74
74
  this._source.fire('popupopen', { popup: this }, true)
75
75
  if (!(this._source instanceof Path)) {
@@ -78,11 +78,11 @@ const Panel = Popup.extend({
78
78
  }
79
79
  },
80
80
 
81
- onRemove: function (map) {
82
- map.panel.close()
81
+ onRemove: function (leafletMap) {
82
+ leafletMap._umap.panel.close()
83
83
 
84
84
  // fire events as in base class Popup.js:onRemove
85
- map.fire('popupclose', { popup: this })
85
+ leafletMap.fire('popupclose', { popup: this })
86
86
  if (this._source) {
87
87
  this._source.fire('popupclose', { popup: this }, true)
88
88
  if (!(this._source instanceof Path)) {
@@ -2,8 +2,9 @@ import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
2
2
  import { translate, getLocale } from '../i18n.js'
3
3
  import * as Utils from '../utils.js'
4
4
  import * as Icon from './icon.js'
5
+ import { Request } from '../request.js'
5
6
 
6
- export default function loadTemplate(name, feature, container) {
7
+ export default async function loadTemplate(name, feature, container) {
7
8
  let klass = PopupTemplate
8
9
  switch (name) {
9
10
  case 'GeoRSSLink':
@@ -18,9 +19,12 @@ export default function loadTemplate(name, feature, container) {
18
19
  case 'OSM':
19
20
  klass = OSM
20
21
  break
22
+ case 'Wikipedia':
23
+ klass = Wikipedia
24
+ break
21
25
  }
22
26
  const content = new klass()
23
- return content.render(feature, container)
27
+ return await content.render(feature, container)
24
28
  }
25
29
 
26
30
  class PopupTemplate {
@@ -35,6 +39,7 @@ class PopupTemplate {
35
39
  feature.properties.description || '',
36
40
  properties
37
41
  )
42
+ properties.name = properties.name ?? feature.getDisplayName()
38
43
  let content = Utils.greedyTemplate(template, properties)
39
44
  content = Utils.toHTML(content, { target: target })
40
45
  return Utils.loadTemplate(`<div class="umap-popup-container text">${content}</div>`)
@@ -59,7 +64,7 @@ class PopupTemplate {
59
64
  feature: nextFeature.properties.name || translate('next'),
60
65
  })
61
66
  DomEvent.on(next, 'click', () => {
62
- nextFeature.zoomTo({ callback: nextFeature.view })
67
+ nextFeature.zoomTo({ callback: (event) => nextFeature.view(event) })
63
68
  })
64
69
  }
65
70
  if (previousFeature) {
@@ -67,7 +72,7 @@ class PopupTemplate {
67
72
  feature: previousFeature.properties.name || translate('previous'),
68
73
  })
69
74
  DomEvent.on(previous, 'click', () => {
70
- previousFeature.zoomTo({ callback: previousFeature.view })
75
+ previousFeature.zoomTo({ callback: (event) => previousFeature.view(event) })
71
76
  })
72
77
  }
73
78
  DomEvent.on(zoom, 'click', () => feature.zoomTo())
@@ -75,10 +80,10 @@ class PopupTemplate {
75
80
  }
76
81
  }
77
82
 
78
- render(feature, container) {
83
+ async render(feature, container) {
79
84
  const title = this.renderTitle(feature)
80
85
  if (title) container.appendChild(title)
81
- const body = this.renderBody(feature)
86
+ const body = await this.renderBody(feature)
82
87
  if (body) DomUtil.add('div', 'umap-popup-content', container, body)
83
88
  const footer = this.renderFooter(feature)
84
89
  if (footer) container.appendChild(footer)
@@ -110,11 +115,13 @@ class Table extends TitleMixin(PopupTemplate) {
110
115
  )
111
116
  }
112
117
 
113
- renderBody(feature) {
118
+ async renderBody(feature) {
114
119
  const table = document.createElement('table')
115
120
 
116
121
  for (const key in feature.properties) {
117
- if (typeof feature.properties[key] === 'object' || key === 'name') continue
122
+ if (typeof feature.properties[key] === 'object' || U.LABEL_KEYS.includes(key)) {
123
+ continue
124
+ }
118
125
  table.appendChild(this.makeRow(feature, key))
119
126
  }
120
127
  return table
@@ -122,7 +129,7 @@ class Table extends TitleMixin(PopupTemplate) {
122
129
  }
123
130
 
124
131
  class GeoRSSImage extends TitleMixin(PopupTemplate) {
125
- renderBody(feature) {
132
+ async renderBody(feature) {
126
133
  const body = DomUtil.create('a')
127
134
  body.href = feature.properties.link
128
135
  body.target = '_blank'
@@ -139,7 +146,7 @@ class GeoRSSImage extends TitleMixin(PopupTemplate) {
139
146
  }
140
147
 
141
148
  class GeoRSSLink extends PopupTemplate {
142
- renderBody(feature) {
149
+ async renderBody(feature) {
143
150
  if (feature.properties.link) {
144
151
  return Utils.loadTemplate(
145
152
  `<a href="${feature.properties.link}" target="_blank"><h3>${feature.getDisplayName()}</h3></a>`
@@ -148,7 +155,20 @@ class GeoRSSLink extends PopupTemplate {
148
155
  }
149
156
  }
150
157
 
151
- class OSM extends TitleMixin(PopupTemplate) {
158
+ class OSM extends PopupTemplate {
159
+ renderTitle(feature) {
160
+ const title = DomUtil.add('h3', 'popup-title')
161
+ const color = feature.getPreviewColor()
162
+ title.style.backgroundColor = color
163
+ const iconUrl = feature.getDynamicOption('iconUrl')
164
+ const icon = Icon.makeElement(iconUrl, title)
165
+ DomUtil.addClass(icon, 'icon')
166
+ Icon.setContrast(icon, title, iconUrl, color)
167
+ if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
168
+ DomUtil.add('span', '', title, this.getName(feature))
169
+ return title
170
+ }
171
+
152
172
  getName(feature) {
153
173
  const props = feature.properties
154
174
  const locale = getLocale()
@@ -156,18 +176,10 @@ class OSM extends TitleMixin(PopupTemplate) {
156
176
  return props.name
157
177
  }
158
178
 
159
- renderBody(feature) {
179
+ async renderBody(feature) {
160
180
  const props = feature.properties
161
181
  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))
182
+ const locale = getLocale()
171
183
  const street = props['addr:street']
172
184
  if (street) {
173
185
  const row = DomUtil.add('address', 'address', body)
@@ -204,6 +216,21 @@ class OSM extends TitleMixin(PopupTemplate) {
204
216
  Utils.loadTemplate(`<div><a href="mailto:${email}">${email}</a></div>`)
205
217
  )
206
218
  }
219
+ if (props.panoramax) {
220
+ body.appendChild(
221
+ Utils.loadTemplate(
222
+ `<div><img src="https://api.panoramax.xyz/api/pictures/${props.panoramax}/sd.jpg" /></div>`
223
+ )
224
+ )
225
+ }
226
+ const wikipedia = props[`wikipedia:${locale}`] || props.wikipedia
227
+ if (wikipedia) {
228
+ body.appendChild(
229
+ Utils.loadTemplate(
230
+ `<div class="wikipedia-link"><a href="https://wikipedia.org/wiki/${wikipedia}" target="_blank">${translate('Wikipedia')}</a></div>`
231
+ )
232
+ )
233
+ }
207
234
  const id = props['@id'] || props.id
208
235
  if (id) {
209
236
  body.appendChild(
@@ -215,3 +242,43 @@ class OSM extends TitleMixin(PopupTemplate) {
215
242
  return body
216
243
  }
217
244
  }
245
+
246
+ const _WIKIPEDIA_CACHE = {}
247
+
248
+ class Wikipedia extends PopupTemplate {
249
+ async callWikipedia(wikipedia) {
250
+ if (wikipedia && _WIKIPEDIA_CACHE[wikipedia]) return _WIKIPEDIA_CACHE[wikipedia]
251
+ // Wikipedia value should be in form of "{locale}:{title}", according to https://wiki.openstreetmap.org/wiki/Key:wikipedia
252
+ const [locale, page] = wikipedia.split(':')
253
+ const url = `https://${locale}.wikipedia.org/w/api.php?action=query&format=json&origin=*&pithumbsize=500&prop=extracts|pageimages&titles=${page}`
254
+ const request = new Request()
255
+ const response = await request.get(url)
256
+ if (response?.ok) {
257
+ const data = await response.json()
258
+ _WIKIPEDIA_CACHE[wikipedia] = data
259
+ return data
260
+ }
261
+ }
262
+
263
+ async renderBody(feature) {
264
+ const body = document.createElement('div')
265
+ const wikipedia = feature.properties.wikipedia
266
+ if (!wikipedia) return ''
267
+ const data = await this.callWikipedia(wikipedia)
268
+ if (data) {
269
+ const page = Object.values(data.query.pages)[0]
270
+ const title = page.title || feature.getDisplayName()
271
+ const extract = page.extract || ''
272
+ const thumbnail = page.thumbnail?.source
273
+ const [content, { image }] = Utils.loadTemplateWithRefs(
274
+ `<div><h3>${Utils.escapeHTML(title)}</h3><img data-ref="image" hidden src="" />${Utils.escapeHTML(extract)}</div>`
275
+ )
276
+ if (thumbnail) {
277
+ image.src = thumbnail
278
+ image.hidden = false
279
+ }
280
+ body.appendChild(content)
281
+ }
282
+ return body
283
+ }
284
+ }