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.
- umap/__init__.py +1 -1
- umap/forms.py +4 -14
- umap/locale/am_ET/LC_MESSAGES/django.mo +0 -0
- umap/locale/am_ET/LC_MESSAGES/django.po +278 -151
- umap/locale/ar/LC_MESSAGES/django.mo +0 -0
- umap/locale/ar/LC_MESSAGES/django.po +335 -141
- umap/locale/bg/LC_MESSAGES/django.mo +0 -0
- umap/locale/bg/LC_MESSAGES/django.po +279 -152
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +95 -79
- umap/locale/ca/LC_MESSAGES/django.mo +0 -0
- umap/locale/ca/LC_MESSAGES/django.po +85 -68
- umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- umap/locale/cs_CZ/LC_MESSAGES/django.po +78 -66
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +280 -153
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +80 -64
- umap/locale/el/LC_MESSAGES/django.mo +0 -0
- umap/locale/el/LC_MESSAGES/django.po +82 -66
- umap/locale/en/LC_MESSAGES/django.po +73 -61
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +75 -63
- umap/locale/et/LC_MESSAGES/django.mo +0 -0
- umap/locale/et/LC_MESSAGES/django.po +280 -153
- umap/locale/eu/LC_MESSAGES/django.mo +0 -0
- umap/locale/eu/LC_MESSAGES/django.po +82 -66
- umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
- umap/locale/fa_IR/LC_MESSAGES/django.po +80 -64
- umap/locale/fi/LC_MESSAGES/django.mo +0 -0
- umap/locale/fi/LC_MESSAGES/django.po +278 -151
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +75 -63
- umap/locale/gl/LC_MESSAGES/django.mo +0 -0
- umap/locale/gl/LC_MESSAGES/django.po +280 -153
- umap/locale/he/LC_MESSAGES/django.mo +0 -0
- umap/locale/he/LC_MESSAGES/django.po +281 -154
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +80 -64
- umap/locale/is/LC_MESSAGES/django.mo +0 -0
- umap/locale/is/LC_MESSAGES/django.po +280 -153
- umap/locale/it/LC_MESSAGES/django.mo +0 -0
- umap/locale/it/LC_MESSAGES/django.po +82 -66
- umap/locale/ja/LC_MESSAGES/django.mo +0 -0
- umap/locale/ja/LC_MESSAGES/django.po +280 -153
- umap/locale/ko/LC_MESSAGES/django.mo +0 -0
- umap/locale/ko/LC_MESSAGES/django.po +280 -153
- umap/locale/lt/LC_MESSAGES/django.mo +0 -0
- umap/locale/lt/LC_MESSAGES/django.po +280 -153
- umap/locale/ms/LC_MESSAGES/django.mo +0 -0
- umap/locale/ms/LC_MESSAGES/django.po +82 -66
- umap/locale/nl/LC_MESSAGES/django.mo +0 -0
- umap/locale/nl/LC_MESSAGES/django.po +280 -153
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +82 -66
- umap/locale/pt/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt/LC_MESSAGES/django.po +75 -63
- umap/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt_BR/LC_MESSAGES/django.po +280 -153
- umap/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- umap/locale/pt_PT/LC_MESSAGES/django.po +280 -153
- umap/locale/ru/LC_MESSAGES/django.mo +0 -0
- umap/locale/ru/LC_MESSAGES/django.po +280 -153
- umap/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- umap/locale/sk_SK/LC_MESSAGES/django.po +280 -153
- umap/locale/sl/LC_MESSAGES/django.mo +0 -0
- umap/locale/sl/LC_MESSAGES/django.po +280 -153
- umap/locale/sr/LC_MESSAGES/django.mo +0 -0
- umap/locale/sr/LC_MESSAGES/django.po +280 -153
- umap/locale/sv/LC_MESSAGES/django.mo +0 -0
- umap/locale/sv/LC_MESSAGES/django.po +81 -65
- umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
- umap/locale/th_TH/LC_MESSAGES/django.po +257 -185
- umap/locale/tr/LC_MESSAGES/django.mo +0 -0
- umap/locale/tr/LC_MESSAGES/django.po +280 -153
- umap/locale/uk_UA/LC_MESSAGES/django.mo +0 -0
- umap/locale/uk_UA/LC_MESSAGES/django.po +280 -153
- umap/locale/vi/LC_MESSAGES/django.mo +0 -0
- umap/locale/vi/LC_MESSAGES/django.po +278 -151
- umap/locale/zh/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh/LC_MESSAGES/django.po +278 -151
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +97 -81
- umap/management/commands/empty_trash.py +35 -0
- umap/management/commands/migrate_to_S3.py +29 -0
- umap/migrations/0023_alter_datalayer_uuid.py +19 -0
- umap/migrations/0024_alter_map_share_status.py +30 -0
- umap/migrations/0025_alter_datalayer_geojson.py +24 -0
- umap/models.py +68 -116
- umap/settings/base.py +23 -3
- umap/settings/local_s3.py +45 -0
- umap/static/umap/base.css +3 -603
- umap/static/umap/content.css +5 -3
- umap/static/umap/css/bar.css +202 -0
- umap/static/umap/css/form.css +620 -0
- umap/static/umap/css/icon.css +21 -1
- umap/static/umap/css/popup.css +125 -0
- umap/static/umap/img/16-white.svg +16 -4
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +46 -45
- umap/static/umap/img/source/16.svg +1 -753
- umap/static/umap/js/components/fragment.js +3 -1
- umap/static/umap/js/modules/browser.js +20 -19
- umap/static/umap/js/modules/caption.js +21 -22
- umap/static/umap/js/modules/data/features.js +120 -78
- umap/static/umap/js/modules/data/layer.js +195 -153
- umap/static/umap/js/modules/facets.js +9 -9
- umap/static/umap/js/modules/formatter.js +5 -5
- umap/static/umap/js/modules/global.js +4 -52
- umap/static/umap/js/modules/help.js +18 -21
- umap/static/umap/js/modules/importer.js +133 -56
- umap/static/umap/js/modules/importers/cadastrefr.js +4 -0
- umap/static/umap/js/modules/importers/geodatamine.js +3 -3
- umap/static/umap/js/modules/importers/overpass.js +5 -0
- umap/static/umap/js/modules/permissions.js +85 -87
- umap/static/umap/js/modules/rendering/icon.js +2 -1
- umap/static/umap/js/modules/rendering/layers/base.js +15 -15
- umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
- umap/static/umap/js/modules/rendering/layers/cluster.js +1 -1
- umap/static/umap/js/modules/rendering/layers/heat.js +1 -1
- umap/static/umap/js/modules/rendering/map.js +390 -0
- umap/static/umap/js/modules/rendering/popup.js +19 -19
- umap/static/umap/js/modules/rendering/template.js +88 -21
- umap/static/umap/js/modules/rendering/ui.js +63 -14
- umap/static/umap/js/modules/request.js +2 -2
- umap/static/umap/js/modules/rules.js +22 -25
- umap/static/umap/js/modules/saving.js +47 -0
- umap/static/umap/js/modules/schema.js +6 -0
- umap/static/umap/js/modules/share.js +21 -24
- umap/static/umap/js/modules/slideshow.js +24 -20
- umap/static/umap/js/modules/sync/updaters.js +7 -9
- umap/static/umap/js/modules/tableeditor.js +20 -19
- umap/static/umap/js/modules/ui/bar.js +196 -0
- umap/static/umap/js/modules/ui/dialog.js +5 -0
- umap/static/umap/js/modules/ui/panel.js +10 -9
- umap/static/umap/js/modules/umap.js +1691 -0
- umap/static/umap/js/modules/urls.js +2 -2
- umap/static/umap/js/modules/utils.js +22 -6
- umap/static/umap/js/umap.controls.js +81 -305
- umap/static/umap/js/umap.core.js +29 -50
- umap/static/umap/js/umap.forms.js +78 -27
- umap/static/umap/keycloak.png +0 -0
- umap/static/umap/locale/am_ET.js +26 -10
- umap/static/umap/locale/am_ET.json +26 -10
- umap/static/umap/locale/ar.js +26 -10
- umap/static/umap/locale/ar.json +26 -10
- umap/static/umap/locale/ast.js +26 -10
- umap/static/umap/locale/ast.json +26 -10
- umap/static/umap/locale/bg.js +26 -10
- umap/static/umap/locale/bg.json +26 -10
- umap/static/umap/locale/br.js +27 -20
- umap/static/umap/locale/br.json +27 -20
- umap/static/umap/locale/ca.js +32 -29
- umap/static/umap/locale/ca.json +32 -29
- umap/static/umap/locale/cs_CZ.js +24 -17
- umap/static/umap/locale/cs_CZ.json +24 -17
- umap/static/umap/locale/da.js +26 -10
- umap/static/umap/locale/da.json +26 -10
- umap/static/umap/locale/de.js +21 -14
- umap/static/umap/locale/de.json +21 -14
- umap/static/umap/locale/el.js +28 -12
- umap/static/umap/locale/el.json +28 -12
- umap/static/umap/locale/en.js +14 -9
- umap/static/umap/locale/en.json +14 -9
- umap/static/umap/locale/en_US.json +26 -10
- umap/static/umap/locale/es.js +16 -13
- umap/static/umap/locale/es.json +16 -13
- umap/static/umap/locale/et.js +26 -10
- umap/static/umap/locale/et.json +26 -10
- umap/static/umap/locale/eu.js +16 -9
- umap/static/umap/locale/eu.json +16 -9
- umap/static/umap/locale/fa_IR.js +16 -9
- umap/static/umap/locale/fa_IR.json +16 -9
- umap/static/umap/locale/fi.js +26 -10
- umap/static/umap/locale/fi.json +26 -10
- umap/static/umap/locale/fr.js +14 -9
- umap/static/umap/locale/fr.json +14 -9
- umap/static/umap/locale/gl.js +26 -10
- umap/static/umap/locale/gl.json +26 -10
- umap/static/umap/locale/he.js +26 -10
- umap/static/umap/locale/he.json +26 -10
- umap/static/umap/locale/hr.js +26 -10
- umap/static/umap/locale/hr.json +26 -10
- umap/static/umap/locale/hu.js +16 -9
- umap/static/umap/locale/hu.json +16 -9
- umap/static/umap/locale/id.js +26 -10
- umap/static/umap/locale/id.json +26 -10
- umap/static/umap/locale/is.js +26 -10
- umap/static/umap/locale/is.json +26 -10
- umap/static/umap/locale/it.js +26 -10
- umap/static/umap/locale/it.json +26 -10
- umap/static/umap/locale/ja.js +26 -10
- umap/static/umap/locale/ja.json +26 -10
- umap/static/umap/locale/ko.js +26 -10
- umap/static/umap/locale/ko.json +26 -10
- umap/static/umap/locale/lt.js +26 -10
- umap/static/umap/locale/lt.json +26 -10
- umap/static/umap/locale/ms.js +28 -12
- umap/static/umap/locale/ms.json +28 -12
- umap/static/umap/locale/nl.js +28 -12
- umap/static/umap/locale/nl.json +28 -12
- umap/static/umap/locale/no.js +26 -10
- umap/static/umap/locale/no.json +26 -10
- umap/static/umap/locale/pl.js +28 -12
- umap/static/umap/locale/pl.json +28 -12
- umap/static/umap/locale/pl_PL.json +26 -10
- umap/static/umap/locale/pt.js +16 -9
- umap/static/umap/locale/pt.json +16 -9
- umap/static/umap/locale/pt_BR.js +26 -10
- umap/static/umap/locale/pt_BR.json +26 -10
- umap/static/umap/locale/pt_PT.js +16 -9
- umap/static/umap/locale/pt_PT.json +16 -9
- umap/static/umap/locale/ro.js +26 -10
- umap/static/umap/locale/ro.json +26 -10
- umap/static/umap/locale/ru.js +26 -10
- umap/static/umap/locale/ru.json +26 -10
- umap/static/umap/locale/si.js +7 -7
- umap/static/umap/locale/si.json +7 -7
- umap/static/umap/locale/sk_SK.js +26 -10
- umap/static/umap/locale/sk_SK.json +26 -10
- umap/static/umap/locale/sl.js +26 -10
- umap/static/umap/locale/sl.json +26 -10
- umap/static/umap/locale/sr.js +26 -10
- umap/static/umap/locale/sr.json +26 -10
- umap/static/umap/locale/sv.js +27 -11
- umap/static/umap/locale/sv.json +27 -11
- umap/static/umap/locale/th_TH.js +28 -12
- umap/static/umap/locale/th_TH.json +28 -12
- umap/static/umap/locale/tr.js +26 -10
- umap/static/umap/locale/tr.json +26 -10
- umap/static/umap/locale/uk_UA.js +26 -10
- umap/static/umap/locale/uk_UA.json +26 -10
- umap/static/umap/locale/vi.js +26 -10
- umap/static/umap/locale/vi.json +26 -10
- umap/static/umap/locale/vi_VN.json +26 -10
- umap/static/umap/locale/zh.js +26 -10
- umap/static/umap/locale/zh.json +26 -10
- umap/static/umap/locale/zh_CN.json +26 -10
- umap/static/umap/locale/zh_TW.Big5.json +26 -10
- umap/static/umap/locale/zh_TW.js +34 -27
- umap/static/umap/locale/zh_TW.json +34 -27
- umap/static/umap/map.css +39 -530
- umap/static/umap/unittests/URLs.js +15 -15
- umap/static/umap/unittests/utils.js +23 -1
- umap/static/umap/vars.css +2 -1
- umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +5 -1
- umap/storage/__init__.py +3 -0
- umap/storage/fs.py +101 -0
- umap/storage/s3.py +61 -0
- umap/templates/base.html +2 -0
- umap/templates/registration/login.html +7 -6
- umap/templates/umap/components/alerts/alert.html +4 -0
- umap/templates/umap/css.html +6 -0
- umap/templates/umap/js.html +3 -2
- umap/templates/umap/map_init.html +6 -5
- umap/templates/umap/user_dashboard.html +20 -19
- umap/tests/base.py +5 -1
- umap/tests/fixtures/test_upload_simple_marker.json +19 -0
- umap/tests/integration/conftest.py +2 -1
- umap/tests/integration/test_anonymous_owned_map.py +18 -10
- umap/tests/integration/test_browser.py +16 -1
- umap/tests/integration/test_dashboard.py +1 -1
- umap/tests/integration/test_edit_datalayer.py +29 -7
- umap/tests/integration/test_import.py +28 -6
- umap/tests/integration/test_optimistic_merge.py +31 -8
- umap/tests/integration/test_owned_map.py +22 -16
- umap/tests/integration/test_popup.py +44 -0
- umap/tests/integration/test_save.py +35 -0
- umap/tests/integration/test_view_marker.py +12 -0
- umap/tests/integration/test_view_polyline.py +257 -0
- umap/tests/integration/test_websocket_sync.py +81 -9
- umap/tests/test_dashboard.py +82 -0
- umap/tests/test_datalayer.py +6 -7
- umap/tests/test_datalayer_s3.py +135 -0
- umap/tests/test_datalayer_views.py +28 -10
- umap/tests/test_empty_trash.py +34 -0
- umap/tests/test_map.py +12 -3
- umap/tests/test_map_views.py +69 -37
- umap/tests/test_statics.py +1 -1
- umap/tests/test_team_views.py +35 -1
- umap/tests/test_views.py +31 -52
- umap/urls.py +3 -3
- umap/views.py +126 -90
- {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/METADATA +16 -13
- {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/RECORD +289 -269
- umap/management/commands/purge_purgatory.py +0 -28
- umap/static/umap/js/umap.js +0 -1903
- umap/tests/test_purge_purgatory.py +0 -25
- /umap/{storage.py → storage/staticfiles.py} +0 -0
- {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/WHEEL +0 -0
- {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/entry_points.txt +0 -0
- {umap_project-2.7.3.dist-info → umap_project-2.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1691 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DomUtil,
|
|
3
|
+
Util as LeafletUtil,
|
|
4
|
+
stamp,
|
|
5
|
+
latLngBounds,
|
|
6
|
+
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
7
|
+
import { translate, setLocale, getLocale } from './i18n.js'
|
|
8
|
+
import * as Utils from './utils.js'
|
|
9
|
+
import { ServerStored } from './saving.js'
|
|
10
|
+
import * as SAVEMANAGER from './saving.js'
|
|
11
|
+
import { SyncEngine } from './sync/engine.js'
|
|
12
|
+
import { LeafletMap } from './rendering/map.js'
|
|
13
|
+
import URLs from './urls.js'
|
|
14
|
+
import { Panel, EditPanel, FullPanel } from './ui/panel.js'
|
|
15
|
+
import Dialog from './ui/dialog.js'
|
|
16
|
+
import { BottomBar, TopBar } from './ui/bar.js'
|
|
17
|
+
import Tooltip from './ui/tooltip.js'
|
|
18
|
+
import ContextMenu from './ui/contextmenu.js'
|
|
19
|
+
import { Request, ServerRequest } from './request.js'
|
|
20
|
+
import Help from './help.js'
|
|
21
|
+
import { Formatter } from './formatter.js'
|
|
22
|
+
import Slideshow from './slideshow.js'
|
|
23
|
+
import { MapPermissions } from './permissions.js'
|
|
24
|
+
import { SCHEMA } from './schema.js'
|
|
25
|
+
import { DataLayer } from './data/layer.js'
|
|
26
|
+
import Facets from './facets.js'
|
|
27
|
+
import Browser from './browser.js'
|
|
28
|
+
import Caption from './caption.js'
|
|
29
|
+
import Importer from './importer.js'
|
|
30
|
+
import Rules from './rules.js'
|
|
31
|
+
import Share from './share.js'
|
|
32
|
+
import {
|
|
33
|
+
uMapAlertCreation as AlertCreation,
|
|
34
|
+
uMapAlert as Alert,
|
|
35
|
+
} from '../components/alerts/alert.js'
|
|
36
|
+
import Orderable from './orderable.js'
|
|
37
|
+
|
|
38
|
+
export default class Umap extends ServerStored {
|
|
39
|
+
constructor(element, geojson) {
|
|
40
|
+
super()
|
|
41
|
+
// We need to call async function in the init process,
|
|
42
|
+
// the init itself does not need to be awaited, but some calls
|
|
43
|
+
// in the process must be blocker
|
|
44
|
+
this.init(element, geojson)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get id() {
|
|
48
|
+
return this.properties.id
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async init(element, geojson) {
|
|
52
|
+
this.properties = Object.assign(
|
|
53
|
+
{
|
|
54
|
+
enableMarkerDraw: true,
|
|
55
|
+
enablePolygonDraw: true,
|
|
56
|
+
enablePolylineDraw: true,
|
|
57
|
+
hash: true,
|
|
58
|
+
limitBounds: {},
|
|
59
|
+
},
|
|
60
|
+
geojson.properties
|
|
61
|
+
)
|
|
62
|
+
this.searchParams = new URLSearchParams(window.location.search)
|
|
63
|
+
|
|
64
|
+
this.sync_engine = new SyncEngine(this)
|
|
65
|
+
this.sync = this.sync_engine.proxy(this)
|
|
66
|
+
// Locale name (pt_PT, en_US…)
|
|
67
|
+
// To be used for Django localization
|
|
68
|
+
if (geojson.properties.locale) setLocale(geojson.properties.locale)
|
|
69
|
+
|
|
70
|
+
// Language code (pt-pt, en-us…)
|
|
71
|
+
// To be used in javascript APIs
|
|
72
|
+
if (geojson.properties.lang) U.lang = geojson.properties.lang
|
|
73
|
+
|
|
74
|
+
// Make it available to utils, without needing a reference to `Umap`.
|
|
75
|
+
U.LABEL_KEYS = geojson.properties.defaultLabelKeys || []
|
|
76
|
+
U.DEFAULT_LABEL_KEY = U.LABEL_KEYS[0] || 'name'
|
|
77
|
+
|
|
78
|
+
this.setPropertiesFromQueryString()
|
|
79
|
+
|
|
80
|
+
// Needed for actions labels
|
|
81
|
+
this.help = new Help(this)
|
|
82
|
+
// Prevent default creation of controls
|
|
83
|
+
const zoomControl = this.properties.zoomControl
|
|
84
|
+
const fullscreenControl = this.properties.fullscreenControl
|
|
85
|
+
const center = geojson.geometry
|
|
86
|
+
this.properties.zoomControl = false
|
|
87
|
+
this.properties.fullscreenControl = false
|
|
88
|
+
|
|
89
|
+
this._leafletMap = new LeafletMap(this, element)
|
|
90
|
+
|
|
91
|
+
this.properties.zoomControl = zoomControl !== undefined ? zoomControl : true
|
|
92
|
+
this.properties.fullscreenControl =
|
|
93
|
+
fullscreenControl !== undefined ? fullscreenControl : true
|
|
94
|
+
|
|
95
|
+
if (center) {
|
|
96
|
+
this._leafletMap.options.center = this._leafletMap.latLng(center)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Needed to render controls
|
|
100
|
+
this.permissions = new MapPermissions(this)
|
|
101
|
+
this.urls = new URLs(this.properties.urls)
|
|
102
|
+
this.slideshow = new Slideshow(this, this._leafletMap)
|
|
103
|
+
|
|
104
|
+
this._leafletMap.setup()
|
|
105
|
+
|
|
106
|
+
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
|
|
107
|
+
|
|
108
|
+
this.panel = new Panel(this, this._leafletMap)
|
|
109
|
+
this.dialog = new Dialog({ className: 'dark' })
|
|
110
|
+
this.topBar = new TopBar(this, this._leafletMap._controlContainer)
|
|
111
|
+
this.bottomBar = new BottomBar(
|
|
112
|
+
this,
|
|
113
|
+
this.slideshow,
|
|
114
|
+
this._leafletMap._controlContainer
|
|
115
|
+
)
|
|
116
|
+
this.tooltip = new Tooltip(this._leafletMap._controlContainer)
|
|
117
|
+
this.contextmenu = new ContextMenu()
|
|
118
|
+
this.server = new ServerRequest()
|
|
119
|
+
this.request = new Request()
|
|
120
|
+
this.facets = new Facets(this)
|
|
121
|
+
this.browser = new Browser(this, this._leafletMap)
|
|
122
|
+
this.caption = new Caption(this, this._leafletMap)
|
|
123
|
+
this.importer = new Importer(this)
|
|
124
|
+
this.share = new Share(this)
|
|
125
|
+
this.rules = new Rules(this)
|
|
126
|
+
|
|
127
|
+
if (this.hasEditMode()) {
|
|
128
|
+
this.editPanel = new EditPanel(this, this._leafletMap)
|
|
129
|
+
this.fullPanel = new FullPanel(this, this._leafletMap)
|
|
130
|
+
this._leafletMap.initEditTools()
|
|
131
|
+
this.topBar.setup()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.datalayersFromQueryString = this.searchParams.get('datalayers')
|
|
135
|
+
if (this.datalayersFromQueryString) {
|
|
136
|
+
this.datalayersFromQueryString = this.datalayersFromQueryString
|
|
137
|
+
.toString()
|
|
138
|
+
.split(',')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Retrocompat
|
|
142
|
+
if (
|
|
143
|
+
this.properties.slideshow?.delay &&
|
|
144
|
+
this.properties.slideshow.active === undefined
|
|
145
|
+
) {
|
|
146
|
+
this.properties.slideshow.active = true
|
|
147
|
+
}
|
|
148
|
+
if (this.properties.advancedFilterKey) {
|
|
149
|
+
this.properties.facetKey = this.properties.advancedFilterKey
|
|
150
|
+
delete this.properties.advancedFilterKey
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Global storage for retrieving datalayers and features.
|
|
154
|
+
this.datalayers = {} // All datalayers, including deleted.
|
|
155
|
+
this.datalayersIndex = [] // Datalayers actually on the map and ordered.
|
|
156
|
+
this.featuresIndex = {}
|
|
157
|
+
|
|
158
|
+
this.formatter = new Formatter(this)
|
|
159
|
+
|
|
160
|
+
this.initDataLayers()
|
|
161
|
+
|
|
162
|
+
if (this.properties.displayCaptionOnLoad) {
|
|
163
|
+
// Retrocompat
|
|
164
|
+
if (!this.properties.onLoadPanel) {
|
|
165
|
+
this.properties.onLoadPanel = 'caption'
|
|
166
|
+
}
|
|
167
|
+
delete this.properties.displayCaptionOnLoad
|
|
168
|
+
}
|
|
169
|
+
if (this.properties.displayDataBrowserOnLoad) {
|
|
170
|
+
// Retrocompat
|
|
171
|
+
if (!this.properties.onLoadPanel) {
|
|
172
|
+
this.properties.onLoadPanel = 'databrowser'
|
|
173
|
+
}
|
|
174
|
+
delete this.properties.displayDataBrowserOnLoad
|
|
175
|
+
}
|
|
176
|
+
if (this.properties.datalayersControl === 'expanded') {
|
|
177
|
+
if (!this.properties.onLoadPanel) {
|
|
178
|
+
this.properties.onLoadPanel = 'datalayers'
|
|
179
|
+
}
|
|
180
|
+
delete this.properties.datalayersControl
|
|
181
|
+
}
|
|
182
|
+
if (this.properties.onLoadPanel === 'facet') {
|
|
183
|
+
this.properties.onLoadPanel = 'datafilters'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Creation mode
|
|
187
|
+
if (!this.id) {
|
|
188
|
+
if (!this.properties.preview) {
|
|
189
|
+
this.isDirty = true
|
|
190
|
+
this.enableEdit()
|
|
191
|
+
}
|
|
192
|
+
this._defaultExtent = true
|
|
193
|
+
this.properties.name = translate('Untitled map')
|
|
194
|
+
await this.loadDataFromQueryString()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!this.properties.noControl) {
|
|
198
|
+
this.initShortcuts()
|
|
199
|
+
this._leafletMap.on('contextmenu', (e) => this.onContextMenu(e))
|
|
200
|
+
this.onceDataLoaded(this.setViewFromQueryString)
|
|
201
|
+
this.bottomBar.setup()
|
|
202
|
+
this.propagate()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
window.onbeforeunload = () => (this.editEnabled && SAVEMANAGER.isDirty) || null
|
|
206
|
+
this.backup()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get editedFeature() {
|
|
210
|
+
return this._editedFeature
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
set editedFeature(feature) {
|
|
214
|
+
if (this._editedFeature && this._editedFeature !== feature) {
|
|
215
|
+
this._editedFeature.endEdit()
|
|
216
|
+
}
|
|
217
|
+
this._editedFeature = feature
|
|
218
|
+
this.fire('seteditedfeature')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
get activeFeature() {
|
|
222
|
+
return this._activeFeature
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
set activeFeature(feature) {
|
|
226
|
+
this._activeFeature = feature
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setPropertiesFromQueryString() {
|
|
230
|
+
const asBoolean = (key) => {
|
|
231
|
+
const value = this.searchParams.get(key)
|
|
232
|
+
if (value !== undefined && value !== null) {
|
|
233
|
+
this.properties[key] = value === '1' || value === 'true'
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const asNullableBoolean = (key) => {
|
|
237
|
+
if (this.searchParams.has(key)) {
|
|
238
|
+
let value = this.searchParams.get(key)
|
|
239
|
+
if (value === 'null') value = null
|
|
240
|
+
else if (value === '0' || value === 'false') value = false
|
|
241
|
+
else value = true
|
|
242
|
+
this.properties[key] = value
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const asNumber = (key) => {
|
|
246
|
+
const value = +this.searchParams.get(key)
|
|
247
|
+
if (!Number.isNaN(value)) this.properties[name] = value
|
|
248
|
+
}
|
|
249
|
+
// FIXME retrocompat
|
|
250
|
+
asBoolean('displayDataBrowserOnLoad')
|
|
251
|
+
asBoolean('displayCaptionOnLoad')
|
|
252
|
+
for (const [key, schema] of Object.entries(SCHEMA)) {
|
|
253
|
+
switch (schema.type) {
|
|
254
|
+
case Boolean:
|
|
255
|
+
if (schema.nullable) asNullableBoolean(key)
|
|
256
|
+
else asBoolean(key)
|
|
257
|
+
break
|
|
258
|
+
case Number:
|
|
259
|
+
asNumber(key)
|
|
260
|
+
break
|
|
261
|
+
case String: {
|
|
262
|
+
if (this.searchParams.has(key)) {
|
|
263
|
+
const value = this.searchParams.get(key)
|
|
264
|
+
if (value !== undefined) this.properties[key] = value
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Specific case for datalayersControl
|
|
271
|
+
// which accepts "expanded" value, on top of true/false/null
|
|
272
|
+
if (this.searchParams.get('datalayersControl') === 'expanded') {
|
|
273
|
+
if (!this.properties.onLoadPanel) {
|
|
274
|
+
this.properties.onLoadPanel = 'datalayers'
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async setViewFromQueryString() {
|
|
280
|
+
if (this.properties.noControl) return
|
|
281
|
+
// TODO: move to a "initPanel" function
|
|
282
|
+
if (this.searchParams.has('share')) {
|
|
283
|
+
this.share.open()
|
|
284
|
+
} else if (this.properties.onLoadPanel === 'databrowser') {
|
|
285
|
+
this.panel.setDefaultMode('expanded')
|
|
286
|
+
this.openBrowser('data')
|
|
287
|
+
} else if (this.properties.onLoadPanel === 'datalayers') {
|
|
288
|
+
this.panel.setDefaultMode('condensed')
|
|
289
|
+
this.openBrowser('layers')
|
|
290
|
+
} else if (this.properties.onLoadPanel === 'datafilters') {
|
|
291
|
+
this.panel.setDefaultMode('expanded')
|
|
292
|
+
this.openBrowser('filters')
|
|
293
|
+
} else if (this.properties.onLoadPanel === 'caption') {
|
|
294
|
+
this.panel.setDefaultMode('condensed')
|
|
295
|
+
this.openCaption()
|
|
296
|
+
}
|
|
297
|
+
// Comes after default panels, so if it opens in a panel it will
|
|
298
|
+
// take precedence.
|
|
299
|
+
const slug = this.searchParams.get('feature')
|
|
300
|
+
if (slug && this.featuresIndex[slug]) this.featuresIndex[slug].view()
|
|
301
|
+
if (this.searchParams.has('edit')) {
|
|
302
|
+
if (this.hasEditMode()) this.enableEdit()
|
|
303
|
+
// Sometimes users share the ?edit link by mistake, let's remove
|
|
304
|
+
// this search parameter from URL to prevent this
|
|
305
|
+
const url = new URL(window.location)
|
|
306
|
+
url.searchParams.delete('edit')
|
|
307
|
+
history.pushState({}, '', url)
|
|
308
|
+
}
|
|
309
|
+
if (this.searchParams.has('download')) {
|
|
310
|
+
const download_url = this.urls.get('map_download', {
|
|
311
|
+
map_id: this.id,
|
|
312
|
+
})
|
|
313
|
+
window.location = download_url
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async loadDataFromQueryString() {
|
|
318
|
+
let data = this.searchParams.get('data')
|
|
319
|
+
const dataUrls = this.searchParams.getAll('dataUrl')
|
|
320
|
+
const dataFormat = this.searchParams.get('dataFormat') || 'geojson'
|
|
321
|
+
if (dataUrls.length) {
|
|
322
|
+
for (let dataUrl of dataUrls) {
|
|
323
|
+
dataUrl = decodeURIComponent(dataUrl)
|
|
324
|
+
dataUrl = this.renderUrl(dataUrl)
|
|
325
|
+
dataUrl = this.proxyUrl(dataUrl)
|
|
326
|
+
const datalayer = this.createDataLayer()
|
|
327
|
+
await datalayer
|
|
328
|
+
.importFromUrl(dataUrl, dataFormat)
|
|
329
|
+
.then(() => datalayer.zoomTo())
|
|
330
|
+
}
|
|
331
|
+
} else if (data) {
|
|
332
|
+
data = decodeURIComponent(data)
|
|
333
|
+
const datalayer = this.createDataLayer()
|
|
334
|
+
await datalayer.importRaw(data, dataFormat).then(() => datalayer.zoomTo())
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
getOwnContextMenuItems(event) {
|
|
339
|
+
const items = []
|
|
340
|
+
if (this.hasEditMode()) {
|
|
341
|
+
if (this.editEnabled) {
|
|
342
|
+
if (!SAVEMANAGER.isDirty) {
|
|
343
|
+
items.push({
|
|
344
|
+
label: this.help.displayLabel('STOP_EDIT'),
|
|
345
|
+
action: () => this.disableEdit(),
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
if (this.properties.enableMarkerDraw) {
|
|
349
|
+
items.push({
|
|
350
|
+
label: this.help.displayLabel('DRAW_MARKER'),
|
|
351
|
+
action: () => this._leafletMap.startMarker(event),
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
if (this.properties.enablePolylineDraw) {
|
|
355
|
+
items.push({
|
|
356
|
+
label: this.help.displayLabel('DRAW_POLYGON'),
|
|
357
|
+
action: () => this._leafletMap.startPolygon(event),
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
if (this.properties.enablePolygonDraw) {
|
|
361
|
+
items.push({
|
|
362
|
+
label: this.help.displayLabel('DRAW_LINE'),
|
|
363
|
+
action: () => this._leafletMap.startPolyline(event),
|
|
364
|
+
})
|
|
365
|
+
}
|
|
366
|
+
items.push('-')
|
|
367
|
+
items.push({
|
|
368
|
+
label: translate('Help'),
|
|
369
|
+
action: () => this.help.show('edit'),
|
|
370
|
+
})
|
|
371
|
+
} else {
|
|
372
|
+
items.push({
|
|
373
|
+
label: this.help.displayLabel('TOGGLE_EDIT'),
|
|
374
|
+
action: () => this.enableEdit(),
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (items.length) {
|
|
379
|
+
items.push('-')
|
|
380
|
+
}
|
|
381
|
+
items.push(
|
|
382
|
+
{
|
|
383
|
+
label: translate('Open browser'),
|
|
384
|
+
action: () => this.openBrowser('layers'),
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
label: translate('Browse data'),
|
|
388
|
+
action: () => this.openBrowser('data'),
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
if (this.properties.facetKey) {
|
|
392
|
+
items.push({
|
|
393
|
+
label: translate('Filter data'),
|
|
394
|
+
action: () => this.openBrowser('filters'),
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
items.push(
|
|
398
|
+
{
|
|
399
|
+
label: translate('Open caption'),
|
|
400
|
+
action: () => this.openCaption(),
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
label: this.help.displayLabel('SEARCH'),
|
|
404
|
+
action: () => this.search(),
|
|
405
|
+
}
|
|
406
|
+
)
|
|
407
|
+
return items
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
getSharedContextMenuItems(event) {
|
|
411
|
+
const items = []
|
|
412
|
+
if (this.properties.urls.routing) {
|
|
413
|
+
items.push('-', {
|
|
414
|
+
label: translate('Directions from here'),
|
|
415
|
+
action: () => this.openExternalRouting(event),
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
if (this.properties.urls.edit_in_osm) {
|
|
419
|
+
items.push('-', {
|
|
420
|
+
label: translate('Edit in OpenStreetMap'),
|
|
421
|
+
action: () => this.editInOSM(event),
|
|
422
|
+
})
|
|
423
|
+
}
|
|
424
|
+
return items
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
onContextMenu(event) {
|
|
428
|
+
const items = this.getOwnContextMenuItems(event).concat(
|
|
429
|
+
this.getSharedContextMenuItems(event)
|
|
430
|
+
)
|
|
431
|
+
this.contextmenu.open(event.originalEvent, items)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Merge the given schema with the default one
|
|
435
|
+
// Missing keys inside the schema are merged with the default ones.
|
|
436
|
+
overrideSchema(schema) {
|
|
437
|
+
for (const [key, extra] of Object.entries(schema)) {
|
|
438
|
+
SCHEMA[key] = Object.assign({}, SCHEMA[key], extra)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
search() {
|
|
443
|
+
if (this._leafletMap._controls.search) this._leafletMap._controls.search.open()
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
hasEditMode() {
|
|
447
|
+
const editMode = this.properties.editMode
|
|
448
|
+
return editMode === 'simple' || editMode === 'advanced'
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
getProperty(key, feature) {
|
|
452
|
+
if (feature) {
|
|
453
|
+
const value = this.rules.getOption(key, feature)
|
|
454
|
+
if (value !== undefined) return value
|
|
455
|
+
}
|
|
456
|
+
if (Utils.usableOption(this.properties, key)) return this.properties[key]
|
|
457
|
+
return SCHEMA[key]?.default
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
getOption(key, feature) {
|
|
461
|
+
// TODO: remove when umap.forms.js is refactored and does not call blindly
|
|
462
|
+
// obj.getOption anymore
|
|
463
|
+
return this.getProperty(key, feature)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
getGeoContext() {
|
|
467
|
+
const bounds = this._leafletMap.getBounds()
|
|
468
|
+
const center = this._leafletMap.getCenter()
|
|
469
|
+
const context = {
|
|
470
|
+
bbox: bounds.toBBoxString(),
|
|
471
|
+
north: bounds.getNorthEast().lat,
|
|
472
|
+
east: bounds.getNorthEast().lng,
|
|
473
|
+
south: bounds.getSouthWest().lat,
|
|
474
|
+
west: bounds.getSouthWest().lng,
|
|
475
|
+
lat: center.lat,
|
|
476
|
+
lng: center.lng,
|
|
477
|
+
zoom: this._leafletMap.getZoom(),
|
|
478
|
+
}
|
|
479
|
+
context.left = context.west
|
|
480
|
+
context.bottom = context.south
|
|
481
|
+
context.right = context.east
|
|
482
|
+
context.top = context.north
|
|
483
|
+
return context
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
renderUrl(url) {
|
|
487
|
+
return Utils.greedyTemplate(url, this.getGeoContext(), true)
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
initShortcuts() {
|
|
491
|
+
const globalShortcuts = (event) => {
|
|
492
|
+
if (event.key === 'Escape') {
|
|
493
|
+
if (this.importer.dialog.visible) {
|
|
494
|
+
this.importer.dialog.close()
|
|
495
|
+
} else if (this.editEnabled && this._leafletMap.editTools.drawing()) {
|
|
496
|
+
this._leafletMap.editTools.onEscape()
|
|
497
|
+
} else if (this._leafletMap.measureTools.enabled()) {
|
|
498
|
+
this._leafletMap.measureTools.stopDrawing()
|
|
499
|
+
} else if (this.fullPanel?.isOpen()) {
|
|
500
|
+
this.fullPanel?.close()
|
|
501
|
+
} else if (this.editPanel?.isOpen()) {
|
|
502
|
+
this.editPanel?.close()
|
|
503
|
+
} else if (this.panel.isOpen()) {
|
|
504
|
+
this.panel.close()
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// From now on, only ctrl/meta shortcut
|
|
509
|
+
if (!(event.ctrlKey || event.metaKey) || event.shiftKey) return
|
|
510
|
+
|
|
511
|
+
if (event.key === 'f') {
|
|
512
|
+
event.stopPropagation()
|
|
513
|
+
event.preventDefault()
|
|
514
|
+
this.search()
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* Edit mode only shortcuts */
|
|
518
|
+
if (!this.hasEditMode()) return
|
|
519
|
+
|
|
520
|
+
// Edit mode Off
|
|
521
|
+
if (!this.editEnabled) {
|
|
522
|
+
switch (event.key) {
|
|
523
|
+
case 'e':
|
|
524
|
+
event.stopPropagation()
|
|
525
|
+
event.preventDefault()
|
|
526
|
+
this.enableEdit()
|
|
527
|
+
break
|
|
528
|
+
}
|
|
529
|
+
return
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Edit mode on
|
|
533
|
+
let used = true
|
|
534
|
+
switch (event.key) {
|
|
535
|
+
case 'e':
|
|
536
|
+
if (!SAVEMANAGER.isDirty) this.disableEdit()
|
|
537
|
+
break
|
|
538
|
+
case 's':
|
|
539
|
+
if (SAVEMANAGER.isDirty) this.saveAll()
|
|
540
|
+
break
|
|
541
|
+
case 'z':
|
|
542
|
+
if (SAVEMANAGER.isDirty) this.askForReset()
|
|
543
|
+
break
|
|
544
|
+
case 'm':
|
|
545
|
+
this._leafletMap.editTools.startMarker()
|
|
546
|
+
break
|
|
547
|
+
case 'p':
|
|
548
|
+
this._leafletMap.editTools.startPolygon()
|
|
549
|
+
break
|
|
550
|
+
case 'l':
|
|
551
|
+
this._leafletMap.editTools.startPolyline()
|
|
552
|
+
break
|
|
553
|
+
case 'i':
|
|
554
|
+
this.importer.open()
|
|
555
|
+
break
|
|
556
|
+
case 'o':
|
|
557
|
+
this.importer.openFiles()
|
|
558
|
+
break
|
|
559
|
+
case 'h':
|
|
560
|
+
this.help.showGetStarted()
|
|
561
|
+
break
|
|
562
|
+
default:
|
|
563
|
+
used = false
|
|
564
|
+
}
|
|
565
|
+
if (used) {
|
|
566
|
+
event.stopPropagation()
|
|
567
|
+
event.preventDefault()
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
document.addEventListener('keydown', globalShortcuts)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
async initDataLayers(datalayers) {
|
|
574
|
+
datalayers = datalayers || this.properties.datalayers
|
|
575
|
+
for (const options of datalayers) {
|
|
576
|
+
// `false` to not propagate syncing elements served from uMap
|
|
577
|
+
this.createDataLayer(options, false)
|
|
578
|
+
}
|
|
579
|
+
this.datalayersLoaded = true
|
|
580
|
+
this.fire('datalayersloaded')
|
|
581
|
+
const toLoad = []
|
|
582
|
+
for (const datalayer of this.datalayersIndex) {
|
|
583
|
+
if (datalayer.showAtLoad()) toLoad.push(datalayer.show())
|
|
584
|
+
}
|
|
585
|
+
await Promise.all(toLoad)
|
|
586
|
+
this.dataloaded = true
|
|
587
|
+
this.fire('dataloaded')
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
createDataLayer(options = {}, sync = true) {
|
|
591
|
+
options.name =
|
|
592
|
+
options.name || `${translate('Layer')} ${this.datalayersIndex.length + 1}`
|
|
593
|
+
const datalayer = new DataLayer(this, this._leafletMap, options)
|
|
594
|
+
|
|
595
|
+
if (sync !== false) {
|
|
596
|
+
datalayer.sync.upsert(datalayer.options)
|
|
597
|
+
}
|
|
598
|
+
return datalayer
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
newDataLayer() {
|
|
602
|
+
const datalayer = this.createDataLayer({})
|
|
603
|
+
datalayer.edit()
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
reindexDataLayers() {
|
|
607
|
+
this.eachDataLayer((datalayer) => datalayer.reindex())
|
|
608
|
+
this.onDataLayersChanged()
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
indexDatalayers() {
|
|
612
|
+
const panes = this._leafletMap.getPane('overlayPane')
|
|
613
|
+
|
|
614
|
+
this.datalayersIndex = []
|
|
615
|
+
for (const pane of panes.children) {
|
|
616
|
+
if (!pane.dataset || !pane.dataset.id) continue
|
|
617
|
+
this.datalayersIndex.push(this.datalayers[pane.dataset.id])
|
|
618
|
+
}
|
|
619
|
+
this.onDataLayersChanged()
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
onceDatalayersLoaded(callback, context) {
|
|
623
|
+
// Once datalayers **metadata** have been loaded
|
|
624
|
+
if (this.datalayersLoaded) {
|
|
625
|
+
callback.call(context || this, this)
|
|
626
|
+
} else {
|
|
627
|
+
this._leafletMap.once('datalayersloaded', callback, context)
|
|
628
|
+
}
|
|
629
|
+
return this
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
onceDataLoaded(callback, context) {
|
|
633
|
+
// Once datalayers **data** have been loaded
|
|
634
|
+
if (this.dataloaded) {
|
|
635
|
+
callback.call(context || this, this)
|
|
636
|
+
} else {
|
|
637
|
+
this._leafletMap.once('dataloaded', callback, context || this)
|
|
638
|
+
}
|
|
639
|
+
return this
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
onDataLayersChanged() {
|
|
643
|
+
if (this.browser) this.browser.update()
|
|
644
|
+
this.caption.refresh()
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async saveAll() {
|
|
648
|
+
if (!SAVEMANAGER.isDirty) return
|
|
649
|
+
if (this._defaultExtent) this._setCenterAndZoom()
|
|
650
|
+
this.backup()
|
|
651
|
+
await SAVEMANAGER.save()
|
|
652
|
+
// Do a blind render for now, as we are not sure what could
|
|
653
|
+
// have changed, we'll be more subtil when we'll remove the
|
|
654
|
+
// save action
|
|
655
|
+
this.render(['name', 'user', 'permissions'])
|
|
656
|
+
if (!this._leafletMap.listens('saved')) {
|
|
657
|
+
// When we save only layers, we don't have the map feedback message
|
|
658
|
+
this._leafletMap.on('saved', () => {
|
|
659
|
+
Alert.success(translate('Map has been saved!'))
|
|
660
|
+
})
|
|
661
|
+
}
|
|
662
|
+
this.fire('saved')
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
getDisplayName() {
|
|
666
|
+
return this.properties.name || translate('Untitled map')
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
backup() {
|
|
670
|
+
this.backupProperties()
|
|
671
|
+
this._datalayersIndex_bk = [].concat(this.datalayersIndex)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
backupProperties() {
|
|
675
|
+
this._backupProperties = Object.assign({}, this.properties)
|
|
676
|
+
this._backupProperties.tilelayer = Object.assign({}, this.properties.tilelayer)
|
|
677
|
+
this._backupProperties.limitBounds = Object.assign({}, this.properties.limitBounds)
|
|
678
|
+
this._backupProperties.permissions = Object.assign({}, this.permissions.properties)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
resetProperties() {
|
|
682
|
+
this.properties = Object.assign({}, this._backupProperties)
|
|
683
|
+
this.properties.tilelayer = Object.assign({}, this._backupProperties.tilelayer)
|
|
684
|
+
this.permissions.properties = Object.assign({}, this._backupProperties.permissions)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
setProperties(newProperties) {
|
|
688
|
+
for (const key of Object.keys(SCHEMA)) {
|
|
689
|
+
if (newProperties[key] !== undefined) {
|
|
690
|
+
this.properties[key] = newProperties[key]
|
|
691
|
+
if (key === 'rules') this.rules.load()
|
|
692
|
+
if (key === 'slideshow') this.slideshow.load()
|
|
693
|
+
// TODO: sync ?
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
hasData() {
|
|
699
|
+
for (const datalayer of this.datalayersIndex) {
|
|
700
|
+
if (datalayer.hasData()) return true
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
hasLayers() {
|
|
705
|
+
return Boolean(this.datalayersIndex.length)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
allProperties() {
|
|
709
|
+
return [].concat(...this.datalayersIndex.map((dl) => dl.allProperties()))
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
sortedValues(property) {
|
|
713
|
+
return []
|
|
714
|
+
.concat(...this.datalayersIndex.map((dl) => dl.sortedValues(property)))
|
|
715
|
+
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
|
716
|
+
.sort(U.Utils.naturalSort)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
editCaption() {
|
|
720
|
+
if (!this.editEnabled) return
|
|
721
|
+
if (this.properties.editMode !== 'advanced') return
|
|
722
|
+
const container = DomUtil.create('div', 'umap-edit-container')
|
|
723
|
+
const metadataFields = ['properties.name', 'properties.description']
|
|
724
|
+
|
|
725
|
+
DomUtil.createTitle(container, translate('Edit map details'), 'icon-caption')
|
|
726
|
+
const builder = new U.FormBuilder(this, metadataFields, {
|
|
727
|
+
className: 'map-metadata',
|
|
728
|
+
umap: this,
|
|
729
|
+
})
|
|
730
|
+
const form = builder.build()
|
|
731
|
+
container.appendChild(form)
|
|
732
|
+
|
|
733
|
+
const credits = DomUtil.createFieldset(container, translate('Credits'))
|
|
734
|
+
const creditsFields = [
|
|
735
|
+
'properties.licence',
|
|
736
|
+
'properties.shortCredit',
|
|
737
|
+
'properties.longCredit',
|
|
738
|
+
'properties.permanentCredit',
|
|
739
|
+
'properties.permanentCreditBackground',
|
|
740
|
+
]
|
|
741
|
+
const creditsBuilder = new U.FormBuilder(this, creditsFields, { umap: this })
|
|
742
|
+
credits.appendChild(creditsBuilder.build())
|
|
743
|
+
this.editPanel.open({ content: container })
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
_editControls(container) {
|
|
747
|
+
let UIFields = []
|
|
748
|
+
for (const name of this._leafletMap.HIDDABLE_CONTROLS) {
|
|
749
|
+
UIFields.push(`properties.${name}Control`)
|
|
750
|
+
}
|
|
751
|
+
UIFields = UIFields.concat([
|
|
752
|
+
'properties.moreControl',
|
|
753
|
+
'properties.scrollWheelZoom',
|
|
754
|
+
'properties.miniMap',
|
|
755
|
+
'properties.scaleControl',
|
|
756
|
+
'properties.onLoadPanel',
|
|
757
|
+
'properties.defaultView',
|
|
758
|
+
'properties.displayPopupFooter',
|
|
759
|
+
'properties.captionBar',
|
|
760
|
+
'properties.captionMenus',
|
|
761
|
+
])
|
|
762
|
+
const builder = new U.FormBuilder(this, UIFields, { umap: this })
|
|
763
|
+
const controlsOptions = DomUtil.createFieldset(
|
|
764
|
+
container,
|
|
765
|
+
translate('User interface options')
|
|
766
|
+
)
|
|
767
|
+
controlsOptions.appendChild(builder.build())
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
_editShapeProperties(container) {
|
|
771
|
+
const shapeOptions = [
|
|
772
|
+
'properties.color',
|
|
773
|
+
'properties.iconClass',
|
|
774
|
+
'properties.iconUrl',
|
|
775
|
+
'properties.iconOpacity',
|
|
776
|
+
'properties.opacity',
|
|
777
|
+
'properties.weight',
|
|
778
|
+
'properties.fill',
|
|
779
|
+
'properties.fillColor',
|
|
780
|
+
'properties.fillOpacity',
|
|
781
|
+
'properties.smoothFactor',
|
|
782
|
+
'properties.dashArray',
|
|
783
|
+
]
|
|
784
|
+
|
|
785
|
+
const builder = new U.FormBuilder(this, shapeOptions, { umap: this })
|
|
786
|
+
const defaultShapeProperties = DomUtil.createFieldset(
|
|
787
|
+
container,
|
|
788
|
+
translate('Default shape properties')
|
|
789
|
+
)
|
|
790
|
+
defaultShapeProperties.appendChild(builder.build())
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
_editDefaultProperties(container) {
|
|
794
|
+
const optionsFields = [
|
|
795
|
+
'properties.zoomTo',
|
|
796
|
+
'properties.easing',
|
|
797
|
+
'properties.labelKey',
|
|
798
|
+
'properties.sortKey',
|
|
799
|
+
'properties.filterKey',
|
|
800
|
+
'properties.facetKey',
|
|
801
|
+
'properties.slugKey',
|
|
802
|
+
]
|
|
803
|
+
|
|
804
|
+
const builder = new U.FormBuilder(this, optionsFields, { umap: this })
|
|
805
|
+
const defaultProperties = DomUtil.createFieldset(
|
|
806
|
+
container,
|
|
807
|
+
translate('Default properties')
|
|
808
|
+
)
|
|
809
|
+
defaultProperties.appendChild(builder.build())
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
_editInteractionsProperties(container) {
|
|
813
|
+
const popupFields = [
|
|
814
|
+
'properties.popupShape',
|
|
815
|
+
'properties.popupTemplate',
|
|
816
|
+
'properties.popupContentTemplate',
|
|
817
|
+
'properties.showLabel',
|
|
818
|
+
'properties.labelDirection',
|
|
819
|
+
'properties.labelInteractive',
|
|
820
|
+
'properties.outlinkTarget',
|
|
821
|
+
]
|
|
822
|
+
const builder = new U.FormBuilder(this, popupFields, { umap: this })
|
|
823
|
+
const popupFieldset = DomUtil.createFieldset(
|
|
824
|
+
container,
|
|
825
|
+
translate('Default interaction options')
|
|
826
|
+
)
|
|
827
|
+
popupFieldset.appendChild(builder.build())
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
_editTilelayer(container) {
|
|
831
|
+
if (!Utils.isObject(this.properties.tilelayer)) {
|
|
832
|
+
this.properties.tilelayer = {}
|
|
833
|
+
}
|
|
834
|
+
const tilelayerFields = [
|
|
835
|
+
[
|
|
836
|
+
'properties.tilelayer.name',
|
|
837
|
+
{ handler: 'BlurInput', placeholder: translate('display name') },
|
|
838
|
+
],
|
|
839
|
+
[
|
|
840
|
+
'properties.tilelayer.url_template',
|
|
841
|
+
{
|
|
842
|
+
handler: 'BlurInput',
|
|
843
|
+
helpText: `${translate('Supported scheme')}: http://{s}.domain.com/{z}/{x}/{y}.png`,
|
|
844
|
+
placeholder: 'url',
|
|
845
|
+
type: 'url',
|
|
846
|
+
},
|
|
847
|
+
],
|
|
848
|
+
[
|
|
849
|
+
'properties.tilelayer.maxZoom',
|
|
850
|
+
{
|
|
851
|
+
handler: 'BlurIntInput',
|
|
852
|
+
placeholder: translate('max zoom'),
|
|
853
|
+
min: 0,
|
|
854
|
+
max: this.properties.maxZoomLimit,
|
|
855
|
+
},
|
|
856
|
+
],
|
|
857
|
+
[
|
|
858
|
+
'properties.tilelayer.minZoom',
|
|
859
|
+
{
|
|
860
|
+
handler: 'BlurIntInput',
|
|
861
|
+
placeholder: translate('min zoom'),
|
|
862
|
+
min: 0,
|
|
863
|
+
max: this.properties.maxZoomLimit,
|
|
864
|
+
},
|
|
865
|
+
],
|
|
866
|
+
[
|
|
867
|
+
'properties.tilelayer.attribution',
|
|
868
|
+
{ handler: 'BlurInput', placeholder: translate('attribution') },
|
|
869
|
+
],
|
|
870
|
+
[
|
|
871
|
+
'properties.tilelayer.tms',
|
|
872
|
+
{ handler: 'Switch', label: translate('TMS format') },
|
|
873
|
+
],
|
|
874
|
+
]
|
|
875
|
+
const customTilelayer = DomUtil.createFieldset(
|
|
876
|
+
container,
|
|
877
|
+
translate('Custom background')
|
|
878
|
+
)
|
|
879
|
+
const builder = new U.FormBuilder(this, tilelayerFields, { umap: this })
|
|
880
|
+
customTilelayer.appendChild(builder.build())
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
_editOverlay(container) {
|
|
884
|
+
if (!Utils.isObject(this.properties.overlay)) {
|
|
885
|
+
this.properties.overlay = {}
|
|
886
|
+
}
|
|
887
|
+
const overlayFields = [
|
|
888
|
+
[
|
|
889
|
+
'properties.overlay.url_template',
|
|
890
|
+
{
|
|
891
|
+
handler: 'BlurInput',
|
|
892
|
+
helpText: `${translate('Supported scheme')}: http://{s}.domain.com/{z}/{x}/{y}.png`,
|
|
893
|
+
placeholder: 'url',
|
|
894
|
+
label: translate('Background overlay url'),
|
|
895
|
+
type: 'url',
|
|
896
|
+
},
|
|
897
|
+
],
|
|
898
|
+
[
|
|
899
|
+
'properties.overlay.maxZoom',
|
|
900
|
+
{
|
|
901
|
+
handler: 'BlurIntInput',
|
|
902
|
+
placeholder: translate('max zoom'),
|
|
903
|
+
min: 0,
|
|
904
|
+
max: this.properties.maxZoomLimit,
|
|
905
|
+
},
|
|
906
|
+
],
|
|
907
|
+
[
|
|
908
|
+
'properties.overlay.minZoom',
|
|
909
|
+
{
|
|
910
|
+
handler: 'BlurIntInput',
|
|
911
|
+
placeholder: translate('min zoom'),
|
|
912
|
+
min: 0,
|
|
913
|
+
max: this.properties.maxZoomLimit,
|
|
914
|
+
},
|
|
915
|
+
],
|
|
916
|
+
[
|
|
917
|
+
'properties.overlay.attribution',
|
|
918
|
+
{ handler: 'BlurInput', placeholder: translate('attribution') },
|
|
919
|
+
],
|
|
920
|
+
[
|
|
921
|
+
'properties.overlay.opacity',
|
|
922
|
+
{ handler: 'Range', min: 0, max: 1, step: 0.1, label: translate('Opacity') },
|
|
923
|
+
],
|
|
924
|
+
['properties.overlay.tms', { handler: 'Switch', label: translate('TMS format') }],
|
|
925
|
+
]
|
|
926
|
+
const overlay = DomUtil.createFieldset(container, translate('Custom overlay'))
|
|
927
|
+
const builder = new U.FormBuilder(this, overlayFields, { umap: this })
|
|
928
|
+
overlay.appendChild(builder.build())
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
_editBounds(container) {
|
|
932
|
+
if (!Utils.isObject(this.properties.limitBounds)) {
|
|
933
|
+
this.properties.limitBounds = {}
|
|
934
|
+
}
|
|
935
|
+
const limitBounds = DomUtil.createFieldset(container, translate('Limit bounds'))
|
|
936
|
+
const boundsFields = [
|
|
937
|
+
[
|
|
938
|
+
'properties.limitBounds.south',
|
|
939
|
+
{ handler: 'BlurFloatInput', placeholder: translate('max South') },
|
|
940
|
+
],
|
|
941
|
+
[
|
|
942
|
+
'properties.limitBounds.west',
|
|
943
|
+
{ handler: 'BlurFloatInput', placeholder: translate('max West') },
|
|
944
|
+
],
|
|
945
|
+
[
|
|
946
|
+
'properties.limitBounds.north',
|
|
947
|
+
{ handler: 'BlurFloatInput', placeholder: translate('max North') },
|
|
948
|
+
],
|
|
949
|
+
[
|
|
950
|
+
'properties.limitBounds.east',
|
|
951
|
+
{ handler: 'BlurFloatInput', placeholder: translate('max East') },
|
|
952
|
+
],
|
|
953
|
+
]
|
|
954
|
+
const boundsBuilder = new U.FormBuilder(this, boundsFields, { umap: this })
|
|
955
|
+
limitBounds.appendChild(boundsBuilder.build())
|
|
956
|
+
const boundsButtons = DomUtil.create('div', 'button-bar half', limitBounds)
|
|
957
|
+
DomUtil.createButton(
|
|
958
|
+
'button',
|
|
959
|
+
boundsButtons,
|
|
960
|
+
translate('Use current bounds'),
|
|
961
|
+
function () {
|
|
962
|
+
const bounds = this._leafletMap.getBounds()
|
|
963
|
+
this.properties.limitBounds.south = LeafletUtil.formatNum(bounds.getSouth())
|
|
964
|
+
this.properties.limitBounds.west = LeafletUtil.formatNum(bounds.getWest())
|
|
965
|
+
this.properties.limitBounds.north = LeafletUtil.formatNum(bounds.getNorth())
|
|
966
|
+
this.properties.limitBounds.east = LeafletUtil.formatNum(bounds.getEast())
|
|
967
|
+
boundsBuilder.fetchAll()
|
|
968
|
+
|
|
969
|
+
this.sync.update(this, 'properties.limitBounds', this.properties.limitBounds)
|
|
970
|
+
this.isDirty = true
|
|
971
|
+
this._leafletMap.handleLimitBounds()
|
|
972
|
+
},
|
|
973
|
+
this
|
|
974
|
+
)
|
|
975
|
+
DomUtil.createButton(
|
|
976
|
+
'button',
|
|
977
|
+
boundsButtons,
|
|
978
|
+
translate('Empty'),
|
|
979
|
+
function () {
|
|
980
|
+
this.properties.limitBounds.south = null
|
|
981
|
+
this.properties.limitBounds.west = null
|
|
982
|
+
this.properties.limitBounds.north = null
|
|
983
|
+
this.properties.limitBounds.east = null
|
|
984
|
+
boundsBuilder.fetchAll()
|
|
985
|
+
this.isDirty = true
|
|
986
|
+
this._leafletMap.handleLimitBounds()
|
|
987
|
+
},
|
|
988
|
+
this
|
|
989
|
+
)
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
_editSlideshow(container) {
|
|
993
|
+
const slideshow = DomUtil.createFieldset(container, translate('Slideshow'))
|
|
994
|
+
const slideshowFields = [
|
|
995
|
+
[
|
|
996
|
+
'properties.slideshow.active',
|
|
997
|
+
{ handler: 'Switch', label: translate('Activate slideshow mode') },
|
|
998
|
+
],
|
|
999
|
+
[
|
|
1000
|
+
'properties.slideshow.delay',
|
|
1001
|
+
{
|
|
1002
|
+
handler: 'SlideshowDelay',
|
|
1003
|
+
helpText: translate('Delay between two transitions when in play mode'),
|
|
1004
|
+
},
|
|
1005
|
+
],
|
|
1006
|
+
[
|
|
1007
|
+
'properties.slideshow.easing',
|
|
1008
|
+
{
|
|
1009
|
+
handler: 'Switch',
|
|
1010
|
+
label: translate('Animated transitions'),
|
|
1011
|
+
inheritable: true,
|
|
1012
|
+
},
|
|
1013
|
+
],
|
|
1014
|
+
[
|
|
1015
|
+
'properties.slideshow.autoplay',
|
|
1016
|
+
{ handler: 'Switch', label: translate('Autostart when map is loaded') },
|
|
1017
|
+
],
|
|
1018
|
+
]
|
|
1019
|
+
const slideshowBuilder = new U.FormBuilder(this, slideshowFields, {
|
|
1020
|
+
callback: () => {
|
|
1021
|
+
this.slideshow.load()
|
|
1022
|
+
// FIXME when we refactor formbuilder: this callback is called in a 'postsync'
|
|
1023
|
+
// event, which comes after the call of `setter` method, which will call the
|
|
1024
|
+
// map.render method, which should do this redraw.
|
|
1025
|
+
this.bottomBar.redraw()
|
|
1026
|
+
},
|
|
1027
|
+
umap: this,
|
|
1028
|
+
})
|
|
1029
|
+
slideshow.appendChild(slideshowBuilder.build())
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
_editSync(container) {
|
|
1033
|
+
const sync = DomUtil.createFieldset(container, translate('Real-time collaboration'))
|
|
1034
|
+
const builder = new U.FormBuilder(this, ['properties.syncEnabled'], { umap: this })
|
|
1035
|
+
sync.appendChild(builder.build())
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
_advancedActions(container) {
|
|
1039
|
+
const advancedActions = DomUtil.createFieldset(
|
|
1040
|
+
container,
|
|
1041
|
+
translate('Advanced actions')
|
|
1042
|
+
)
|
|
1043
|
+
const advancedButtons = DomUtil.create('div', 'button-bar half', advancedActions)
|
|
1044
|
+
if (this.permissions.isOwner()) {
|
|
1045
|
+
const deleteButton = Utils.loadTemplate(`
|
|
1046
|
+
<button class="button" type="button">
|
|
1047
|
+
<i class="icon icon-24 icon-delete"></i>${translate('Delete')}
|
|
1048
|
+
</button>`)
|
|
1049
|
+
deleteButton.addEventListener('click', () => this.del())
|
|
1050
|
+
advancedButtons.appendChild(deleteButton)
|
|
1051
|
+
|
|
1052
|
+
DomUtil.createButton(
|
|
1053
|
+
'button umap-empty',
|
|
1054
|
+
advancedButtons,
|
|
1055
|
+
translate('Clear data'),
|
|
1056
|
+
this.emptyDataLayers,
|
|
1057
|
+
this
|
|
1058
|
+
)
|
|
1059
|
+
DomUtil.createButton(
|
|
1060
|
+
'button umap-empty',
|
|
1061
|
+
advancedButtons,
|
|
1062
|
+
translate('Remove layers'),
|
|
1063
|
+
this.removeDataLayers,
|
|
1064
|
+
this
|
|
1065
|
+
)
|
|
1066
|
+
}
|
|
1067
|
+
DomUtil.createButton(
|
|
1068
|
+
'button umap-clone',
|
|
1069
|
+
advancedButtons,
|
|
1070
|
+
translate('Clone this map'),
|
|
1071
|
+
this.clone,
|
|
1072
|
+
this
|
|
1073
|
+
)
|
|
1074
|
+
DomUtil.createButton(
|
|
1075
|
+
'button umap-download',
|
|
1076
|
+
advancedButtons,
|
|
1077
|
+
translate('Open share & download panel'),
|
|
1078
|
+
this.share.open,
|
|
1079
|
+
this.share
|
|
1080
|
+
)
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
edit() {
|
|
1084
|
+
if (!this.editEnabled) return
|
|
1085
|
+
if (this.properties.editMode !== 'advanced') return
|
|
1086
|
+
const container = DomUtil.create('div')
|
|
1087
|
+
DomUtil.createTitle(
|
|
1088
|
+
container,
|
|
1089
|
+
translate('Map advanced properties'),
|
|
1090
|
+
'icon-settings'
|
|
1091
|
+
)
|
|
1092
|
+
this._editControls(container)
|
|
1093
|
+
this._editShapeProperties(container)
|
|
1094
|
+
this._editDefaultProperties(container)
|
|
1095
|
+
this._editInteractionsProperties(container)
|
|
1096
|
+
this.rules.edit(container)
|
|
1097
|
+
this._editTilelayer(container)
|
|
1098
|
+
this._editOverlay(container)
|
|
1099
|
+
this._editBounds(container)
|
|
1100
|
+
this._editSlideshow(container)
|
|
1101
|
+
if (this.properties.websocketEnabled) {
|
|
1102
|
+
this._editSync(container)
|
|
1103
|
+
}
|
|
1104
|
+
this._advancedActions(container)
|
|
1105
|
+
|
|
1106
|
+
this.editPanel.open({ content: container, className: 'dark' })
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
reset() {
|
|
1110
|
+
if (this._leafletMap.editTools) this._leafletMap.editTools.stopDrawing()
|
|
1111
|
+
this.resetProperties()
|
|
1112
|
+
this.datalayersIndex = [].concat(this._datalayersIndex_bk)
|
|
1113
|
+
// Iter over all datalayers, including deleted if any.
|
|
1114
|
+
for (const datalayer of Object.values(this.datalayers)) {
|
|
1115
|
+
if (datalayer.isDeleted) datalayer.connectToMap()
|
|
1116
|
+
if (datalayer.isDirty) datalayer.reset()
|
|
1117
|
+
}
|
|
1118
|
+
this.ensurePanesOrder()
|
|
1119
|
+
this._leafletMap.initTileLayers()
|
|
1120
|
+
this.onDataLayersChanged()
|
|
1121
|
+
this.isDirty = !this.id
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
async save() {
|
|
1125
|
+
this.rules.commit()
|
|
1126
|
+
const geojson = {
|
|
1127
|
+
type: 'Feature',
|
|
1128
|
+
geometry: this.geometry(),
|
|
1129
|
+
properties: this.exportProperties(),
|
|
1130
|
+
}
|
|
1131
|
+
const formData = new FormData()
|
|
1132
|
+
formData.append('name', this.properties.name)
|
|
1133
|
+
formData.append('center', JSON.stringify(this.geometry()))
|
|
1134
|
+
formData.append('settings', JSON.stringify(geojson))
|
|
1135
|
+
const uri = this.urls.get('map_save', { map_id: this.id })
|
|
1136
|
+
const [data, _, error] = await this.server.post(uri, {}, formData)
|
|
1137
|
+
// FIXME: login_required response will not be an error, so it will not
|
|
1138
|
+
// stop code while it should
|
|
1139
|
+
if (error) {
|
|
1140
|
+
return
|
|
1141
|
+
}
|
|
1142
|
+
// TOOD: map.save may not always be the first call during save process
|
|
1143
|
+
// since SAVEMANAGER refactor
|
|
1144
|
+
if (data.login_required) {
|
|
1145
|
+
window.onLogin = () => this.saveAll()
|
|
1146
|
+
window.open(data.login_required)
|
|
1147
|
+
return
|
|
1148
|
+
}
|
|
1149
|
+
this.properties.user = data.user
|
|
1150
|
+
if (!this.id) {
|
|
1151
|
+
this.properties.id = data.id
|
|
1152
|
+
this.permissions.setProperties(data.permissions)
|
|
1153
|
+
this.permissions.commit()
|
|
1154
|
+
if (data.permissions?.anonymous_edit_url) {
|
|
1155
|
+
this._leafletMap.once('saved', () => {
|
|
1156
|
+
AlertCreation.info(
|
|
1157
|
+
translate('Your map has been created with an anonymous account!'),
|
|
1158
|
+
Number.Infinity,
|
|
1159
|
+
data.permissions.anonymous_edit_url,
|
|
1160
|
+
this.properties.urls.map_send_edit_link
|
|
1161
|
+
? this.sendEditLinkEmail.bind(this)
|
|
1162
|
+
: null
|
|
1163
|
+
)
|
|
1164
|
+
})
|
|
1165
|
+
} else {
|
|
1166
|
+
this._leafletMap.once('saved', () => {
|
|
1167
|
+
Alert.success(translate('Congratulations, your map has been created!'))
|
|
1168
|
+
})
|
|
1169
|
+
}
|
|
1170
|
+
} else {
|
|
1171
|
+
if (!this.permissions.isDirty) {
|
|
1172
|
+
// Do not override local changes to permissions,
|
|
1173
|
+
// but update in case some other editors changed them in the meantime.
|
|
1174
|
+
this.permissions.setProperties(data.permissions)
|
|
1175
|
+
this.permissions.commit()
|
|
1176
|
+
}
|
|
1177
|
+
this._leafletMap.once('saved', () => {
|
|
1178
|
+
Alert.success(data.info || translate('Map has been saved!'))
|
|
1179
|
+
})
|
|
1180
|
+
}
|
|
1181
|
+
// Update URL in case the name has changed.
|
|
1182
|
+
if (history?.pushState) {
|
|
1183
|
+
history.pushState({}, this.properties.name, data.url)
|
|
1184
|
+
} else {
|
|
1185
|
+
window.location = data.url
|
|
1186
|
+
}
|
|
1187
|
+
return true
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
exportProperties() {
|
|
1191
|
+
const properties = {}
|
|
1192
|
+
for (const key of Object.keys(SCHEMA)) {
|
|
1193
|
+
if (this.properties[key] !== undefined) {
|
|
1194
|
+
properties[key] = this.properties[key]
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return properties
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
geometry() {
|
|
1201
|
+
/* Return a GeoJSON geometry Object */
|
|
1202
|
+
const latlng = this._leafletMap.latLng(
|
|
1203
|
+
this.properties.center || this._leafletMap.getCenter()
|
|
1204
|
+
)
|
|
1205
|
+
return {
|
|
1206
|
+
type: 'Point',
|
|
1207
|
+
coordinates: [latlng.lng, latlng.lat],
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
toGeoJSON() {
|
|
1212
|
+
let features = []
|
|
1213
|
+
this.eachDataLayer((datalayer) => {
|
|
1214
|
+
if (datalayer.isVisible()) {
|
|
1215
|
+
features = features.concat(datalayer.featuresToGeoJSON())
|
|
1216
|
+
}
|
|
1217
|
+
})
|
|
1218
|
+
const geojson = {
|
|
1219
|
+
type: 'FeatureCollection',
|
|
1220
|
+
features: features,
|
|
1221
|
+
}
|
|
1222
|
+
return geojson
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
enableEdit() {
|
|
1226
|
+
document.body.classList.add('umap-edit-enabled')
|
|
1227
|
+
this.editEnabled = true
|
|
1228
|
+
this.drop.enable()
|
|
1229
|
+
this.fire('edit:enabled')
|
|
1230
|
+
this.initSyncEngine()
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
disableEdit() {
|
|
1234
|
+
if (this.isDirty) return
|
|
1235
|
+
this.drop.disable()
|
|
1236
|
+
document.body.classList.remove('umap-edit-enabled')
|
|
1237
|
+
this.editedFeature = null
|
|
1238
|
+
this.editEnabled = false
|
|
1239
|
+
this.fire('edit:disabled')
|
|
1240
|
+
this.editPanel.close()
|
|
1241
|
+
this.fullPanel.close()
|
|
1242
|
+
this.sync.stop()
|
|
1243
|
+
this._leafletMap.closeInplaceToolbar()
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
fire(name) {
|
|
1247
|
+
this._leafletMap.fire(name)
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
askForReset(e) {
|
|
1251
|
+
this.dialog
|
|
1252
|
+
.confirm(translate('Are you sure you want to cancel your changes?'))
|
|
1253
|
+
.then(() => {
|
|
1254
|
+
this.reset()
|
|
1255
|
+
this.disableEdit()
|
|
1256
|
+
})
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
async initSyncEngine() {
|
|
1260
|
+
if (this.properties.websocketEnabled === false) return
|
|
1261
|
+
if (this.properties.syncEnabled !== true) {
|
|
1262
|
+
this.sync.stop()
|
|
1263
|
+
} else {
|
|
1264
|
+
const ws_token_uri = this.urls.get('map_websocket_auth_token', {
|
|
1265
|
+
map_id: this.id,
|
|
1266
|
+
})
|
|
1267
|
+
await this.sync.authenticate(
|
|
1268
|
+
ws_token_uri,
|
|
1269
|
+
this.properties.websocketURI,
|
|
1270
|
+
this.server
|
|
1271
|
+
)
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
getSyncMetadata() {
|
|
1276
|
+
return {
|
|
1277
|
+
engine: this.sync,
|
|
1278
|
+
subject: 'map',
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
render(fields = []) {
|
|
1283
|
+
// Propagate will remove the fields it has already
|
|
1284
|
+
// processed
|
|
1285
|
+
fields = this.propagate(fields)
|
|
1286
|
+
|
|
1287
|
+
const impacts = Utils.getImpactsFromSchema(fields)
|
|
1288
|
+
for (const impact of impacts) {
|
|
1289
|
+
switch (impact) {
|
|
1290
|
+
case 'ui':
|
|
1291
|
+
this._leafletMap.renderUI()
|
|
1292
|
+
this.browser.redraw()
|
|
1293
|
+
this.topBar.redraw()
|
|
1294
|
+
this.bottomBar.redraw()
|
|
1295
|
+
break
|
|
1296
|
+
case 'data':
|
|
1297
|
+
this.eachVisibleDataLayer((datalayer) => {
|
|
1298
|
+
datalayer.redraw()
|
|
1299
|
+
})
|
|
1300
|
+
break
|
|
1301
|
+
case 'datalayer-index':
|
|
1302
|
+
this.reindexDataLayers()
|
|
1303
|
+
break
|
|
1304
|
+
case 'background':
|
|
1305
|
+
this._leafletMap.initTileLayers()
|
|
1306
|
+
break
|
|
1307
|
+
case 'bounds':
|
|
1308
|
+
this._leafletMap.handleLimitBounds()
|
|
1309
|
+
break
|
|
1310
|
+
case 'sync':
|
|
1311
|
+
this.initSyncEngine()
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// This method does a targeted update of the UI,
|
|
1317
|
+
// it whould be merged with `render`` method and the
|
|
1318
|
+
// SCHEMA at some point
|
|
1319
|
+
propagate(fields = []) {
|
|
1320
|
+
const impacts = {
|
|
1321
|
+
'properties.name': () => {
|
|
1322
|
+
Utils.eachElement('.map-name', (el) => {
|
|
1323
|
+
el.textContent = this.getDisplayName()
|
|
1324
|
+
})
|
|
1325
|
+
},
|
|
1326
|
+
user: () => {
|
|
1327
|
+
Utils.eachElement('.umap-user .username', (el) => {
|
|
1328
|
+
if (this.properties.user?.id) {
|
|
1329
|
+
el.textContent = this.properties.user.name
|
|
1330
|
+
}
|
|
1331
|
+
})
|
|
1332
|
+
},
|
|
1333
|
+
'properties.permissions': () => {
|
|
1334
|
+
const status = this.permissions.getShareStatusDisplay()
|
|
1335
|
+
if (status) {
|
|
1336
|
+
Utils.eachElement('.share-status', (el) => {
|
|
1337
|
+
el.textContent = translate('Visibility: {status}', {
|
|
1338
|
+
status: status,
|
|
1339
|
+
})
|
|
1340
|
+
})
|
|
1341
|
+
}
|
|
1342
|
+
this.topBar.redraw()
|
|
1343
|
+
},
|
|
1344
|
+
numberOfConnectedPeers: () => {
|
|
1345
|
+
Utils.eachElement('.connected-peers span', (el) => {
|
|
1346
|
+
el.textContent = this.sync.getNumberOfConnectedPeers()
|
|
1347
|
+
})
|
|
1348
|
+
},
|
|
1349
|
+
}
|
|
1350
|
+
for (const [field, impact] of Object.entries(impacts)) {
|
|
1351
|
+
if (!fields.length || fields.includes(field)) {
|
|
1352
|
+
impact()
|
|
1353
|
+
fields = fields.filter((item) => item !== field)
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return fields
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// TODO: allow to control the default datalayer
|
|
1360
|
+
// (edit and viewing)
|
|
1361
|
+
// cf https://github.com/umap-project/umap/issues/585
|
|
1362
|
+
defaultEditDataLayer() {
|
|
1363
|
+
let datalayer
|
|
1364
|
+
let fallback
|
|
1365
|
+
datalayer = this.lastUsedDataLayer
|
|
1366
|
+
if (
|
|
1367
|
+
datalayer &&
|
|
1368
|
+
!datalayer.isDataReadOnly() &&
|
|
1369
|
+
datalayer.isBrowsable() &&
|
|
1370
|
+
datalayer.isVisible()
|
|
1371
|
+
) {
|
|
1372
|
+
return datalayer
|
|
1373
|
+
}
|
|
1374
|
+
datalayer = this.findDataLayer((datalayer) => {
|
|
1375
|
+
if (!datalayer.isDataReadOnly() && datalayer.isBrowsable()) {
|
|
1376
|
+
fallback = datalayer
|
|
1377
|
+
if (datalayer.isVisible()) return true
|
|
1378
|
+
}
|
|
1379
|
+
})
|
|
1380
|
+
if (datalayer) return datalayer
|
|
1381
|
+
if (fallback) {
|
|
1382
|
+
// No datalayer visible, let's force one
|
|
1383
|
+
fallback.show()
|
|
1384
|
+
return fallback
|
|
1385
|
+
}
|
|
1386
|
+
return this.createDataLayer()
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
findDataLayer(method, context) {
|
|
1390
|
+
for (let i = this.datalayersIndex.length - 1; i >= 0; i--) {
|
|
1391
|
+
if (method.call(context, this.datalayersIndex[i])) {
|
|
1392
|
+
return this.datalayersIndex[i]
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
eachDataLayer(method, context) {
|
|
1398
|
+
for (let i = 0; i < this.datalayersIndex.length; i++) {
|
|
1399
|
+
method.call(context, this.datalayersIndex[i])
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
eachDataLayerReverse(method, context, filter) {
|
|
1404
|
+
for (let i = this.datalayersIndex.length - 1; i >= 0; i--) {
|
|
1405
|
+
if (filter && !filter.call(context, this.datalayersIndex[i])) continue
|
|
1406
|
+
method.call(context, this.datalayersIndex[i])
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
eachBrowsableDataLayer(method, context) {
|
|
1411
|
+
this.eachDataLayerReverse(method, context, (d) => d.allowBrowse())
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
eachVisibleDataLayer(method, context) {
|
|
1415
|
+
this.eachDataLayerReverse(method, context, (d) => d.isVisible())
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
eachFeature(callback, context) {
|
|
1419
|
+
this.eachBrowsableDataLayer((datalayer) => {
|
|
1420
|
+
if (datalayer.isVisible()) datalayer.eachFeature(callback, context)
|
|
1421
|
+
})
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
removeDataLayers() {
|
|
1425
|
+
this.eachDataLayerReverse((datalayer) => {
|
|
1426
|
+
datalayer._delete()
|
|
1427
|
+
})
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
emptyDataLayers() {
|
|
1431
|
+
this.eachDataLayerReverse((datalayer) => {
|
|
1432
|
+
datalayer.empty()
|
|
1433
|
+
})
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
editDatalayers() {
|
|
1437
|
+
if (!this.editEnabled) return
|
|
1438
|
+
const container = DomUtil.create('div')
|
|
1439
|
+
DomUtil.createTitle(container, translate('Manage layers'), 'icon-layers')
|
|
1440
|
+
const ul = DomUtil.create('ul', '', container)
|
|
1441
|
+
this.eachDataLayerReverse((datalayer) => {
|
|
1442
|
+
const row = DomUtil.create('li', 'orderable', ul)
|
|
1443
|
+
DomUtil.createIcon(row, 'icon-drag', translate('Drag to reorder'))
|
|
1444
|
+
datalayer.renderToolbox(row)
|
|
1445
|
+
const builder = new U.FormBuilder(
|
|
1446
|
+
datalayer,
|
|
1447
|
+
[['options.name', { handler: 'EditableText' }]],
|
|
1448
|
+
{ className: 'umap-form-inline' }
|
|
1449
|
+
)
|
|
1450
|
+
const form = builder.build()
|
|
1451
|
+
row.appendChild(form)
|
|
1452
|
+
row.classList.toggle('off', !datalayer.isVisible())
|
|
1453
|
+
row.dataset.id = stamp(datalayer)
|
|
1454
|
+
})
|
|
1455
|
+
const onReorder = (src, dst, initialIndex, finalIndex) => {
|
|
1456
|
+
const movedLayer = this.datalayers[src.dataset.id]
|
|
1457
|
+
const targetLayer = this.datalayers[dst.dataset.id]
|
|
1458
|
+
const minIndex = Math.min(movedLayer.getRank(), targetLayer.getRank())
|
|
1459
|
+
const maxIndex = Math.max(movedLayer.getRank(), targetLayer.getRank())
|
|
1460
|
+
if (finalIndex === 0) movedLayer.bringToTop()
|
|
1461
|
+
else if (finalIndex > initialIndex) movedLayer.insertBefore(targetLayer)
|
|
1462
|
+
else movedLayer.insertAfter(targetLayer)
|
|
1463
|
+
this.eachDataLayerReverse((datalayer) => {
|
|
1464
|
+
if (datalayer.getRank() >= minIndex && datalayer.getRank() <= maxIndex)
|
|
1465
|
+
datalayer.isDirty = true
|
|
1466
|
+
})
|
|
1467
|
+
this.indexDatalayers()
|
|
1468
|
+
}
|
|
1469
|
+
const orderable = new Orderable(ul, onReorder)
|
|
1470
|
+
|
|
1471
|
+
const bar = DomUtil.create('div', 'button-bar', container)
|
|
1472
|
+
DomUtil.createButton(
|
|
1473
|
+
'show-on-edit block add-datalayer button',
|
|
1474
|
+
bar,
|
|
1475
|
+
translate('Add a layer'),
|
|
1476
|
+
this.newDataLayer,
|
|
1477
|
+
this
|
|
1478
|
+
)
|
|
1479
|
+
|
|
1480
|
+
this.editPanel.open({ content: container })
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
getDataLayerByUmapId(id) {
|
|
1484
|
+
const datalayer = this.findDataLayer((d) => d.id === id)
|
|
1485
|
+
if (!datalayer) throw new Error(`Can't find datalayer with id ${id}`)
|
|
1486
|
+
return datalayer
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
firstVisibleDatalayer() {
|
|
1490
|
+
return this.findDataLayer((datalayer) => {
|
|
1491
|
+
if (datalayer.isVisible()) return true
|
|
1492
|
+
})
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
ensurePanesOrder() {
|
|
1496
|
+
this.eachDataLayer((datalayer) => {
|
|
1497
|
+
datalayer.bringToTop()
|
|
1498
|
+
})
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
openBrowser(mode) {
|
|
1502
|
+
this.onceDatalayersLoaded(() => this.browser.open(mode))
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
openCaption() {
|
|
1506
|
+
this.onceDatalayersLoaded(() => this.caption.open())
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
addAuthorLink(container) {
|
|
1510
|
+
const author = this.properties.author
|
|
1511
|
+
if (author?.name) {
|
|
1512
|
+
const el = Utils.loadTemplate(
|
|
1513
|
+
`<span class="umap-map-author"> ${translate('by')} <a href="${author.url}">${author.name}</a></span>`
|
|
1514
|
+
)
|
|
1515
|
+
container.appendChild(el)
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
async star() {
|
|
1520
|
+
if (!this.id) {
|
|
1521
|
+
return Alert.error(translate('Please save the map first'))
|
|
1522
|
+
}
|
|
1523
|
+
const url = this.urls.get('map_star', { map_id: this.id })
|
|
1524
|
+
const [data, response, error] = await this.server.post(url)
|
|
1525
|
+
if (error) {
|
|
1526
|
+
return
|
|
1527
|
+
}
|
|
1528
|
+
this.properties.starred = data.starred
|
|
1529
|
+
Alert.success(
|
|
1530
|
+
data.starred
|
|
1531
|
+
? translate('Map has been starred')
|
|
1532
|
+
: translate('Map has been unstarred')
|
|
1533
|
+
)
|
|
1534
|
+
this.render(['starred'])
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
processFileToImport(file, layer, type) {
|
|
1538
|
+
type = type || Utils.detectFileType(file)
|
|
1539
|
+
if (!type) {
|
|
1540
|
+
Alert.error(
|
|
1541
|
+
translate('Unable to detect format of file {filename}', {
|
|
1542
|
+
filename: file.name,
|
|
1543
|
+
})
|
|
1544
|
+
)
|
|
1545
|
+
return
|
|
1546
|
+
}
|
|
1547
|
+
if (type === 'umap') {
|
|
1548
|
+
this.importUmapFile(file, 'umap')
|
|
1549
|
+
} else {
|
|
1550
|
+
if (!layer) layer = this.createDataLayer({ name: file.name })
|
|
1551
|
+
layer.importFromFile(file, type)
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
async importFromUrl(uri) {
|
|
1556
|
+
const response = await this.request.get(uri)
|
|
1557
|
+
if (response?.ok) {
|
|
1558
|
+
this.importRaw(await response.text())
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
importRaw(rawData) {
|
|
1563
|
+
const importedData = JSON.parse(rawData)
|
|
1564
|
+
|
|
1565
|
+
this.setProperties(importedData.properties)
|
|
1566
|
+
|
|
1567
|
+
if (importedData.geometry) {
|
|
1568
|
+
this.properties.center = this._leafletMap.latLng(importedData.geometry)
|
|
1569
|
+
}
|
|
1570
|
+
for (const geojson of importedData.layers) {
|
|
1571
|
+
if (!geojson._umap_options && geojson._storage) {
|
|
1572
|
+
geojson._umap_options = geojson._storage
|
|
1573
|
+
delete geojson._storage
|
|
1574
|
+
}
|
|
1575
|
+
delete geojson._umap_options?.id // Never trust an id at this stage
|
|
1576
|
+
const dataLayer = this.createDataLayer(geojson._umap_options)
|
|
1577
|
+
dataLayer.fromUmapGeoJSON(geojson)
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// For now render->propagate expect a `properties.` prefix.
|
|
1581
|
+
// Remove this when we have refactored schema and render.
|
|
1582
|
+
const fields = Object.keys(importedData.properties).map(
|
|
1583
|
+
(field) => `properties.${field}`
|
|
1584
|
+
)
|
|
1585
|
+
this.render(fields)
|
|
1586
|
+
this._leafletMap._setDefaultCenter()
|
|
1587
|
+
this.isDirty = true
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
importUmapFile(file) {
|
|
1591
|
+
const reader = new FileReader()
|
|
1592
|
+
reader.readAsText(file)
|
|
1593
|
+
reader.onload = (e) => {
|
|
1594
|
+
const rawData = e.target.result
|
|
1595
|
+
try {
|
|
1596
|
+
this.importRaw(rawData)
|
|
1597
|
+
} catch (e) {
|
|
1598
|
+
console.error('Error importing data', e)
|
|
1599
|
+
U.Alert.error(
|
|
1600
|
+
translate('Invalid umap data in {filename}', { filename: file.name })
|
|
1601
|
+
)
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
async del() {
|
|
1607
|
+
this.dialog
|
|
1608
|
+
.confirm(translate('Are you sure you want to delete this map?'))
|
|
1609
|
+
.then(async () => {
|
|
1610
|
+
const url = this.urls.get('map_delete', { map_id: this.id })
|
|
1611
|
+
const [data, response, error] = await this.server.post(url)
|
|
1612
|
+
if (data.redirect) window.location = data.redirect
|
|
1613
|
+
})
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
async clone() {
|
|
1617
|
+
this.dialog
|
|
1618
|
+
.confirm(
|
|
1619
|
+
translate('Are you sure you want to clone this map and all its datalayers?')
|
|
1620
|
+
)
|
|
1621
|
+
.then(async () => {
|
|
1622
|
+
const url = this.urls.get('map_clone', { map_id: this.id })
|
|
1623
|
+
const [data, response, error] = await this.server.post(url)
|
|
1624
|
+
if (data.redirect) window.location = data.redirect
|
|
1625
|
+
})
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
async sendEditLinkEmail(formData) {
|
|
1629
|
+
const sendLink =
|
|
1630
|
+
this.properties.urls.map_send_edit_link &&
|
|
1631
|
+
this.urls.get('map_send_edit_link', {
|
|
1632
|
+
map_id: this.id,
|
|
1633
|
+
})
|
|
1634
|
+
await this.server.post(sendLink, {}, formData)
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
getLayersBounds() {
|
|
1638
|
+
const bounds = new latLngBounds()
|
|
1639
|
+
this.eachBrowsableDataLayer((d) => {
|
|
1640
|
+
if (d.isVisible()) bounds.extend(d.layer.getBounds())
|
|
1641
|
+
})
|
|
1642
|
+
return bounds
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
fitDataBounds() {
|
|
1646
|
+
const bounds = this.getLayersBounds()
|
|
1647
|
+
if (!this.hasData() || !bounds.isValid()) return false
|
|
1648
|
+
this._leafletMap.fitBounds(bounds)
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
proxyUrl(url, ttl) {
|
|
1652
|
+
if (this.properties.urls.ajax_proxy) {
|
|
1653
|
+
url = Utils.greedyTemplate(this.properties.urls.ajax_proxy, {
|
|
1654
|
+
url: encodeURIComponent(url),
|
|
1655
|
+
ttl: ttl,
|
|
1656
|
+
})
|
|
1657
|
+
}
|
|
1658
|
+
return url
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
openExternalRouting(event) {
|
|
1662
|
+
const url = this.urls.get('routing', {
|
|
1663
|
+
lat: event.latlng.lat,
|
|
1664
|
+
lng: event.latlng.lng,
|
|
1665
|
+
locale: getLocale(),
|
|
1666
|
+
zoom: this._leafletMap.getZoom(),
|
|
1667
|
+
})
|
|
1668
|
+
if (url) window.open(url)
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
editInOSM(event) {
|
|
1672
|
+
const url = this.urls.get('edit_in_osm', {
|
|
1673
|
+
lat: event.latlng.lat,
|
|
1674
|
+
lng: event.latlng.lng,
|
|
1675
|
+
zoom: Math.max(this._leafletMap.getZoom(), 16),
|
|
1676
|
+
})
|
|
1677
|
+
if (url) window.open(url)
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
setCenterAndZoom() {
|
|
1681
|
+
this._setCenterAndZoom()
|
|
1682
|
+
Alert.success(translate('The zoom and center have been modified.'))
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
_setCenterAndZoom() {
|
|
1686
|
+
this.properties.center = this._leafletMap.getCenter()
|
|
1687
|
+
this.properties.zoom = this._leafletMap.getZoom()
|
|
1688
|
+
this.isDirty = true
|
|
1689
|
+
this._defaultExtent = false
|
|
1690
|
+
}
|
|
1691
|
+
}
|