mirror of
https://github.com/umap-project/umap.git
synced 2025-05-04 21:51:50 +02:00
Compare commits
No commits in common. "8c2a0eca285a1af5d575cc4dd4566a855f4cb97b" and "ce3b8554bbd6f4749825306dfb29cd11478e9c13" have entirely different histories.
8c2a0eca28
...
ce3b8554bb
38 changed files with 2784 additions and 2768 deletions
|
@ -63,3 +63,15 @@ When the data layers are initialized, they can have two states:
|
||||||
To mark what needs to be synced with the server, uMap currently mark objects as "dirty". Something marked as "dirty" has changed on the client, but is not yet saved on the server.
|
To mark what needs to be synced with the server, uMap currently mark objects as "dirty". Something marked as "dirty" has changed on the client, but is not yet saved on the server.
|
||||||
|
|
||||||
Each map, datalayer and permission objects can be marked as "dirty". When a change is made on an object, it will mark its parent as "dirty" as well, so it can be updated accordingly.
|
Each map, datalayer and permission objects can be marked as "dirty". When a change is made on an object, it will mark its parent as "dirty" as well, so it can be updated accordingly.
|
||||||
|
|
||||||
|
### Saving data to the server with `umap.save()`
|
||||||
|
|
||||||
|
Here is what's being done when you call `map.save()`:
|
||||||
|
|
||||||
|
1. `map.saveSelf()`, posting `name`, `center` and `settings` to the server, and then
|
||||||
|
2. calls `permission.save()`, which will post the permissions to the server, and then call back
|
||||||
|
3. `map.continueSaving()`, which will take the first dirtyLayer and call
|
||||||
|
4. `datalayer.save()` on it. It does the following:
|
||||||
|
1. Post the data (`name`, `displayOnLoad`, `rank`, `settings`, and `geojson`)
|
||||||
|
2. Calls `permission.save()`, posting `edit_status` to the server, and then calling `map.continue_saving()` and remove the datalayer from `dirtyDatalayers`.
|
||||||
|
5. When the `dirtyDatalayers` list is empty, we are done.
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import Umap from '../modules/umap.js'
|
|
||||||
|
|
||||||
class UmapFragment extends HTMLElement {
|
class UmapFragment extends HTMLElement {
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
new Umap(this.firstElementChild.id, JSON.parse(this.dataset.settings))
|
new U.Map(this.firstElementChild.id, JSON.parse(this.dataset.settings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,9 @@ import { EXPORT_FORMATS } from './formatter.js'
|
||||||
import ContextMenu from './ui/contextmenu.js'
|
import ContextMenu from './ui/contextmenu.js'
|
||||||
|
|
||||||
export default class Browser {
|
export default class Browser {
|
||||||
constructor(umap, leafletMap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this._leafletMap = leafletMap
|
this.map.on('moveend', this.onMoveEnd, this)
|
||||||
this._leafletMap.on('moveend', this.onMoveEnd, this)
|
|
||||||
this.options = {
|
this.options = {
|
||||||
filter: '',
|
filter: '',
|
||||||
inBbox: false,
|
inBbox: false,
|
||||||
|
@ -83,7 +82,7 @@ export default class Browser {
|
||||||
|
|
||||||
updateDatalayer(datalayer) {
|
updateDatalayer(datalayer) {
|
||||||
// Compute once, but use it for each feature later.
|
// Compute once, but use it for each feature later.
|
||||||
this.bounds = this._leafletMap.getBounds()
|
this.bounds = this.map.getBounds()
|
||||||
const parent = DomUtil.get(this.datalayerId(datalayer))
|
const parent = DomUtil.get(this.datalayerId(datalayer))
|
||||||
// Panel is not open
|
// Panel is not open
|
||||||
if (!parent) return
|
if (!parent) return
|
||||||
|
@ -116,10 +115,10 @@ export default class Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
onFormChange() {
|
onFormChange() {
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
datalayer.resetLayer(true)
|
datalayer.resetLayer(true)
|
||||||
this.updateDatalayer(datalayer)
|
this.updateDatalayer(datalayer)
|
||||||
if (this._umap.fullPanel?.isOpen()) datalayer.tableEdit()
|
if (this.map.fullPanel?.isOpen()) datalayer.tableEdit()
|
||||||
})
|
})
|
||||||
this.toggleBadge()
|
this.toggleBadge()
|
||||||
}
|
}
|
||||||
|
@ -133,13 +132,13 @@ export default class Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFilters() {
|
hasFilters() {
|
||||||
return !!this.options.filter || this._umap.facets.isActive()
|
return !!this.options.filter || this.map.facets.isActive()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoveEnd() {
|
onMoveEnd() {
|
||||||
if (!this.isOpen()) return
|
if (!this.isOpen()) return
|
||||||
const isListDynamic = this.options.inBbox
|
const isListDynamic = this.options.inBbox
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
if (!isListDynamic && !datalayer.hasDynamicData()) return
|
if (!isListDynamic && !datalayer.hasDynamicData()) return
|
||||||
this.updateDatalayer(datalayer)
|
this.updateDatalayer(datalayer)
|
||||||
})
|
})
|
||||||
|
@ -148,7 +147,7 @@ export default class Browser {
|
||||||
update() {
|
update() {
|
||||||
if (!this.isOpen()) return
|
if (!this.isOpen()) return
|
||||||
this.dataContainer.innerHTML = ''
|
this.dataContainer.innerHTML = ''
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
this.addDataLayer(datalayer, this.dataContainer)
|
this.addDataLayer(datalayer, this.dataContainer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -187,9 +186,9 @@ export default class Browser {
|
||||||
DomEvent.on(builder.form, 'reset', () => {
|
DomEvent.on(builder.form, 'reset', () => {
|
||||||
window.setTimeout(builder.syncAll.bind(builder))
|
window.setTimeout(builder.syncAll.bind(builder))
|
||||||
})
|
})
|
||||||
if (this._umap.properties.facetKey) {
|
if (this.map.options.facetKey) {
|
||||||
fields = this._umap.facets.build()
|
fields = this.map.facets.build()
|
||||||
filtersBuilder = new L.FormBuilder(this._umap.facets, fields, {
|
filtersBuilder = new L.FormBuilder(this.map.facets, fields, {
|
||||||
callback: () => this.onFormChange(),
|
callback: () => this.onFormChange(),
|
||||||
})
|
})
|
||||||
DomEvent.on(filtersBuilder.form, 'reset', () => {
|
DomEvent.on(filtersBuilder.form, 'reset', () => {
|
||||||
|
@ -207,7 +206,7 @@ export default class Browser {
|
||||||
textContent: translate('Reset all'),
|
textContent: translate('Reset all'),
|
||||||
})
|
})
|
||||||
|
|
||||||
this._umap.panel.open({
|
this.map.panel.open({
|
||||||
content: container,
|
content: container,
|
||||||
className: 'umap-browser',
|
className: 'umap-browser',
|
||||||
})
|
})
|
||||||
|
@ -231,7 +230,7 @@ export default class Browser {
|
||||||
`)
|
`)
|
||||||
container.appendChild(toolbox)
|
container.appendChild(toolbox)
|
||||||
toggle.addEventListener('click', () => this.toggleLayers())
|
toggle.addEventListener('click', () => this.toggleLayers())
|
||||||
fitBounds.addEventListener('click', () => this._umap.fitDataBounds())
|
fitBounds.addEventListener('click', () => this.map.fitDataBounds())
|
||||||
download.addEventListener('click', () => this.downloadVisible(download))
|
download.addEventListener('click', () => this.downloadVisible(download))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +240,7 @@ export default class Browser {
|
||||||
for (const format of Object.keys(EXPORT_FORMATS)) {
|
for (const format of Object.keys(EXPORT_FORMATS)) {
|
||||||
items.push({
|
items.push({
|
||||||
label: format,
|
label: format,
|
||||||
action: () => this._umap.share.download(format),
|
action: () => this.map.share.download(format),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
menu.openBelow(element, items)
|
menu.openBelow(element, items)
|
||||||
|
@ -251,10 +250,10 @@ export default class Browser {
|
||||||
// If at least one layer is shown, hide it
|
// If at least one layer is shown, hide it
|
||||||
// otherwise show all
|
// otherwise show all
|
||||||
let allHidden = true
|
let allHidden = true
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
if (datalayer.isVisible()) allHidden = false
|
if (datalayer.isVisible()) allHidden = false
|
||||||
})
|
})
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
if (allHidden) {
|
if (allHidden) {
|
||||||
datalayer.show()
|
datalayer.show()
|
||||||
} else {
|
} else {
|
||||||
|
@ -263,7 +262,7 @@ export default class Browser {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static backButton(umap) {
|
static backButton(map) {
|
||||||
const button = DomUtil.createButtonIcon(
|
const button = DomUtil.createButtonIcon(
|
||||||
DomUtil.create('li', '', undefined),
|
DomUtil.create('li', '', undefined),
|
||||||
'icon-back',
|
'icon-back',
|
||||||
|
@ -272,7 +271,7 @@ export default class Browser {
|
||||||
// Fixme: remove me when this is merged and released
|
// Fixme: remove me when this is merged and released
|
||||||
// https://github.com/Leaflet/Leaflet/pull/9052
|
// https://github.com/Leaflet/Leaflet/pull/9052
|
||||||
DomEvent.disableClickPropagation(button)
|
DomEvent.disableClickPropagation(button)
|
||||||
DomEvent.on(button, 'click', () => umap.openBrowser())
|
DomEvent.on(button, 'click', map.openBrowser, map)
|
||||||
return button
|
return button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
|
|
||||||
export default class Caption {
|
export default class Caption {
|
||||||
constructor(umap, leafletMap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this._leafletMap = leafletMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isOpen() {
|
isOpen() {
|
||||||
|
@ -22,36 +21,38 @@ export default class Caption {
|
||||||
const hgroup = DomUtil.element({ tagName: 'hgroup', parent: container })
|
const hgroup = DomUtil.element({ tagName: 'hgroup', parent: container })
|
||||||
DomUtil.createTitle(
|
DomUtil.createTitle(
|
||||||
hgroup,
|
hgroup,
|
||||||
this._umap.getDisplayName(),
|
this.map.getDisplayName(),
|
||||||
'icon-caption icon-block',
|
'icon-caption icon-block',
|
||||||
'map-name'
|
'map-name'
|
||||||
)
|
)
|
||||||
const title = Utils.loadTemplate('<h4></h4>')
|
this.map.addAuthorLink('h4', hgroup)
|
||||||
hgroup.appendChild(title)
|
if (this.map.options.description) {
|
||||||
this._umap.addAuthorLink(title)
|
|
||||||
if (this._umap.properties.description) {
|
|
||||||
const description = DomUtil.element({
|
const description = DomUtil.element({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
className: 'umap-map-description text',
|
className: 'umap-map-description text',
|
||||||
safeHTML: Utils.toHTML(this._umap.properties.description),
|
safeHTML: Utils.toHTML(this.map.options.description),
|
||||||
parent: container,
|
parent: container,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const datalayerContainer = DomUtil.create('div', 'datalayer-container', container)
|
const datalayerContainer = DomUtil.create('div', 'datalayer-container', container)
|
||||||
this._umap.eachDataLayerReverse((datalayer) =>
|
this.map.eachDataLayerReverse((datalayer) =>
|
||||||
this.addDataLayer(datalayer, datalayerContainer)
|
this.addDataLayer(datalayer, datalayerContainer)
|
||||||
)
|
)
|
||||||
const creditsContainer = DomUtil.create('div', 'credits-container', container)
|
const creditsContainer = DomUtil.create('div', 'credits-container', container)
|
||||||
this.addCredits(creditsContainer)
|
this.addCredits(creditsContainer)
|
||||||
this._umap.panel.open({ content: container }).then(() => {
|
this.map.panel.open({ content: container }).then(() => {
|
||||||
// Create the legend when the panel is actually on the DOM
|
// Create the legend when the panel is actually on the DOM
|
||||||
this._umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
|
this.map.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
addDataLayer(datalayer, container) {
|
addDataLayer(datalayer, container) {
|
||||||
if (!datalayer.options.inCaption) return
|
if (!datalayer.options.inCaption) return
|
||||||
const p = DomUtil.create('p', `caption-item ${datalayer.cssId}`, container)
|
const p = DomUtil.create(
|
||||||
|
'p',
|
||||||
|
`caption-item ${datalayer.cssId}`,
|
||||||
|
container
|
||||||
|
)
|
||||||
const legend = DomUtil.create('span', 'datalayer-legend', p)
|
const legend = DomUtil.create('span', 'datalayer-legend', p)
|
||||||
const headline = DomUtil.create('strong', '', p)
|
const headline = DomUtil.create('strong', '', p)
|
||||||
if (datalayer.options.description) {
|
if (datalayer.options.description) {
|
||||||
|
@ -68,16 +69,16 @@ export default class Caption {
|
||||||
addCredits(container) {
|
addCredits(container) {
|
||||||
const credits = DomUtil.createFieldset(container, translate('Credits'))
|
const credits = DomUtil.createFieldset(container, translate('Credits'))
|
||||||
let title = DomUtil.add('h5', '', credits, translate('User content credits'))
|
let title = DomUtil.add('h5', '', credits, translate('User content credits'))
|
||||||
if (this._umap.properties.shortCredit || this._umap.properties.longCredit) {
|
if (this.map.options.shortCredit || this.map.options.longCredit) {
|
||||||
DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'p',
|
tagName: 'p',
|
||||||
parent: credits,
|
parent: credits,
|
||||||
safeHTML: Utils.toHTML(
|
safeHTML: Utils.toHTML(
|
||||||
this._umap.properties.longCredit || this._umap.properties.shortCredit
|
this.map.options.longCredit || this.map.options.shortCredit
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (this._umap.properties.licence) {
|
if (this.map.options.licence) {
|
||||||
const licence = DomUtil.add(
|
const licence = DomUtil.add(
|
||||||
'p',
|
'p',
|
||||||
'',
|
'',
|
||||||
|
@ -87,8 +88,8 @@ export default class Caption {
|
||||||
DomUtil.createLink(
|
DomUtil.createLink(
|
||||||
'',
|
'',
|
||||||
licence,
|
licence,
|
||||||
this._umap.properties.licence.name,
|
this.map.options.licence.name,
|
||||||
this._umap.properties.licence.url
|
this.map.options.licence.url
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
DomUtil.add('p', '', credits, translate('No licence has been set'))
|
DomUtil.add('p', '', credits, translate('No licence has been set'))
|
||||||
|
@ -99,19 +100,19 @@ export default class Caption {
|
||||||
DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'strong',
|
tagName: 'strong',
|
||||||
parent: tilelayerCredit,
|
parent: tilelayerCredit,
|
||||||
textContent: `${this._leafletMap.selectedTilelayer.options.name} `,
|
textContent: `${this.map.selected_tilelayer.options.name} `,
|
||||||
})
|
})
|
||||||
DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
parent: tilelayerCredit,
|
parent: tilelayerCredit,
|
||||||
safeHTML: this._leafletMap.selectedTilelayer.getAttribution(),
|
safeHTML: this.map.selected_tilelayer.getAttribution(),
|
||||||
})
|
})
|
||||||
const urls = {
|
const urls = {
|
||||||
leaflet: 'http://leafletjs.com',
|
leaflet: 'http://leafletjs.com',
|
||||||
django: 'https://www.djangoproject.com',
|
django: 'https://www.djangoproject.com',
|
||||||
umap: 'https://umap-project.org/',
|
umap: 'https://umap-project.org/',
|
||||||
changelog: 'https://docs.umap-project.org/en/master/changelog/',
|
changelog: 'https://docs.umap-project.org/en/master/changelog/',
|
||||||
version: this._umap.properties.umap_version,
|
version: this.map.options.umap_version,
|
||||||
}
|
}
|
||||||
const creditHTML = translate(
|
const creditHTML = translate(
|
||||||
`
|
`
|
||||||
|
|
|
@ -18,9 +18,8 @@ import {
|
||||||
import loadPopup from '../rendering/popup.js'
|
import loadPopup from '../rendering/popup.js'
|
||||||
|
|
||||||
class Feature {
|
class Feature {
|
||||||
constructor(umap, datalayer, geojson = {}, id = null) {
|
constructor(datalayer, geojson = {}, id = null) {
|
||||||
this._umap = umap
|
this.sync = datalayer.map.sync_engine.proxy(this)
|
||||||
this.sync = umap.sync_engine.proxy(this)
|
|
||||||
this._marked_for_deletion = false
|
this._marked_for_deletion = false
|
||||||
this._isDirty = false
|
this._isDirty = false
|
||||||
this._ui = null
|
this._ui = null
|
||||||
|
@ -70,6 +69,10 @@ class Feature {
|
||||||
return this._ui
|
return this._ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get map() {
|
||||||
|
return this.datalayer?.map
|
||||||
|
}
|
||||||
|
|
||||||
get center() {
|
get center() {
|
||||||
return this.ui.getCenter()
|
return this.ui.getCenter()
|
||||||
}
|
}
|
||||||
|
@ -165,7 +168,7 @@ class Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
getSlug() {
|
getSlug() {
|
||||||
return this.properties[this._umap.getProperty('slugKey') || 'name'] || ''
|
return this.properties[this.map.getOption('slugKey') || 'name'] || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermalink() {
|
getPermalink() {
|
||||||
|
@ -193,10 +196,10 @@ class Feature {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO deal with an event instead?
|
// TODO deal with an event instead?
|
||||||
if (this._umap.slideshow) {
|
if (this.map.slideshow) {
|
||||||
this._umap.slideshow.current = this
|
this.map.slideshow.current = this
|
||||||
}
|
}
|
||||||
this._umap.currentFeature = this
|
this.map.currentFeature = this
|
||||||
this.attachPopup()
|
this.attachPopup()
|
||||||
this.ui.openPopup(latlng || this.center)
|
this.ui.openPopup(latlng || this.center)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +209,7 @@ class Feature {
|
||||||
return field.startsWith('properties.')
|
return field.startsWith('properties.')
|
||||||
})
|
})
|
||||||
if (impactData) {
|
if (impactData) {
|
||||||
if (this._umap.currentFeature === this) {
|
if (this.map.currentFeature === this) {
|
||||||
this.view()
|
this.view()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +217,7 @@ class Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
edit(event) {
|
edit(event) {
|
||||||
if (!this._umap.editEnabled || this.isReadOnly()) return
|
if (!this.map.editEnabled || this.isReadOnly()) return
|
||||||
const container = DomUtil.create('div', 'umap-feature-container')
|
const container = DomUtil.create('div', 'umap-feature-container')
|
||||||
DomUtil.createTitle(
|
DomUtil.createTitle(
|
||||||
container,
|
container,
|
||||||
|
@ -253,12 +256,12 @@ class Feature {
|
||||||
translate('Advanced actions')
|
translate('Advanced actions')
|
||||||
)
|
)
|
||||||
this.getAdvancedEditActions(advancedActions)
|
this.getAdvancedEditActions(advancedActions)
|
||||||
const onLoad = this._umap.editPanel.open({ content: container })
|
const onLoad = this.map.editPanel.open({ content: container })
|
||||||
onLoad.then(() => {
|
onLoad.then(() => {
|
||||||
builder.helpers['properties.name'].input.focus()
|
builder.helpers['properties.name'].input.focus()
|
||||||
})
|
})
|
||||||
this._umap.editedFeature = this
|
this.map.editedFeature = this
|
||||||
if (!this.ui.isOnScreen(this._umap._leafletMap.getBounds())) this.zoomTo(event)
|
if (!this.ui.isOnScreen(this.map.getBounds())) this.zoomTo(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
getAdvancedEditActions(container) {
|
getAdvancedEditActions(container) {
|
||||||
|
@ -267,7 +270,7 @@ class Feature {
|
||||||
<i class="icon icon-24 icon-delete"></i>${translate('Delete')}
|
<i class="icon icon-24 icon-delete"></i>${translate('Delete')}
|
||||||
</button>`)
|
</button>`)
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
this.confirmDelete().then(() => this._umap.editPanel.close())
|
this.confirmDelete().then(() => this.map.editPanel.close())
|
||||||
})
|
})
|
||||||
container.appendChild(button)
|
container.appendChild(button)
|
||||||
}
|
}
|
||||||
|
@ -317,25 +320,20 @@ class Feature {
|
||||||
endEdit() {}
|
endEdit() {}
|
||||||
|
|
||||||
getDisplayName(fallback) {
|
getDisplayName(fallback) {
|
||||||
|
if (fallback === undefined) fallback = this.datalayer.getName()
|
||||||
const key = this.getOption('labelKey') || 'name'
|
const key = this.getOption('labelKey') || 'name'
|
||||||
// Variables mode.
|
// Variables mode.
|
||||||
if (Utils.hasVar(key)) {
|
if (U.Utils.hasVar(key))
|
||||||
return Utils.greedyTemplate(key, this.extendedProperties())
|
return U.Utils.greedyTemplate(key, this.extendedProperties())
|
||||||
}
|
|
||||||
// Simple mode.
|
// Simple mode.
|
||||||
return (
|
return this.properties[key] || this.properties.title || fallback
|
||||||
this.properties[key] ||
|
|
||||||
this.properties.title ||
|
|
||||||
fallback ||
|
|
||||||
this.datalayer.getName()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPopupFooter() {
|
hasPopupFooter() {
|
||||||
if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) {
|
if (this.datalayer.isRemoteLayer() && this.datalayer.options.remoteData.dynamic) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return this._umap.getProperty('displayPopupFooter')
|
return this.map.getOption('displayPopupFooter')
|
||||||
}
|
}
|
||||||
|
|
||||||
getPopupClass() {
|
getPopupClass() {
|
||||||
|
@ -349,7 +347,7 @@ class Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirmDelete() {
|
async confirmDelete() {
|
||||||
const confirmed = await this._umap.dialog.confirm(
|
const confirmed = await this.map.dialog.confirm(
|
||||||
translate('Are you sure you want to delete the feature?')
|
translate('Are you sure you want to delete the feature?')
|
||||||
)
|
)
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
|
@ -361,7 +359,7 @@ class Feature {
|
||||||
|
|
||||||
del(sync) {
|
del(sync) {
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
this._umap._leafletMap.closePopup()
|
this.map.closePopup()
|
||||||
if (this.datalayer) {
|
if (this.datalayer) {
|
||||||
this.datalayer.removeFeature(this, sync)
|
this.datalayer.removeFeature(this, sync)
|
||||||
}
|
}
|
||||||
|
@ -419,37 +417,34 @@ class Feature {
|
||||||
let value = fallback
|
let value = fallback
|
||||||
if (typeof this.staticOptions[option] !== 'undefined') {
|
if (typeof this.staticOptions[option] !== 'undefined') {
|
||||||
value = this.staticOptions[option]
|
value = this.staticOptions[option]
|
||||||
} else if (Utils.usableOption(this.properties._umap_options, option)) {
|
} else if (U.Utils.usableOption(this.properties._umap_options, option)) {
|
||||||
value = this.properties._umap_options[option]
|
value = this.properties._umap_options[option]
|
||||||
} else if (this.datalayer) {
|
} else if (this.datalayer) {
|
||||||
value = this.datalayer.getOption(option, this)
|
value = this.datalayer.getOption(option, this)
|
||||||
} else {
|
} else {
|
||||||
value = this._umap.getProperty(option)
|
value = this.map.getOption(option)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
getDynamicOption(key, fallback) {
|
getDynamicOption(option, fallback) {
|
||||||
let value = this.getOption(key, fallback)
|
let value = this.getOption(option, fallback)
|
||||||
// There is a variable inside.
|
// There is a variable inside.
|
||||||
if (Utils.hasVar(value)) {
|
if (U.Utils.hasVar(value)) {
|
||||||
value = Utils.greedyTemplate(value, this.properties, true)
|
value = U.Utils.greedyTemplate(value, this.properties, true)
|
||||||
if (Utils.hasVar(value)) value = SCHEMA[key]?.default
|
if (U.Utils.hasVar(value)) value = this.map.getDefaultOption(option)
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomTo({ easing, latlng, callback } = {}) {
|
zoomTo({ easing, latlng, callback } = {}) {
|
||||||
if (easing === undefined) easing = this._umap.getProperty('easing')
|
if (easing === undefined) easing = this.map.getOption('easing')
|
||||||
if (callback) this._umap._leafletMap.once('moveend', callback.bind(this))
|
if (callback) this.map.once('moveend', callback.bind(this))
|
||||||
if (easing) {
|
if (easing) {
|
||||||
this._umap._leafletMap.flyTo(this.center, this.getBestZoom())
|
this.map.flyTo(this.center, this.getBestZoom())
|
||||||
} else {
|
} else {
|
||||||
latlng = latlng || this.center
|
latlng = latlng || this.center
|
||||||
this._umap._leafletMap.setView(
|
this.map.setView(latlng, this.getBestZoom() || this.map.getZoom())
|
||||||
latlng,
|
|
||||||
this.getBestZoom() || this._umap._leafletMap.getZoom()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,9 +494,13 @@ class Feature {
|
||||||
return [U.ToggleEditAction, U.DeleteFeatureAction]
|
return [U.ToggleEditAction, U.DeleteFeatureAction]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
isFiltered() {
|
isFiltered() {
|
||||||
const filterKeys = this.datalayer.getFilterKeys()
|
const filterKeys = this.datalayer.getFilterKeys()
|
||||||
const filter = this._umap.browser.options.filter
|
const filter = this.map.browser.options.filter
|
||||||
if (filter && !this.matchFilter(filter, filterKeys)) return true
|
if (filter && !this.matchFilter(filter, filterKeys)) return true
|
||||||
if (!this.matchFacets()) return true
|
if (!this.matchFacets()) return true
|
||||||
return false
|
return false
|
||||||
|
@ -526,10 +525,10 @@ class Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
matchFacets() {
|
matchFacets() {
|
||||||
const selected = this._umap.facets.selected
|
const selected = this.map.facets.selected
|
||||||
for (const [name, { type, min, max, choices }] of Object.entries(selected)) {
|
for (const [name, { type, min, max, choices }] of Object.entries(selected)) {
|
||||||
let value = this.properties[name]
|
let value = this.properties[name]
|
||||||
const parser = this._umap.facets.getParser(type)
|
const parser = this.map.facets.getParser(type)
|
||||||
value = parser(value)
|
value = parser(value)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'date':
|
case 'date':
|
||||||
|
@ -563,10 +562,10 @@ class Feature {
|
||||||
|
|
||||||
extendedProperties() {
|
extendedProperties() {
|
||||||
// Include context properties
|
// Include context properties
|
||||||
const properties = this._umap.getGeoContext()
|
const properties = this.map.getGeoContext()
|
||||||
const locale = L.getLocale()
|
const locale = L.getLocale()
|
||||||
if (locale) properties.locale = locale
|
if (locale) properties.locale = locale
|
||||||
if (U.lang) properties.lang = U.lang
|
if (L.lang) properties.lang = L.lang
|
||||||
properties.rank = this.getRank() + 1
|
properties.rank = this.getRank() + 1
|
||||||
properties.layer = this.datalayer.getName()
|
properties.layer = this.datalayer.getName()
|
||||||
if (this.ui._map && this.hasGeom()) {
|
if (this.ui._map && this.hasGeom()) {
|
||||||
|
@ -613,10 +612,10 @@ class Feature {
|
||||||
label: translate('Copy as GeoJSON'),
|
label: translate('Copy as GeoJSON'),
|
||||||
action: () => {
|
action: () => {
|
||||||
L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON()))
|
L.Util.copyToClipboard(JSON.stringify(this.toGeoJSON()))
|
||||||
this._umap.tooltip.open({ content: L._('✅ Copied!') })
|
this.map.tooltip.open({ content: L._('✅ Copied!') })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (this._umap.editEnabled && !this.isReadOnly()) {
|
if (this.map.editEnabled && !this.isReadOnly()) {
|
||||||
items = items.concat(this.getContextMenuEditItems(event))
|
items = items.concat(this.getContextMenuEditItems(event))
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
@ -624,7 +623,7 @@ class Feature {
|
||||||
|
|
||||||
getContextMenuEditItems() {
|
getContextMenuEditItems() {
|
||||||
let items = ['-']
|
let items = ['-']
|
||||||
if (this._umap.editedFeature !== this) {
|
if (this.map.editedFeature !== this) {
|
||||||
items.push({
|
items.push({
|
||||||
label: `${translate('Edit this feature')} (⇧+Click)`,
|
label: `${translate('Edit this feature')} (⇧+Click)`,
|
||||||
action: () => this.edit(),
|
action: () => this.edit(),
|
||||||
|
@ -632,7 +631,7 @@ class Feature {
|
||||||
}
|
}
|
||||||
items = items.concat(
|
items = items.concat(
|
||||||
{
|
{
|
||||||
label: this._umap.help.displayLabel('EDIT_FEATURE_LAYER'),
|
label: this.map.help.displayLabel('EDIT_FEATURE_LAYER'),
|
||||||
action: () => this.datalayer.edit(),
|
action: () => this.datalayer.edit(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -649,8 +648,8 @@ class Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Point extends Feature {
|
export class Point extends Feature {
|
||||||
constructor(umap, datalayer, geojson, id) {
|
constructor(datalayer, geojson, id) {
|
||||||
super(umap, datalayer, geojson, id)
|
super(datalayer, geojson, id)
|
||||||
this.staticOptions = {
|
this.staticOptions = {
|
||||||
mainColor: 'color',
|
mainColor: 'color',
|
||||||
className: 'marker',
|
className: 'marker',
|
||||||
|
@ -751,17 +750,17 @@ class Path extends Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
edit(event) {
|
edit(event) {
|
||||||
if (this._umap.editEnabled) {
|
if (this.map.editEnabled) {
|
||||||
super.edit(event)
|
super.edit(event)
|
||||||
if (!this.ui.editEnabled()) this.ui.makeGeometryEditable()
|
if (!this.ui.editEnabled()) this.ui.makeGeometryEditable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_toggleEditing(event) {
|
_toggleEditing(event) {
|
||||||
if (this._umap.editEnabled) {
|
if (this.map.editEnabled) {
|
||||||
if (this.ui.editEnabled()) {
|
if (this.ui.editEnabled()) {
|
||||||
this.endEdit()
|
this.endEdit()
|
||||||
this._umap.editPanel.close()
|
this.map.editPanel.close()
|
||||||
} else {
|
} else {
|
||||||
this.edit(event)
|
this.edit(event)
|
||||||
}
|
}
|
||||||
|
@ -787,10 +786,7 @@ class Path extends Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBestZoom() {
|
getBestZoom() {
|
||||||
return (
|
return this.getOption('zoomTo') || this.map.getBoundsZoom(this.bounds, true)
|
||||||
this.getOption('zoomTo') ||
|
|
||||||
this._umap._leafletMap.getBoundsZoom(this.bounds, true)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endEdit() {
|
endEdit() {
|
||||||
|
@ -829,14 +825,11 @@ class Path extends Feature {
|
||||||
|
|
||||||
zoomTo({ easing, callback }) {
|
zoomTo({ easing, callback }) {
|
||||||
// Use bounds instead of centroid for paths.
|
// Use bounds instead of centroid for paths.
|
||||||
easing = easing || this._umap.getProperty('easing')
|
easing = easing || this.map.getOption('easing')
|
||||||
if (easing) {
|
if (easing) {
|
||||||
this._umap._leafletMap.flyToBounds(this.bounds, this.getBestZoom())
|
this.map.flyToBounds(this.bounds, this.getBestZoom())
|
||||||
} else {
|
} else {
|
||||||
this._umap._leafletMap.fitBounds(
|
this.map.fitBounds(this.bounds, this.getBestZoom() || this.map.getZoom())
|
||||||
this.bounds,
|
|
||||||
this.getBestZoom() || this._umap._leafletMap.getZoom()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (callback) callback.call(this)
|
if (callback) callback.call(this)
|
||||||
}
|
}
|
||||||
|
@ -847,7 +840,7 @@ class Path extends Feature {
|
||||||
label: translate('Display measure'),
|
label: translate('Display measure'),
|
||||||
action: () => Alert.info(this.ui.getMeasure()),
|
action: () => Alert.info(this.ui.getMeasure()),
|
||||||
})
|
})
|
||||||
if (this._umap.editEnabled && !this.isReadOnly() && this.isMulti()) {
|
if (this.map.editEnabled && !this.isReadOnly() && this.isMulti()) {
|
||||||
items.push(...this.getContextMenuMultiItems(event))
|
items.push(...this.getContextMenuMultiItems(event))
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
@ -878,14 +871,11 @@ class Path extends Feature {
|
||||||
|
|
||||||
getContextMenuEditItems(event) {
|
getContextMenuEditItems(event) {
|
||||||
const items = super.getContextMenuEditItems(event)
|
const items = super.getContextMenuEditItems(event)
|
||||||
if (
|
if (this.map?.editedFeature !== this && this.isSameClass(this.map.editedFeature)) {
|
||||||
this._umap?.editedFeature !== this &&
|
|
||||||
this.isSameClass(this._umap.editedFeature)
|
|
||||||
) {
|
|
||||||
items.push({
|
items.push({
|
||||||
label: translate('Transfer shape to edited feature'),
|
label: translate('Transfer shape to edited feature'),
|
||||||
action: () => {
|
action: () => {
|
||||||
this.transferShape(event.latlng, this._umap.editedFeature)
|
this.transferShape(event.latlng, this.map.editedFeature)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -902,8 +892,8 @@ class Path extends Feature {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LineString extends Path {
|
export class LineString extends Path {
|
||||||
constructor(umap, datalayer, geojson, id) {
|
constructor(datalayer, geojson, id) {
|
||||||
super(umap, datalayer, geojson, id)
|
super(datalayer, geojson, id)
|
||||||
this.staticOptions = {
|
this.staticOptions = {
|
||||||
stroke: true,
|
stroke: true,
|
||||||
fill: false,
|
fill: false,
|
||||||
|
@ -987,8 +977,8 @@ export class LineString extends Path {
|
||||||
}
|
}
|
||||||
const a = toMerge[0]
|
const a = toMerge[0]
|
||||||
const b = toMerge[1]
|
const b = toMerge[1]
|
||||||
const p1 = this._umap._leafletMap.latLngToContainerPoint(a[a.length - 1])
|
const p1 = this.map.latLngToContainerPoint(a[a.length - 1])
|
||||||
const p2 = this._umap._leafletMap.latLngToContainerPoint(b[0])
|
const p2 = this.map.latLngToContainerPoint(b[0])
|
||||||
const tolerance = 5 // px on screen
|
const tolerance = 5 // px on screen
|
||||||
if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
|
if (Math.abs(p1.x - p2.x) <= tolerance && Math.abs(p1.y - p2.y) <= tolerance) {
|
||||||
a.pop()
|
a.pop()
|
||||||
|
@ -1032,7 +1022,7 @@ export class LineString extends Path {
|
||||||
})
|
})
|
||||||
} else if (index === 0 || index === event.vertex.getLastIndex()) {
|
} else if (index === 0 || index === event.vertex.getLastIndex()) {
|
||||||
items.push({
|
items.push({
|
||||||
label: this._umap.help.displayLabel('CONTINUE_LINE'),
|
label: this.map.help.displayLabel('CONTINUE_LINE'),
|
||||||
action: () => event.vertex.continue(),
|
action: () => event.vertex.continue(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1051,8 +1041,8 @@ export class LineString extends Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Polygon extends Path {
|
export class Polygon extends Path {
|
||||||
constructor(umap, datalayer, geojson, id) {
|
constructor(datalayer, geojson, id) {
|
||||||
super(umap, datalayer, geojson, id)
|
super(datalayer, geojson, id)
|
||||||
this.staticOptions = {
|
this.staticOptions = {
|
||||||
mainColor: 'fillColor',
|
mainColor: 'fillColor',
|
||||||
className: 'polygon',
|
className: 'polygon',
|
||||||
|
|
|
@ -21,7 +21,6 @@ import { DataLayerPermissions } from '../permissions.js'
|
||||||
import { Point, LineString, Polygon } from './features.js'
|
import { Point, LineString, Polygon } from './features.js'
|
||||||
import TableEditor from '../tableeditor.js'
|
import TableEditor from '../tableeditor.js'
|
||||||
import { ServerStored } from '../saving.js'
|
import { ServerStored } from '../saving.js'
|
||||||
import * as Schema from '../schema.js'
|
|
||||||
|
|
||||||
export const LAYER_TYPES = [
|
export const LAYER_TYPES = [
|
||||||
DefaultLayer,
|
DefaultLayer,
|
||||||
|
@ -38,10 +37,10 @@ const LAYER_MAP = LAYER_TYPES.reduce((acc, klass) => {
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
export class DataLayer extends ServerStored {
|
export class DataLayer extends ServerStored {
|
||||||
constructor(umap, leafletMap, data) {
|
constructor(map, data) {
|
||||||
super()
|
super()
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.sync = umap.sync_engine.proxy(this)
|
this.sync = map.sync_engine.proxy(this)
|
||||||
this._index = Array()
|
this._index = Array()
|
||||||
this._features = {}
|
this._features = {}
|
||||||
this._geojson = null
|
this._geojson = null
|
||||||
|
@ -49,12 +48,8 @@ export class DataLayer extends ServerStored {
|
||||||
this._loaded = false // Are layer metadata loaded
|
this._loaded = false // Are layer metadata loaded
|
||||||
this._dataloaded = false // Are layer data loaded
|
this._dataloaded = false // Are layer data loaded
|
||||||
|
|
||||||
this._leafletMap = leafletMap
|
this.parentPane = this.map.getPane('overlayPane')
|
||||||
this.parentPane = this._leafletMap.getPane('overlayPane')
|
this.pane = this.map.createPane(`datalayer${stamp(this)}`, this.parentPane)
|
||||||
this.pane = this._leafletMap.createPane(
|
|
||||||
`datalayer${stamp(this)}`,
|
|
||||||
this.parentPane
|
|
||||||
)
|
|
||||||
this.pane.dataset.id = stamp(this)
|
this.pane.dataset.id = stamp(this)
|
||||||
// FIXME: should be on layer
|
// FIXME: should be on layer
|
||||||
this.renderer = L.svg({ pane: this.pane })
|
this.renderer = L.svg({ pane: this.pane })
|
||||||
|
@ -83,7 +78,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
this.backupOptions()
|
this.backupOptions()
|
||||||
this.connectToMap()
|
this.connectToMap()
|
||||||
this.permissions = new DataLayerPermissions(this._umap, this)
|
this.permissions = new DataLayerPermissions(this)
|
||||||
if (!this.umap_id) {
|
if (!this.umap_id) {
|
||||||
if (this.showAtLoad()) this.show()
|
if (this.showAtLoad()) this.show()
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
|
@ -133,7 +128,7 @@ export class DataLayer extends ServerStored {
|
||||||
for (const impact of impacts) {
|
for (const impact of impacts) {
|
||||||
switch (impact) {
|
switch (impact) {
|
||||||
case 'ui':
|
case 'ui':
|
||||||
this._umap.onDataLayersChanged()
|
this.map.onDataLayersChanged()
|
||||||
break
|
break
|
||||||
case 'data':
|
case 'data':
|
||||||
if (fields.includes('options.type')) {
|
if (fields.includes('options.type')) {
|
||||||
|
@ -158,8 +153,8 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
autoLoaded() {
|
autoLoaded() {
|
||||||
if (!this._umap.datalayersFromQueryString) return this.options.displayOnLoad
|
if (!this.map.datalayersFromQueryString) return this.options.displayOnLoad
|
||||||
const datalayerIds = this._umap.datalayersFromQueryString
|
const datalayerIds = this.map.datalayersFromQueryString
|
||||||
let loadMe = datalayerIds.includes(this.umap_id.toString())
|
let loadMe = datalayerIds.includes(this.umap_id.toString())
|
||||||
if (this.options.old_id) {
|
if (this.options.old_id) {
|
||||||
loadMe = loadMe || datalayerIds.includes(this.options.old_id.toString())
|
loadMe = loadMe || datalayerIds.includes(this.options.old_id.toString())
|
||||||
|
@ -197,7 +192,7 @@ export class DataLayer extends ServerStored {
|
||||||
const visible = this.isVisible()
|
const visible = this.isVisible()
|
||||||
if (this.layer) this.layer.clearLayers()
|
if (this.layer) this.layer.clearLayers()
|
||||||
// delete this.layer?
|
// delete this.layer?
|
||||||
if (visible) this._leafletMap.removeLayer(this.layer)
|
if (visible) this.map.removeLayer(this.layer)
|
||||||
const Class = LAYER_MAP[this.options.type] || DefaultLayer
|
const Class = LAYER_MAP[this.options.type] || DefaultLayer
|
||||||
this.layer = new Class(this)
|
this.layer = new Class(this)
|
||||||
// Rendering layer changed, so let's force reset the feature rendering too.
|
// Rendering layer changed, so let's force reset the feature rendering too.
|
||||||
|
@ -218,7 +213,7 @@ export class DataLayer extends ServerStored {
|
||||||
if (!this.umap_id) return
|
if (!this.umap_id) return
|
||||||
if (this._loading) return
|
if (this._loading) return
|
||||||
this._loading = true
|
this._loading = true
|
||||||
const [geojson, response, error] = await this._umap.server.get(this._dataUrl())
|
const [geojson, response, error] = await this.map.server.get(this._dataUrl())
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this._reference_version = response.headers.get('X-Datalayer-Version')
|
this._reference_version = response.headers.get('X-Datalayer-Version')
|
||||||
// FIXME: for now this property is set dynamically from backend
|
// FIXME: for now this property is set dynamically from backend
|
||||||
|
@ -239,7 +234,7 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
dataChanged() {
|
dataChanged() {
|
||||||
if (!this.hasDataLoaded()) return
|
if (!this.hasDataLoaded()) return
|
||||||
this._umap.onDataLayersChanged()
|
this.map.onDataLayersChanged()
|
||||||
this.layer.dataChanged()
|
this.layer.dataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,14 +275,14 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
reindex() {
|
reindex() {
|
||||||
const features = Object.values(this._features)
|
const features = Object.values(this._features)
|
||||||
Utils.sortFeatures(features, this._umap.getProperty('sortKey'), U.lang)
|
Utils.sortFeatures(features, this.map.getOption('sortKey'), L.lang)
|
||||||
this._index = features.map((feature) => stamp(feature))
|
this._index = features.map((feature) => stamp(feature))
|
||||||
}
|
}
|
||||||
|
|
||||||
showAtZoom() {
|
showAtZoom() {
|
||||||
const from = Number.parseInt(this.options.fromZoom, 10)
|
const from = Number.parseInt(this.options.fromZoom, 10)
|
||||||
const to = Number.parseInt(this.options.toZoom, 10)
|
const to = Number.parseInt(this.options.toZoom, 10)
|
||||||
const zoom = this._leafletMap.getZoom()
|
const zoom = this.map.getZoom()
|
||||||
return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to))
|
return !((!Number.isNaN(from) && zoom < from) || (!Number.isNaN(to) && zoom > to))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,14 +294,14 @@ export class DataLayer extends ServerStored {
|
||||||
if (!this.isRemoteLayer()) return
|
if (!this.isRemoteLayer()) return
|
||||||
if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return
|
if (!this.hasDynamicData() && this.hasDataLoaded() && !force) return
|
||||||
if (!this.isVisible()) return
|
if (!this.isVisible()) return
|
||||||
let url = this._umap.renderUrl(this.options.remoteData.url)
|
let url = this.map.localizeUrl(this.options.remoteData.url)
|
||||||
if (this.options.remoteData.proxy) {
|
if (this.options.remoteData.proxy) {
|
||||||
url = this._umap.proxyUrl(url, this.options.remoteData.ttl)
|
url = this.map.proxyUrl(url, this.options.remoteData.ttl)
|
||||||
}
|
}
|
||||||
const response = await this._umap.request.get(url)
|
const response = await this.map.request.get(url)
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
this.clear()
|
this.clear()
|
||||||
this._umap.formatter
|
this.map.formatter
|
||||||
.parse(await response.text(), this.options.remoteData.format)
|
.parse(await response.text(), this.options.remoteData.format)
|
||||||
.then((geojson) => this.fromGeoJSON(geojson))
|
.then((geojson) => this.fromGeoJSON(geojson))
|
||||||
}
|
}
|
||||||
|
@ -346,23 +341,25 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
connectToMap() {
|
connectToMap() {
|
||||||
const id = stamp(this)
|
const id = stamp(this)
|
||||||
if (!this._umap.datalayers[id]) {
|
if (!this.map.datalayers[id]) {
|
||||||
this._umap.datalayers[id] = this
|
this.map.datalayers[id] = this
|
||||||
}
|
}
|
||||||
if (!this._umap.datalayersIndex.includes(this)) {
|
if (!this.map.datalayers_index.includes(this)) {
|
||||||
this._umap.datalayersIndex.push(this)
|
this.map.datalayers_index.push(this)
|
||||||
}
|
}
|
||||||
this._umap.onDataLayersChanged()
|
this.map.onDataLayersChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataUrl() {
|
_dataUrl() {
|
||||||
let url = this._umap.urls.get('datalayer_view', {
|
const template = this.map.options.urls.datalayer_view
|
||||||
|
|
||||||
|
let url = Utils.template(template, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
|
|
||||||
// No browser cache for owners/editors.
|
// No browser cache for owners/editors.
|
||||||
if (this._umap.hasEditMode()) url = `${url}?${Date.now()}`
|
if (this.map.hasEditMode()) url = `${url}?${Date.now()}`
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +386,7 @@ export class DataLayer extends ServerStored {
|
||||||
this._index.push(id)
|
this._index.push(id)
|
||||||
this._features[id] = feature
|
this._features[id] = feature
|
||||||
this.indexProperties(feature)
|
this.indexProperties(feature)
|
||||||
this._umap.featuresIndex[feature.getSlug()] = feature
|
this.map.features_index[feature.getSlug()] = feature
|
||||||
this.showFeature(feature)
|
this.showFeature(feature)
|
||||||
this.dataChanged()
|
this.dataChanged()
|
||||||
}
|
}
|
||||||
|
@ -398,7 +395,7 @@ export class DataLayer extends ServerStored {
|
||||||
const id = stamp(feature)
|
const id = stamp(feature)
|
||||||
if (sync !== false) feature.sync.delete()
|
if (sync !== false) feature.sync.delete()
|
||||||
this.hideFeature(feature)
|
this.hideFeature(feature)
|
||||||
delete this._umap.featuresIndex[feature.getSlug()]
|
delete this.map.features_index[feature.getSlug()]
|
||||||
feature.disconnectFromDataLayer(this)
|
feature.disconnectFromDataLayer(this)
|
||||||
this._index.splice(this._index.indexOf(id), 1)
|
this._index.splice(this._index.indexOf(id), 1)
|
||||||
delete this._features[id]
|
delete this._features[id]
|
||||||
|
@ -449,8 +446,7 @@ export class DataLayer extends ServerStored {
|
||||||
const collection = Array.isArray(geojson)
|
const collection = Array.isArray(geojson)
|
||||||
? geojson
|
? geojson
|
||||||
: geojson.features || geojson.geometries
|
: geojson.features || geojson.geometries
|
||||||
if (!collection) return
|
Utils.sortFeatures(collection, this.map.getOption('sortKey'), L.lang)
|
||||||
Utils.sortFeatures(collection, this._umap.getProperty('sortKey'), U.lang)
|
|
||||||
for (const feature of collection) {
|
for (const feature of collection) {
|
||||||
this.makeFeature(feature, sync)
|
this.makeFeature(feature, sync)
|
||||||
}
|
}
|
||||||
|
@ -464,15 +460,15 @@ export class DataLayer extends ServerStored {
|
||||||
switch (geometry.type) {
|
switch (geometry.type) {
|
||||||
case 'Point':
|
case 'Point':
|
||||||
// FIXME: deal with MultiPoint
|
// FIXME: deal with MultiPoint
|
||||||
feature = new Point(this._umap, this, geojson, id)
|
feature = new Point(this, geojson, id)
|
||||||
break
|
break
|
||||||
case 'MultiLineString':
|
case 'MultiLineString':
|
||||||
case 'LineString':
|
case 'LineString':
|
||||||
feature = new LineString(this._umap, this, geojson, id)
|
feature = new LineString(this, geojson, id)
|
||||||
break
|
break
|
||||||
case 'MultiPolygon':
|
case 'MultiPolygon':
|
||||||
case 'Polygon':
|
case 'Polygon':
|
||||||
feature = new Polygon(this._umap, this, geojson, id)
|
feature = new Polygon(this, geojson, id)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.log(geojson)
|
console.log(geojson)
|
||||||
|
@ -490,7 +486,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async importRaw(raw, format) {
|
async importRaw(raw, format) {
|
||||||
this._umap.formatter
|
this.map.formatter
|
||||||
.parse(raw, format)
|
.parse(raw, format)
|
||||||
.then((geojson) => this.addData(geojson))
|
.then((geojson) => this.addData(geojson))
|
||||||
.then(() => this.zoomTo())
|
.then(() => this.zoomTo())
|
||||||
|
@ -511,35 +507,35 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async importFromUrl(uri, type) {
|
async importFromUrl(uri, type) {
|
||||||
uri = this._umap.renderUrl(uri)
|
uri = this.map.localizeUrl(uri)
|
||||||
const response = await this._umap.request.get(uri)
|
const response = await this.map.request.get(uri)
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
this.importRaw(await response.text(), type)
|
this.importRaw(await response.text(), type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getColor() {
|
getColor() {
|
||||||
return this.options.color || this._umap.getProperty('color')
|
return this.options.color || this.map.getOption('color')
|
||||||
}
|
}
|
||||||
|
|
||||||
getDeleteUrl() {
|
getDeleteUrl() {
|
||||||
return this._umap.urls.get('datalayer_delete', {
|
return Utils.template(this.map.options.urls.datalayer_delete, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersionsUrl() {
|
getVersionsUrl() {
|
||||||
return this._umap.urls.get('datalayer_versions', {
|
return Utils.template(this.map.options.urls.datalayer_versions, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersionUrl(name) {
|
getVersionUrl(name) {
|
||||||
return this._umap.urls.get('datalayer_version', {
|
return Utils.template(this.map.options.urls.datalayer_version, {
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
name: name,
|
name: name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -560,17 +556,17 @@ export class DataLayer extends ServerStored {
|
||||||
options.name = translate('Clone of {name}', { name: this.options.name })
|
options.name = translate('Clone of {name}', { name: this.options.name })
|
||||||
delete options.id
|
delete options.id
|
||||||
const geojson = Utils.CopyJSON(this._geojson)
|
const geojson = Utils.CopyJSON(this._geojson)
|
||||||
const datalayer = this._umap.createDataLayer(options)
|
const datalayer = this.map.createDataLayer(options)
|
||||||
datalayer.fromGeoJSON(geojson)
|
datalayer.fromGeoJSON(geojson)
|
||||||
return datalayer
|
return datalayer
|
||||||
}
|
}
|
||||||
|
|
||||||
erase() {
|
erase() {
|
||||||
this.hide()
|
this.hide()
|
||||||
this._umap.datalayersIndex.splice(this.getRank(), 1)
|
this.map.datalayers_index.splice(this.getRank(), 1)
|
||||||
this.parentPane.removeChild(this.pane)
|
this.parentPane.removeChild(this.pane)
|
||||||
this._umap.onDataLayersChanged()
|
this.map.onDataLayersChanged()
|
||||||
this.layer.onDelete(this._leafletMap)
|
this.layer.onDelete(this.map)
|
||||||
this.propagateDelete()
|
this.propagateDelete()
|
||||||
this._leaflet_events_bk = this._leaflet_events
|
this._leaflet_events_bk = this._leaflet_events
|
||||||
this.clear()
|
this.clear()
|
||||||
|
@ -601,7 +597,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
edit() {
|
edit() {
|
||||||
if (!this._umap.editEnabled || !this.isLoaded()) {
|
if (!this.map.editEnabled || !this.isLoaded()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const container = DomUtil.create('div', 'umap-layer-properties-container')
|
const container = DomUtil.create('div', 'umap-layer-properties-container')
|
||||||
|
@ -635,7 +631,7 @@ export class DataLayer extends ServerStored {
|
||||||
DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
|
DomUtil.createTitle(container, translate('Layer properties'), 'icon-layers')
|
||||||
let builder = new U.FormBuilder(this, metadataFields, {
|
let builder = new U.FormBuilder(this, metadataFields, {
|
||||||
callback(e) {
|
callback(e) {
|
||||||
this._umap.onDataLayersChanged()
|
this.map.onDataLayersChanged()
|
||||||
if (e.helper.field === 'options.type') {
|
if (e.helper.field === 'options.type') {
|
||||||
this.edit()
|
this.edit()
|
||||||
}
|
}
|
||||||
|
@ -746,7 +742,7 @@ export class DataLayer extends ServerStored {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
if (this._umap.properties.urls.ajax_proxy) {
|
if (this.map.options.urls.ajax_proxy) {
|
||||||
remoteDataFields.push([
|
remoteDataFields.push([
|
||||||
'options.remoteData.proxy',
|
'options.remoteData.proxy',
|
||||||
{
|
{
|
||||||
|
@ -772,8 +768,7 @@ export class DataLayer extends ServerStored {
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this._umap.properties.urls.datalayer_versions)
|
if (this.map.options.urls.datalayer_versions) this.buildVersionsFieldset(container)
|
||||||
this.buildVersionsFieldset(container)
|
|
||||||
|
|
||||||
const advancedActions = DomUtil.createFieldset(
|
const advancedActions = DomUtil.createFieldset(
|
||||||
container,
|
container,
|
||||||
|
@ -786,7 +781,7 @@ export class DataLayer extends ServerStored {
|
||||||
</button>`)
|
</button>`)
|
||||||
deleteButton.addEventListener('click', () => {
|
deleteButton.addEventListener('click', () => {
|
||||||
this._delete()
|
this._delete()
|
||||||
this._umap.editPanel.close()
|
this.map.editPanel.close()
|
||||||
})
|
})
|
||||||
advancedButtons.appendChild(deleteButton)
|
advancedButtons.appendChild(deleteButton)
|
||||||
|
|
||||||
|
@ -825,9 +820,9 @@ export class DataLayer extends ServerStored {
|
||||||
// Fixme: remove me when this is merged and released
|
// Fixme: remove me when this is merged and released
|
||||||
// https://github.com/Leaflet/Leaflet/pull/9052
|
// https://github.com/Leaflet/Leaflet/pull/9052
|
||||||
DomEvent.disableClickPropagation(backButton)
|
DomEvent.disableClickPropagation(backButton)
|
||||||
DomEvent.on(backButton, 'click', this._umap.editDatalayers, this._umap)
|
DomEvent.on(backButton, 'click', this.map.editDatalayers, this.map)
|
||||||
|
|
||||||
this._umap.editPanel.open({
|
this.map.editPanel.open({
|
||||||
content: container,
|
content: container,
|
||||||
actions: [backButton],
|
actions: [backButton],
|
||||||
})
|
})
|
||||||
|
@ -848,13 +843,13 @@ export class DataLayer extends ServerStored {
|
||||||
if (this.layer?.defaults?.[option]) {
|
if (this.layer?.defaults?.[option]) {
|
||||||
return this.layer.defaults[option]
|
return this.layer.defaults[option]
|
||||||
}
|
}
|
||||||
return this._umap.getProperty(option, feature)
|
return this.map.getOption(option, feature)
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildVersionsFieldset(container) {
|
async buildVersionsFieldset(container) {
|
||||||
const appendVersion = (data) => {
|
const appendVersion = (data) => {
|
||||||
const date = new Date(Number.parseInt(data.at, 10))
|
const date = new Date(Number.parseInt(data.at, 10))
|
||||||
const content = `${date.toLocaleString(U.lang)} (${Number.parseInt(data.size) / 1000}Kb)`
|
const content = `${date.toLocaleString(L.lang)} (${Number.parseInt(data.size) / 1000}Kb)`
|
||||||
const el = DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
|
const el = DomUtil.create('div', 'umap-datalayer-version', versionsContainer)
|
||||||
const button = DomUtil.createButton(
|
const button = DomUtil.createButton(
|
||||||
'',
|
'',
|
||||||
|
@ -869,7 +864,7 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
|
const versionsContainer = DomUtil.createFieldset(container, translate('Versions'), {
|
||||||
async callback() {
|
async callback() {
|
||||||
const [{ versions }, response, error] = await this._umap.server.get(
|
const [{ versions }, response, error] = await this.map.server.get(
|
||||||
this.getVersionsUrl()
|
this.getVersionsUrl()
|
||||||
)
|
)
|
||||||
if (!error) versions.forEach(appendVersion)
|
if (!error) versions.forEach(appendVersion)
|
||||||
|
@ -879,11 +874,11 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async restore(version) {
|
async restore(version) {
|
||||||
if (!this._umap.editEnabled) return
|
if (!this.map.editEnabled) return
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.confirm(translate('Are you sure you want to restore this version?'))
|
.confirm(translate('Are you sure you want to restore this version?'))
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
const [geojson, response, error] = await this._umap.server.get(
|
const [geojson, response, error] = await this.map.server.get(
|
||||||
this.getVersionUrl(version)
|
this.getVersionUrl(version)
|
||||||
)
|
)
|
||||||
if (!error) {
|
if (!error) {
|
||||||
|
@ -904,13 +899,13 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async show() {
|
async show() {
|
||||||
this._leafletMap.addLayer(this.layer)
|
this.map.addLayer(this.layer)
|
||||||
if (!this.isLoaded()) await this.fetchData()
|
if (!this.isLoaded()) await this.fetchData()
|
||||||
this.propagateShow()
|
this.propagateShow()
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this._leafletMap.removeLayer(this.layer)
|
this.map.removeLayer(this.layer)
|
||||||
this.propagateHide()
|
this.propagateHide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -927,7 +922,7 @@ export class DataLayer extends ServerStored {
|
||||||
const bounds = this.layer.getBounds()
|
const bounds = this.layer.getBounds()
|
||||||
if (bounds.isValid()) {
|
if (bounds.isValid()) {
|
||||||
const options = { maxZoom: this.getOption('zoomTo') }
|
const options = { maxZoom: this.getOption('zoomTo') }
|
||||||
this._leafletMap.fitBounds(bounds, options)
|
this.map.fitBounds(bounds, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -958,7 +953,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible() {
|
isVisible() {
|
||||||
return Boolean(this.layer && this._leafletMap.hasLayer(this.layer))
|
return Boolean(this.layer && this.map.hasLayer(this.layer))
|
||||||
}
|
}
|
||||||
|
|
||||||
getFeatureByIndex(index) {
|
getFeatureByIndex(index) {
|
||||||
|
@ -995,7 +990,7 @@ export class DataLayer extends ServerStored {
|
||||||
getPreviousBrowsable() {
|
getPreviousBrowsable() {
|
||||||
let id = this.getRank()
|
let id = this.getRank()
|
||||||
let next
|
let next
|
||||||
const index = this._umap.datalayersIndex
|
const index = this.map.datalayers_index
|
||||||
while (((id = index[++id] ? id : 0), (next = index[id]))) {
|
while (((id = index[++id] ? id : 0), (next = index[id]))) {
|
||||||
if (next === this || next.canBrowse()) break
|
if (next === this || next.canBrowse()) break
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1000,7 @@ export class DataLayer extends ServerStored {
|
||||||
getNextBrowsable() {
|
getNextBrowsable() {
|
||||||
let id = this.getRank()
|
let id = this.getRank()
|
||||||
let prev
|
let prev
|
||||||
const index = this._umap.datalayersIndex
|
const index = this.map.datalayers_index
|
||||||
while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
|
while (((id = index[--id] ? id : index.length - 1), (prev = index[id]))) {
|
||||||
if (prev === this || prev.canBrowse()) break
|
if (prev === this || prev.canBrowse()) break
|
||||||
}
|
}
|
||||||
|
@ -1021,7 +1016,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
getRank() {
|
getRank() {
|
||||||
return this._umap.datalayersIndex.indexOf(this)
|
return this.map.datalayers_index.indexOf(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
isReadOnly() {
|
isReadOnly() {
|
||||||
|
@ -1048,8 +1043,8 @@ export class DataLayer extends ServerStored {
|
||||||
// Filename support is shaky, don't do it for now.
|
// Filename support is shaky, don't do it for now.
|
||||||
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
const blob = new Blob([JSON.stringify(geojson)], { type: 'application/json' })
|
||||||
formData.append('geojson', blob)
|
formData.append('geojson', blob)
|
||||||
const saveUrl = this._umap.urls.get('datalayer_save', {
|
const saveUrl = this.map.urls.get('datalayer_save', {
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
pk: this.umap_id,
|
pk: this.umap_id,
|
||||||
})
|
})
|
||||||
const headers = this._reference_version
|
const headers = this._reference_version
|
||||||
|
@ -1061,7 +1056,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _trySave(url, headers, formData) {
|
async _trySave(url, headers, formData) {
|
||||||
const [data, response, error] = await this._umap.server.post(url, headers, formData)
|
const [data, response, error] = await this.map.server.post(url, headers, formData)
|
||||||
if (error) {
|
if (error) {
|
||||||
if (response && response.status === 412) {
|
if (response && response.status === 412) {
|
||||||
AlertConflict.error(
|
AlertConflict.error(
|
||||||
|
@ -1077,7 +1072,7 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
// Call the main save, in case something else needs to be saved
|
// Call the main save, in case something else needs to be saved
|
||||||
// as the conflict stopped the saving flow
|
// as the conflict stopped the saving flow
|
||||||
await this._umap.saveAll()
|
await this.map.saveAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1106,19 +1101,23 @@ export class DataLayer extends ServerStored {
|
||||||
|
|
||||||
async saveDelete() {
|
async saveDelete() {
|
||||||
if (this.umap_id) {
|
if (this.umap_id) {
|
||||||
await this._umap.server.post(this.getDeleteUrl())
|
await this.map.server.post(this.getDeleteUrl())
|
||||||
}
|
}
|
||||||
delete this._umap.datalayers[stamp(this)]
|
delete this.map.datalayers[stamp(this)]
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
return this.options.name || translate('Untitled layer')
|
return this.options.name || translate('Untitled layer')
|
||||||
}
|
}
|
||||||
|
|
||||||
tableEdit() {
|
tableEdit() {
|
||||||
if (!this.isVisible()) return
|
if (!this.isVisible()) return
|
||||||
const editor = new TableEditor(this._umap, this, this._leafletMap)
|
const editor = new TableEditor(this)
|
||||||
editor.open()
|
editor.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1126,9 +1125,9 @@ export class DataLayer extends ServerStored {
|
||||||
// This keys will be used to filter feature from the browser text input.
|
// This keys will be used to filter feature from the browser text input.
|
||||||
// By default, it will we use the "name" property, which is also the one used as label in the features list.
|
// By default, it will we use the "name" property, which is also the one used as label in the features list.
|
||||||
// When map owner has configured another label or sort key, we try to be smart and search in the same keys.
|
// When map owner has configured another label or sort key, we try to be smart and search in the same keys.
|
||||||
if (this._umap.properties.filterKey) return this._umap.properties.filterKey
|
if (this.map.options.filterKey) return this.map.options.filterKey
|
||||||
if (this.getOption('labelKey')) return this.getOption('labelKey')
|
if (this.getOption('labelKey')) return this.getOption('labelKey')
|
||||||
if (this._umap.properties.sortKey) return this._umap.properties.sortKey
|
if (this.map.options.sortKey) return this.map.options.sortKey
|
||||||
return 'displayName'
|
return 'displayName'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1179,7 +1178,7 @@ export class DataLayer extends ServerStored {
|
||||||
'click',
|
'click',
|
||||||
function () {
|
function () {
|
||||||
if (!this.isVisible()) return
|
if (!this.isVisible()) return
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.confirm(translate('Are you sure you want to delete this layer?'))
|
.confirm(translate('Are you sure you want to delete this layer?'))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this._delete()
|
this._delete()
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
|
|
||||||
export default class Facets {
|
export default class Facets {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.selected = {}
|
this.selected = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export default class Facets {
|
||||||
this.selected[name] = selected
|
this.selected[name] = selected
|
||||||
}
|
}
|
||||||
|
|
||||||
this._umap.eachBrowsableDataLayer((datalayer) => {
|
this.map.eachBrowsableDataLayer((datalayer) => {
|
||||||
datalayer.eachFeature((feature) => {
|
datalayer.eachFeature((feature) => {
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
let value = feature.properties[name]
|
let value = feature.properties[name]
|
||||||
|
@ -108,8 +108,8 @@ export default class Facets {
|
||||||
const defaultType = 'checkbox'
|
const defaultType = 'checkbox'
|
||||||
const allowedTypes = [defaultType, 'radio', 'number', 'date', 'datetime']
|
const allowedTypes = [defaultType, 'radio', 'number', 'date', 'datetime']
|
||||||
const defined = new Map()
|
const defined = new Map()
|
||||||
if (!this._umap.properties.facetKey) return defined
|
if (!this.map.options.facetKey) return defined
|
||||||
return (this._umap.properties.facetKey || '').split(',').reduce((acc, curr) => {
|
return (this.map.options.facetKey || '').split(',').reduce((acc, curr) => {
|
||||||
let [name, label, type] = curr.split('|')
|
let [name, label, type] = curr.split('|')
|
||||||
type = allowedTypes.includes(type) ? type : defaultType
|
type = allowedTypes.includes(type) ? type : defaultType
|
||||||
acc.set(name, { label: label || name, type: type })
|
acc.set(name, { label: label || name, type: type })
|
||||||
|
@ -146,15 +146,15 @@ export default class Facets {
|
||||||
const defined = this.getDefined()
|
const defined = this.getDefined()
|
||||||
if (!defined.has(property)) {
|
if (!defined.has(property)) {
|
||||||
defined.set(property, { label, type })
|
defined.set(property, { label, type })
|
||||||
this._umap.properties.facetKey = this.dumps(defined)
|
this.map.options.facetKey = this.dumps(defined)
|
||||||
this._umap.isDirty = true
|
this.map.isDirty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(property) {
|
remove(property) {
|
||||||
const defined = this.getDefined()
|
const defined = this.getDefined()
|
||||||
defined.delete(property)
|
defined.delete(property)
|
||||||
this._umap.properties.facetKey = this.dumps(defined)
|
this.map.options.facetKey = this.dumps(defined)
|
||||||
this._umap.isDirty = true
|
this.map.isDirty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,24 @@ import { translate } from './i18n.js'
|
||||||
|
|
||||||
export const EXPORT_FORMATS = {
|
export const EXPORT_FORMATS = {
|
||||||
geojson: {
|
geojson: {
|
||||||
formatter: async (umap) => JSON.stringify(umap.toGeoJSON(), null, 2),
|
formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
|
||||||
ext: '.geojson',
|
ext: '.geojson',
|
||||||
filetype: 'application/json',
|
filetype: 'application/json',
|
||||||
},
|
},
|
||||||
gpx: {
|
gpx: {
|
||||||
formatter: async (umap) => await umap.formatter.toGPX(umap.toGeoJSON()),
|
formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
|
||||||
ext: '.gpx',
|
ext: '.gpx',
|
||||||
filetype: 'application/gpx+xml',
|
filetype: 'application/gpx+xml',
|
||||||
},
|
},
|
||||||
kml: {
|
kml: {
|
||||||
formatter: async (umap) => await umap.formatter.toKML(umap.toGeoJSON()),
|
formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
|
||||||
ext: '.kml',
|
ext: '.kml',
|
||||||
filetype: 'application/vnd.google-earth.kml+xml',
|
filetype: 'application/vnd.google-earth.kml+xml',
|
||||||
},
|
},
|
||||||
csv: {
|
csv: {
|
||||||
formatter: async (umap) => {
|
formatter: async (map) => {
|
||||||
const table = []
|
const table = []
|
||||||
umap.eachFeature((feature) => {
|
map.eachFeature((feature) => {
|
||||||
const row = feature.toGeoJSON().properties
|
const row = feature.toGeoJSON().properties
|
||||||
const center = feature.center
|
const center = feature.center
|
||||||
delete row._umap_options
|
delete row._umap_options
|
||||||
|
|
|
@ -1,13 +1,37 @@
|
||||||
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
import {
|
||||||
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
uMapAlert as Alert,
|
||||||
|
uMapAlertCreation as AlertCreation,
|
||||||
|
} from '../components/alerts/alert.js'
|
||||||
|
import {
|
||||||
|
AjaxAutocomplete,
|
||||||
|
AjaxAutocompleteMultiple,
|
||||||
|
AutocompleteDatalist,
|
||||||
|
} from './autocomplete.js'
|
||||||
|
import Browser from './browser.js'
|
||||||
|
import Caption from './caption.js'
|
||||||
|
import ContextMenu from './ui/contextmenu.js'
|
||||||
|
import Facets from './facets.js'
|
||||||
|
import { Formatter } from './formatter.js'
|
||||||
import Help from './help.js'
|
import Help from './help.js'
|
||||||
import { ServerRequest } from './request.js'
|
import Importer from './importer.js'
|
||||||
|
import Orderable from './orderable.js'
|
||||||
|
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
||||||
|
import Rules from './rules.js'
|
||||||
import { SCHEMA } from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
|
import Share from './share.js'
|
||||||
|
import Slideshow from './slideshow.js'
|
||||||
|
import { SyncEngine } from './sync/engine.js'
|
||||||
|
import Dialog from './ui/dialog.js'
|
||||||
|
import { EditPanel, FullPanel, Panel } from './ui/panel.js'
|
||||||
|
import Tooltip from './ui/tooltip.js'
|
||||||
|
import URLs from './urls.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import * as Icon from './rendering/icon.js'
|
import * as Icon from './rendering/icon.js'
|
||||||
import { LAYER_TYPES } from './data/layer.js'
|
import { DataLayer, LAYER_TYPES } from './data/layer.js'
|
||||||
|
import { DataLayerPermissions, MapPermissions } from './permissions.js'
|
||||||
import { Point, LineString, Polygon } from './data/features.js'
|
import { Point, LineString, Polygon } from './data/features.js'
|
||||||
import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js'
|
import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js'
|
||||||
|
import * as SAVEMANAGER from './saving.js'
|
||||||
|
|
||||||
// Import modules and export them to the global scope.
|
// Import modules and export them to the global scope.
|
||||||
// For the not yet module-compatible JS out there.
|
// For the not yet module-compatible JS out there.
|
||||||
|
@ -15,18 +39,45 @@ import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.j
|
||||||
// By alphabetic order
|
// By alphabetic order
|
||||||
window.U = {
|
window.U = {
|
||||||
Alert,
|
Alert,
|
||||||
|
AlertCreation,
|
||||||
AjaxAutocomplete,
|
AjaxAutocomplete,
|
||||||
AjaxAutocompleteMultiple,
|
AjaxAutocompleteMultiple,
|
||||||
|
AutocompleteDatalist,
|
||||||
|
Browser,
|
||||||
|
Caption,
|
||||||
|
ContextMenu,
|
||||||
|
DataLayer,
|
||||||
|
DataLayerPermissions,
|
||||||
|
Dialog,
|
||||||
|
EditPanel,
|
||||||
|
Facets,
|
||||||
|
Formatter,
|
||||||
|
FullPanel,
|
||||||
Help,
|
Help,
|
||||||
|
HTTPError,
|
||||||
Icon,
|
Icon,
|
||||||
|
Importer,
|
||||||
LAYER_TYPES,
|
LAYER_TYPES,
|
||||||
LeafletMarker,
|
LeafletMarker,
|
||||||
LeafletPolygon,
|
LeafletPolygon,
|
||||||
LeafletPolyline,
|
LeafletPolyline,
|
||||||
LineString,
|
LineString,
|
||||||
|
MapPermissions,
|
||||||
|
NOKError,
|
||||||
|
Orderable,
|
||||||
|
Panel,
|
||||||
Point,
|
Point,
|
||||||
Polygon,
|
Polygon,
|
||||||
|
Request,
|
||||||
|
RequestError,
|
||||||
|
Rules,
|
||||||
|
SAVEMANAGER,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
ServerRequest,
|
ServerRequest,
|
||||||
|
Share,
|
||||||
|
Slideshow,
|
||||||
|
SyncEngine,
|
||||||
|
Tooltip,
|
||||||
|
URLs,
|
||||||
Utils,
|
Utils,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
|
||||||
import Dialog from './ui/dialog.js'
|
|
||||||
|
|
||||||
const SHORTCUTS = {
|
const SHORTCUTS = {
|
||||||
DRAW_MARKER: {
|
DRAW_MARKER: {
|
||||||
|
@ -165,9 +163,9 @@ const ENTRIES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Help {
|
export default class Help {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this.umap = umap
|
this.map = map
|
||||||
this.dialog = new Dialog({ className: 'dark', accept: false, cancel: false })
|
this.dialog = new U.Dialog()
|
||||||
this.isMacOS = /mac/i.test(
|
this.isMacOS = /mac/i.test(
|
||||||
// eslint-disable-next-line compat/compat -- Fallback available.
|
// eslint-disable-next-line compat/compat -- Fallback available.
|
||||||
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform
|
||||||
|
@ -201,7 +199,7 @@ export default class Help {
|
||||||
innerHTML: ENTRIES[name],
|
innerHTML: ENTRIES[name],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.dialog.open({ template: container })
|
this.dialog.open({ template: container, className: 'dark', cancel: false, accept: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special dynamic case. Do we still think this dialog is useful?
|
// Special dynamic case. Do we still think this dialog is useful?
|
||||||
|
@ -213,7 +211,7 @@ export default class Help {
|
||||||
className: 'umap-help-entry',
|
className: 'umap-help-entry',
|
||||||
parent: container,
|
parent: container,
|
||||||
}).appendChild(this._buildEditEntry())
|
}).appendChild(this._buildEditEntry())
|
||||||
this.dialog.open({ template: container })
|
this.map.dialog.open({ content: container, className: 'dark' })
|
||||||
}
|
}
|
||||||
|
|
||||||
button(container, entries) {
|
button(container, entries) {
|
||||||
|
@ -249,11 +247,9 @@ export default class Help {
|
||||||
DomEvent.on(actionContainer, 'click', action.addHooks, action)
|
DomEvent.on(actionContainer, 'click', action.addHooks, action)
|
||||||
DomEvent.on(actionContainer, 'click', this.dialog.close, this.dialog)
|
DomEvent.on(actionContainer, 'click', this.dialog.close, this.dialog)
|
||||||
}
|
}
|
||||||
for (const action of Object.values(Help.MENU_ACTIONS)) {
|
for (const id in this.map.helpMenuActions) {
|
||||||
addAction(action)
|
addAction(this.map.helpMenuActions[id])
|
||||||
}
|
}
|
||||||
return container
|
return container
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Help.MENU_ACTIONS = {}
|
|
||||||
|
|
|
@ -48,8 +48,8 @@ const TEMPLATE = `
|
||||||
`
|
`
|
||||||
|
|
||||||
export default class Importer {
|
export default class Importer {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
||||||
this.IMPORTERS = []
|
this.IMPORTERS = []
|
||||||
this.loadImporters()
|
this.loadImporters()
|
||||||
|
@ -57,9 +57,9 @@ export default class Importer {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadImporters() {
|
loadImporters() {
|
||||||
for (const [name, config] of Object.entries(this._umap.properties.importers || {})) {
|
for (const [name, config] of Object.entries(this.map.options.importers || {})) {
|
||||||
const register = (mod) => {
|
const register = (mod) => {
|
||||||
this.IMPORTERS.push(new mod.Importer(this._umap, config))
|
this.IMPORTERS.push(new mod.Importer(this.map, config))
|
||||||
}
|
}
|
||||||
// We need to have explicit static paths for Django's collectstatic with hashes.
|
// We need to have explicit static paths for Django's collectstatic with hashes.
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
@ -139,8 +139,8 @@ export default class Importer {
|
||||||
|
|
||||||
get layer() {
|
get layer() {
|
||||||
return (
|
return (
|
||||||
this._umap.datalayers[this.layerId] ||
|
this.map.datalayers[this.layerId] ||
|
||||||
this._umap.createDataLayer({ name: this.layerName })
|
this.map.createDataLayer({ name: this.layerName })
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ export default class Importer {
|
||||||
textContent: type,
|
textContent: type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this._umap.help.parse(this.container)
|
this.map.help.parse(this.container)
|
||||||
DomEvent.on(this.qs('[name=submit]'), 'click', this.submit, this)
|
DomEvent.on(this.qs('[name=submit]'), 'click', this.submit, this)
|
||||||
DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this)
|
DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this)
|
||||||
for (const element of this.container.querySelectorAll('[onchange]')) {
|
for (const element of this.container.querySelectorAll('[onchange]')) {
|
||||||
|
@ -206,7 +206,7 @@ export default class Importer {
|
||||||
this.layerName = null
|
this.layerName = null
|
||||||
const layerSelect = this.qs('[name="layer-id"]')
|
const layerSelect = this.qs('[name="layer-id"]')
|
||||||
layerSelect.innerHTML = ''
|
layerSelect.innerHTML = ''
|
||||||
this._umap.eachDataLayerReverse((datalayer) => {
|
this.map.eachDataLayerReverse((datalayer) => {
|
||||||
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
||||||
DomUtil.element({
|
DomUtil.element({
|
||||||
tagName: 'option',
|
tagName: 'option',
|
||||||
|
@ -227,7 +227,7 @@ export default class Importer {
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
if (!this.container) this.build()
|
if (!this.container) this.build()
|
||||||
const onLoad = this._umap.editPanel.open({ content: this.container })
|
const onLoad = this.map.editPanel.open({ content: this.container })
|
||||||
onLoad.then(() => this.onLoad())
|
onLoad.then(() => this.onLoad())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,15 +251,16 @@ export default class Importer {
|
||||||
}
|
}
|
||||||
|
|
||||||
full() {
|
full() {
|
||||||
|
this.map.once('postsync', this.map._setDefaultCenter)
|
||||||
try {
|
try {
|
||||||
if (this.files.length) {
|
if (this.files.length) {
|
||||||
for (const file of this.files) {
|
for (const file of this.files) {
|
||||||
this._umap.processFileToImport(file, null, 'umap')
|
this.map.processFileToImport(file, null, 'umap')
|
||||||
}
|
}
|
||||||
} else if (this.raw) {
|
} else if (this.raw) {
|
||||||
this._umap.importRaw(this.raw)
|
this.map.importRaw(this.raw)
|
||||||
} else if (this.url) {
|
} else if (this.url) {
|
||||||
this._umap.importFromUrl(this.url, this.format)
|
this.map.importFromUrl(this.url, this.format)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Alert.error(translate('Invalid umap data'))
|
Alert.error(translate('Invalid umap data'))
|
||||||
|
@ -281,7 +282,7 @@ export default class Importer {
|
||||||
url: this.url,
|
url: this.url,
|
||||||
format: this.format,
|
format: this.format,
|
||||||
}
|
}
|
||||||
if (this._umap.properties.urls.ajax_proxy) {
|
if (this.map.options.urls.ajax_proxy) {
|
||||||
layer.options.remoteData.proxy = true
|
layer.options.remoteData.proxy = true
|
||||||
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
||||||
}
|
}
|
||||||
|
@ -299,7 +300,7 @@ export default class Importer {
|
||||||
if (this.clear) layer.empty()
|
if (this.clear) layer.empty()
|
||||||
if (this.files.length) {
|
if (this.files.length) {
|
||||||
for (const file of this.files) {
|
for (const file of this.files) {
|
||||||
this._umap.processFileToImport(file, layer, this.format)
|
this.map.processFileToImport(file, layer, this.format)
|
||||||
}
|
}
|
||||||
} else if (this.raw) {
|
} else if (this.raw) {
|
||||||
layer.importRaw(this.raw, this.format)
|
layer.importRaw(this.raw, this.format)
|
||||||
|
|
|
@ -7,15 +7,15 @@ import * as Utils from './utils.js'
|
||||||
// Dedicated object so we can deal with a separate dirty status, and thus
|
// Dedicated object so we can deal with a separate dirty status, and thus
|
||||||
// call the endpoint only when needed, saving one call at each save.
|
// call the endpoint only when needed, saving one call at each save.
|
||||||
export class MapPermissions extends ServerStored {
|
export class MapPermissions extends ServerStored {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
super()
|
super()
|
||||||
this.setProperties(umap.properties.permissions)
|
this.setOptions(map.options.permissions)
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this._isDirty = false
|
this._isDirty = false
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(properties) {
|
setOptions(options) {
|
||||||
this.properties = Object.assign(
|
this.options = Object.assign(
|
||||||
{
|
{
|
||||||
owner: null,
|
owner: null,
|
||||||
team: null,
|
team: null,
|
||||||
|
@ -23,42 +23,42 @@ export class MapPermissions extends ServerStored {
|
||||||
share_status: null,
|
share_status: null,
|
||||||
edit_status: null,
|
edit_status: null,
|
||||||
},
|
},
|
||||||
properties
|
options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
isOwner() {
|
isOwner() {
|
||||||
return Boolean(this._umap.properties.user?.is_owner)
|
return Boolean(this.map.options.user?.is_owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
isAnonymousMap() {
|
isAnonymousMap() {
|
||||||
return !this._umap.properties.permissions.owner
|
return !this.map.options.permissions.owner
|
||||||
}
|
}
|
||||||
|
|
||||||
_editAnonymous(container) {
|
_editAnonymous(container) {
|
||||||
const fields = []
|
const fields = []
|
||||||
if (this.isOwner()) {
|
if (this.isOwner()) {
|
||||||
fields.push([
|
fields.push([
|
||||||
'properties.edit_status',
|
'options.edit_status',
|
||||||
{
|
{
|
||||||
handler: 'IntSelect',
|
handler: 'IntSelect',
|
||||||
label: translate('Who can edit'),
|
label: translate('Who can edit'),
|
||||||
selectOptions: this._umap.properties.edit_statuses,
|
selectOptions: this.map.options.edit_statuses,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
const builder = new U.FormBuilder(this, fields)
|
const builder = new U.FormBuilder(this, fields)
|
||||||
const form = builder.build()
|
const form = builder.build()
|
||||||
container.appendChild(form)
|
container.appendChild(form)
|
||||||
|
|
||||||
if (this.properties.anonymous_edit_url) {
|
if (this.options.anonymous_edit_url) {
|
||||||
DomUtil.createCopiableInput(
|
DomUtil.createCopiableInput(
|
||||||
container,
|
container,
|
||||||
translate('Secret edit link:'),
|
translate('Secret edit link:'),
|
||||||
this.properties.anonymous_edit_url
|
this.options.anonymous_edit_url
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._umap.properties.user?.id) {
|
if (this.map.options.user?.id) {
|
||||||
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
// We have a user, and this user has come through here, so they can edit the map, so let's allow to own the map.
|
||||||
// Note: real check is made on the back office anyway.
|
// Note: real check is made on the back office anyway.
|
||||||
const advancedActions = DomUtil.createFieldset(
|
const advancedActions = DomUtil.createFieldset(
|
||||||
|
@ -86,38 +86,38 @@ export class MapPermissions extends ServerStored {
|
||||||
container.appendChild(fieldset)
|
container.appendChild(fieldset)
|
||||||
if (this.isOwner()) {
|
if (this.isOwner()) {
|
||||||
topFields.push([
|
topFields.push([
|
||||||
'properties.edit_status',
|
'options.edit_status',
|
||||||
{
|
{
|
||||||
handler: 'IntSelect',
|
handler: 'IntSelect',
|
||||||
label: translate('Who can edit'),
|
label: translate('Who can edit'),
|
||||||
selectOptions: this._umap.properties.edit_statuses,
|
selectOptions: this.map.options.edit_statuses,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
topFields.push([
|
topFields.push([
|
||||||
'properties.share_status',
|
'options.share_status',
|
||||||
{
|
{
|
||||||
handler: 'IntSelect',
|
handler: 'IntSelect',
|
||||||
label: translate('Who can view'),
|
label: translate('Who can view'),
|
||||||
selectOptions: this._umap.properties.share_statuses,
|
selectOptions: this.map.options.share_statuses,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
collaboratorsFields.push([
|
collaboratorsFields.push([
|
||||||
'properties.owner',
|
'options.owner',
|
||||||
{ handler: 'ManageOwner', label: translate("Map's owner") },
|
{ handler: 'ManageOwner', label: translate("Map's owner") },
|
||||||
])
|
])
|
||||||
if (this._umap.properties.user?.teams?.length) {
|
if (this.map.options.user?.teams?.length) {
|
||||||
collaboratorsFields.push([
|
collaboratorsFields.push([
|
||||||
'properties.team',
|
'options.team',
|
||||||
{
|
{
|
||||||
handler: 'ManageTeam',
|
handler: 'ManageTeam',
|
||||||
label: translate('Attach map to a team'),
|
label: translate('Attach map to a team'),
|
||||||
teams: this._umap.properties.user.teams,
|
teams: this.map.options.user.teams,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collaboratorsFields.push([
|
collaboratorsFields.push([
|
||||||
'properties.editors',
|
'options.editors',
|
||||||
{ handler: 'ManageEditors', label: translate("Map's editors") },
|
{ handler: 'ManageEditors', label: translate("Map's editors") },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -136,20 +136,20 @@ export class MapPermissions extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
_editDatalayers(container) {
|
_editDatalayers(container) {
|
||||||
if (this._umap.hasLayers()) {
|
if (this.map.hasLayers()) {
|
||||||
const fieldset = Utils.loadTemplate(
|
const fieldset = Utils.loadTemplate(
|
||||||
`<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
|
`<fieldset class="separator"><legend>${translate('Datalayers')}</legend></fieldset>`
|
||||||
)
|
)
|
||||||
container.appendChild(fieldset)
|
container.appendChild(fieldset)
|
||||||
this._umap.eachDataLayer((datalayer) => {
|
this.map.eachDataLayer((datalayer) => {
|
||||||
datalayer.permissions.edit(fieldset)
|
datalayer.permissions.edit(fieldset)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edit() {
|
edit() {
|
||||||
if (this._umap.properties.editMode !== 'advanced') return
|
if (this.map.options.editMode !== 'advanced') return
|
||||||
if (!this._umap.properties.umap_id) {
|
if (!this.map.options.umap_id) {
|
||||||
Alert.info(translate('Please save the map first'))
|
Alert.info(translate('Please save the map first'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -158,79 +158,78 @@ export class MapPermissions extends ServerStored {
|
||||||
if (this.isAnonymousMap()) this._editAnonymous(container)
|
if (this.isAnonymousMap()) this._editAnonymous(container)
|
||||||
else this._editWithOwner(container)
|
else this._editWithOwner(container)
|
||||||
this._editDatalayers(container)
|
this._editDatalayers(container)
|
||||||
this._umap.editPanel.open({ content: container, className: 'dark' })
|
this.map.editPanel.open({ content: container, className: 'dark' })
|
||||||
}
|
}
|
||||||
|
|
||||||
async attach() {
|
async attach() {
|
||||||
const [data, response, error] = await this._umap.server.post(this.getAttachUrl())
|
const [data, response, error] = await this.map.server.post(this.getAttachUrl())
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.properties.owner = this._umap.properties.user
|
this.options.owner = this.map.options.user
|
||||||
Alert.success(translate('Map has been attached to your account'))
|
Alert.success(translate('Map has been attached to your account'))
|
||||||
this._umap.editPanel.close()
|
this.map.editPanel.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
if (!this.isDirty) return
|
if (!this.isDirty) return
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
if (!this.isAnonymousMap() && this.properties.editors) {
|
if (!this.isAnonymousMap() && this.options.editors) {
|
||||||
const editors = this.properties.editors.map((u) => u.id)
|
const editors = this.options.editors.map((u) => u.id)
|
||||||
for (let i = 0; i < this.properties.editors.length; i++)
|
for (let i = 0; i < this.options.editors.length; i++)
|
||||||
formData.append('editors', this.properties.editors[i].id)
|
formData.append('editors', this.options.editors[i].id)
|
||||||
}
|
}
|
||||||
if (this.isOwner() || this.isAnonymousMap()) {
|
if (this.isOwner() || this.isAnonymousMap()) {
|
||||||
formData.append('edit_status', this.properties.edit_status)
|
formData.append('edit_status', this.options.edit_status)
|
||||||
}
|
}
|
||||||
if (this.isOwner()) {
|
if (this.isOwner()) {
|
||||||
formData.append('owner', this.properties.owner?.id)
|
formData.append('owner', this.options.owner?.id)
|
||||||
formData.append('team', this.properties.team?.id || '')
|
formData.append('team', this.options.team?.id || '')
|
||||||
formData.append('share_status', this.properties.share_status)
|
formData.append('share_status', this.options.share_status)
|
||||||
}
|
}
|
||||||
const [data, response, error] = await this._umap.server.post(
|
const [data, response, error] = await this.map.server.post(
|
||||||
this.getUrl(),
|
this.getUrl(),
|
||||||
{},
|
{},
|
||||||
formData
|
formData
|
||||||
)
|
)
|
||||||
if (!error) {
|
if (!error) {
|
||||||
this.commit()
|
this.commit()
|
||||||
|
this.map.fire('postsync')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getUrl() {
|
getUrl() {
|
||||||
return this._umap.urls.get('map_update_permissions', {
|
return Utils.template(this.map.options.urls.map_update_permissions, {
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttachUrl() {
|
getAttachUrl() {
|
||||||
return this._umap.urls.get('map_attach_owner', {
|
return Utils.template(this.map.options.urls.map_attach_owner, {
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
commit() {
|
commit() {
|
||||||
this._umap.properties.permissions = Object.assign(
|
this.map.options.permissions = Object.assign(
|
||||||
{},
|
this.map.options.permissions,
|
||||||
this._umap.properties.permissions,
|
this.options
|
||||||
this.properties
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getShareStatusDisplay() {
|
getShareStatusDisplay() {
|
||||||
if (this._umap.properties.share_statuses) {
|
if (this.map.options.share_statuses) {
|
||||||
return Object.fromEntries(this._umap.properties.share_statuses)[
|
return Object.fromEntries(this.map.options.share_statuses)[
|
||||||
this.properties.share_status
|
this.options.share_status
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataLayerPermissions extends ServerStored {
|
export class DataLayerPermissions extends ServerStored {
|
||||||
constructor(umap, datalayer) {
|
constructor(datalayer) {
|
||||||
super()
|
super()
|
||||||
this._umap = umap
|
this.options = Object.assign(
|
||||||
this.properties = Object.assign(
|
|
||||||
{
|
{
|
||||||
edit_status: null,
|
edit_status: null,
|
||||||
},
|
},
|
||||||
|
@ -240,16 +239,20 @@ export class DataLayerPermissions extends ServerStored {
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get map() {
|
||||||
|
return this.datalayer.map
|
||||||
|
}
|
||||||
|
|
||||||
edit(container) {
|
edit(container) {
|
||||||
const fields = [
|
const fields = [
|
||||||
[
|
[
|
||||||
'properties.edit_status',
|
'options.edit_status',
|
||||||
{
|
{
|
||||||
handler: 'IntSelect',
|
handler: 'IntSelect',
|
||||||
label: translate('Who can edit "{layer}"', {
|
label: translate('Who can edit "{layer}"', {
|
||||||
layer: this.datalayer.getName(),
|
layer: this.datalayer.getName(),
|
||||||
}),
|
}),
|
||||||
selectOptions: this._umap.properties.datalayer_edit_statuses,
|
selectOptions: this.map.options.datalayer_edit_statuses,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
@ -261,8 +264,8 @@ export class DataLayerPermissions extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
getUrl() {
|
getUrl() {
|
||||||
return this._umap.urls.get('datalayer_permissions', {
|
return this.map.urls.get('datalayer_permissions', {
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
pk: this.datalayer.umap_id,
|
pk: this.datalayer.umap_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -270,8 +273,8 @@ export class DataLayerPermissions extends ServerStored {
|
||||||
async save() {
|
async save() {
|
||||||
if (!this.isDirty) return
|
if (!this.isDirty) return
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('edit_status', this.properties.edit_status)
|
formData.append('edit_status', this.options.edit_status)
|
||||||
const [data, response, error] = await this._umap.server.post(
|
const [data, response, error] = await this.map.server.post(
|
||||||
this.getUrl(),
|
this.getUrl(),
|
||||||
{},
|
{},
|
||||||
formData
|
formData
|
||||||
|
@ -286,7 +289,7 @@ export class DataLayerPermissions extends ServerStored {
|
||||||
this.datalayer.options.permissions = Object.assign(
|
this.datalayer.options.permissions = Object.assign(
|
||||||
{},
|
{},
|
||||||
this.datalayer.options.permissions,
|
this.datalayer.options.permissions,
|
||||||
this.properties
|
this.options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,20 +5,20 @@ import * as Utils from '../../utils.js'
|
||||||
export const LayerMixin = {
|
export const LayerMixin = {
|
||||||
browsable: true,
|
browsable: true,
|
||||||
|
|
||||||
onInit: function (leafletMap) {
|
onInit: function (map) {
|
||||||
if (this.datalayer.autoLoaded()) leafletMap.on('zoomend', this.onZoomEnd, this)
|
if (this.datalayer.autoLoaded()) map.on('zoomend', this.onZoomEnd, this)
|
||||||
},
|
},
|
||||||
|
|
||||||
onDelete: function (leafletMap) {
|
onDelete: function (map) {
|
||||||
leafletMap.off('zoomend', this.onZoomEnd, this)
|
map.off('zoomend', this.onZoomEnd, this)
|
||||||
},
|
},
|
||||||
|
|
||||||
onAdd: function (leafletMap) {
|
onAdd: function (map) {
|
||||||
leafletMap.on('moveend', this.onMoveEnd, this)
|
map.on('moveend', this.onMoveEnd, this)
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemove: function (leafletMap) {
|
onRemove: function (map) {
|
||||||
leafletMap.off('moveend', this.onMoveEnd, this)
|
map.off('moveend', this.onMoveEnd, this)
|
||||||
},
|
},
|
||||||
|
|
||||||
getType: function () {
|
getType: function () {
|
||||||
|
@ -73,17 +73,17 @@ export const Default = FeatureGroup.extend({
|
||||||
initialize: function (datalayer) {
|
initialize: function (datalayer) {
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
FeatureGroup.prototype.initialize.call(this)
|
FeatureGroup.prototype.initialize.call(this)
|
||||||
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
LayerMixin.onInit.call(this, this.datalayer.map)
|
||||||
},
|
},
|
||||||
|
|
||||||
onAdd: function (leafletMap) {
|
onAdd: function (map) {
|
||||||
LayerMixin.onAdd.call(this, leafletMap)
|
LayerMixin.onAdd.call(this, map)
|
||||||
return FeatureGroup.prototype.onAdd.call(this, leafletMap)
|
return FeatureGroup.prototype.onAdd.call(this, map)
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemove: function (leafletMap) {
|
onRemove: function (map) {
|
||||||
LayerMixin.onRemove.call(this, leafletMap)
|
LayerMixin.onRemove.call(this, map)
|
||||||
return FeatureGroup.prototype.onRemove.call(this, leafletMap)
|
return FeatureGroup.prototype.onRemove.call(this, map)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ const ClassifiedMixin = {
|
||||||
}
|
}
|
||||||
this.ensureOptions(this.datalayer.options[key])
|
this.ensureOptions(this.datalayer.options[key])
|
||||||
FeatureGroup.prototype.initialize.call(this, [], this.datalayer.options[key])
|
FeatureGroup.prototype.initialize.call(this, [], this.datalayer.options[key])
|
||||||
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
LayerMixin.onInit.call(this, this.datalayer.map)
|
||||||
},
|
},
|
||||||
|
|
||||||
ensureOptions: () => {},
|
ensureOptions: () => {},
|
||||||
|
|
|
@ -40,7 +40,7 @@ export const Cluster = L.MarkerClusterGroup.extend({
|
||||||
options.maxClusterRadius = this.datalayer.options.cluster.radius
|
options.maxClusterRadius = this.datalayer.options.cluster.radius
|
||||||
}
|
}
|
||||||
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
||||||
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
LayerMixin.onInit.call(this, this.datalayer.map)
|
||||||
this._markerCluster = MarkerCluster
|
this._markerCluster = MarkerCluster
|
||||||
this._layers = []
|
this._layers = []
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,7 +21,7 @@ export const Heat = L.HeatLayer.extend({
|
||||||
initialize: function (datalayer) {
|
initialize: function (datalayer) {
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.options.heat)
|
L.HeatLayer.prototype.initialize.call(this, [], this.datalayer.options.heat)
|
||||||
LayerMixin.onInit.call(this, this.datalayer._leafletMap)
|
LayerMixin.onInit.call(this, this.datalayer.map)
|
||||||
if (!Utils.isObject(this.datalayer.options.heat)) {
|
if (!Utils.isObject(this.datalayer.options.heat)) {
|
||||||
this.datalayer.options.heat = {}
|
this.datalayer.options.heat = {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,581 +0,0 @@
|
||||||
// Goes here all code related to Leaflet, DOM and user interactions.
|
|
||||||
import {
|
|
||||||
Map as BaseMap,
|
|
||||||
DomUtil,
|
|
||||||
DomEvent,
|
|
||||||
latLngBounds,
|
|
||||||
latLng,
|
|
||||||
Control,
|
|
||||||
setOptions,
|
|
||||||
} from '../../../vendors/leaflet/leaflet-src.esm.js'
|
|
||||||
import { translate } from '../i18n.js'
|
|
||||||
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
|
|
||||||
import * as Utils from '../utils.js'
|
|
||||||
import * as Icon from './icon.js'
|
|
||||||
import ContextMenu from '../ui/contextmenu.js'
|
|
||||||
|
|
||||||
// Those options are not saved on the server, so they can live here
|
|
||||||
// instead of in umap.properties
|
|
||||||
BaseMap.mergeOptions({
|
|
||||||
demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' },
|
|
||||||
attributionControl: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const ControlsMixin = {
|
|
||||||
HIDDABLE_CONTROLS: [
|
|
||||||
'zoom',
|
|
||||||
'search',
|
|
||||||
'fullscreen',
|
|
||||||
'embed',
|
|
||||||
'datalayers',
|
|
||||||
'caption',
|
|
||||||
'locate',
|
|
||||||
'measure',
|
|
||||||
'editinosm',
|
|
||||||
'star',
|
|
||||||
'tilelayers',
|
|
||||||
],
|
|
||||||
|
|
||||||
initControls: function () {
|
|
||||||
this._controls = {}
|
|
||||||
|
|
||||||
if (this._umap.hasEditMode() && !this.options.noControl) {
|
|
||||||
new U.EditControl(this).addTo(this)
|
|
||||||
|
|
||||||
new U.DrawToolbar({ map: this }).addTo(this)
|
|
||||||
const editActions = [
|
|
||||||
U.EditCaptionAction,
|
|
||||||
U.EditPropertiesAction,
|
|
||||||
U.EditLayersAction,
|
|
||||||
U.ChangeTileLayerAction,
|
|
||||||
U.UpdateExtentAction,
|
|
||||||
U.UpdatePermsAction,
|
|
||||||
U.ImportAction,
|
|
||||||
]
|
|
||||||
if (this.options.editMode === 'advanced') {
|
|
||||||
new U.SettingsToolbar({ actions: editActions }).addTo(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._controls.zoom = new Control.Zoom({
|
|
||||||
zoomInTitle: translate('Zoom in'),
|
|
||||||
zoomOutTitle: translate('Zoom out'),
|
|
||||||
})
|
|
||||||
this._controls.datalayers = new U.DataLayersControl(this._umap)
|
|
||||||
this._controls.caption = new U.CaptionControl(this._umap)
|
|
||||||
this._controls.locate = new U.Locate(this, {
|
|
||||||
strings: {
|
|
||||||
title: translate('Center map on your location'),
|
|
||||||
},
|
|
||||||
showPopup: false,
|
|
||||||
// We style this control in our own CSS for consistency with other controls,
|
|
||||||
// but the control breaks if we don't specify a class here, so a fake class
|
|
||||||
// will do.
|
|
||||||
icon: 'umap-fake-class',
|
|
||||||
iconLoading: 'umap-fake-class',
|
|
||||||
flyTo: this.options.easing,
|
|
||||||
onLocationError: (err) => U.Alert.error(err.message),
|
|
||||||
})
|
|
||||||
this._controls.fullscreen = new Control.Fullscreen({
|
|
||||||
title: {
|
|
||||||
false: translate('View Fullscreen'),
|
|
||||||
true: translate('Exit Fullscreen'),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this._controls.search = new U.SearchControl()
|
|
||||||
this._controls.embed = new Control.Embed(this._umap)
|
|
||||||
this._controls.tilelayersChooser = new U.TileLayerChooser(this)
|
|
||||||
if (this.options.user?.id) this._controls.star = new U.StarControl(this._umap)
|
|
||||||
this._controls.editinosm = new Control.EditInOSM({
|
|
||||||
position: 'topleft',
|
|
||||||
widgetOptions: {
|
|
||||||
helpText: translate(
|
|
||||||
'Open this map extent in a map editor to provide more accurate data to OpenStreetMap'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
this._controls.measure = new L.MeasureControl().initHandler(this)
|
|
||||||
this._controls.more = new U.MoreControls()
|
|
||||||
this._controls.scale = L.control.scale()
|
|
||||||
this._controls.permanentCredit = new U.PermanentCreditsControl(this)
|
|
||||||
if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
|
|
||||||
else this.scrollWheelZoom.disable()
|
|
||||||
this._umap.drop = new U.DropControl(this)
|
|
||||||
this._controls.tilelayers = new U.TileLayerControl(this)
|
|
||||||
},
|
|
||||||
|
|
||||||
renderControls: function () {
|
|
||||||
const hasSlideshow = Boolean(this.options.slideshow?.active)
|
|
||||||
const barEnabled = this.options.captionBar || hasSlideshow
|
|
||||||
document.body.classList.toggle('umap-caption-bar-enabled', barEnabled)
|
|
||||||
document.body.classList.toggle('umap-slideshow-enabled', hasSlideshow)
|
|
||||||
for (const control of Object.values(this._controls)) {
|
|
||||||
this.removeControl(control)
|
|
||||||
}
|
|
||||||
if (this.options.noControl) return
|
|
||||||
|
|
||||||
this._controls.attribution = new U.AttributionControl().addTo(this)
|
|
||||||
if (this.options.miniMap) {
|
|
||||||
this.whenReady(function () {
|
|
||||||
if (this.selectedTilelayer) {
|
|
||||||
this._controls.miniMap = new Control.MiniMap(this.selectedTilelayer, {
|
|
||||||
aimingRectOptions: {
|
|
||||||
color: this._umap.getProperty('color'),
|
|
||||||
fillColor: this._umap.getProperty('fillColor'),
|
|
||||||
stroke: this._umap.getProperty('stroke'),
|
|
||||||
fill: this._umap.getProperty('fill'),
|
|
||||||
weight: this._umap.getProperty('weight'),
|
|
||||||
opacity: this._umap.getProperty('opacity'),
|
|
||||||
fillOpacity: this._umap.getProperty('fillOpacity'),
|
|
||||||
},
|
|
||||||
}).addTo(this)
|
|
||||||
this._controls.miniMap._miniMap.invalidateSize()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for (const name of this.HIDDABLE_CONTROLS) {
|
|
||||||
const status = this._umap.getProperty(`${name}Control`)
|
|
||||||
if (status === false) continue
|
|
||||||
const control = this._controls[name]
|
|
||||||
if (!control) continue
|
|
||||||
control.addTo(this)
|
|
||||||
if (status === undefined || status === null) {
|
|
||||||
DomUtil.addClass(control._container, 'display-on-more')
|
|
||||||
} else {
|
|
||||||
DomUtil.removeClass(control._container, 'display-on-more')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this._umap.getProperty('permanentCredit'))
|
|
||||||
this._controls.permanentCredit.addTo(this)
|
|
||||||
if (this._umap.getProperty('moreControl')) this._controls.more.addTo(this)
|
|
||||||
if (this._umap.getProperty('scaleControl')) this._controls.scale.addTo(this)
|
|
||||||
this._controls.tilelayers.setLayers()
|
|
||||||
},
|
|
||||||
|
|
||||||
renderEditToolbar: function () {
|
|
||||||
const className = 'umap-main-edit-toolbox'
|
|
||||||
const container =
|
|
||||||
document.querySelector(`.${className}`) ||
|
|
||||||
DomUtil.create('div', `${className} with-transition dark`, this._controlContainer)
|
|
||||||
container.innerHTML = ''
|
|
||||||
const leftContainer = DomUtil.create('div', 'umap-left-edit-toolbox', container)
|
|
||||||
const rightContainer = DomUtil.create('div', 'umap-right-edit-toolbox', container)
|
|
||||||
const logo = DomUtil.create('div', 'logo', leftContainer)
|
|
||||||
DomUtil.createLink('', logo, 'uMap', '/', null, translate('Go to the homepage'))
|
|
||||||
const nameButton = DomUtil.createButton('map-name', leftContainer, '')
|
|
||||||
DomEvent.on(nameButton, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: translate('Edit the title of the map'),
|
|
||||||
anchor: nameButton,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const shareStatusButton = DomUtil.createButton(
|
|
||||||
'share-status',
|
|
||||||
leftContainer,
|
|
||||||
'',
|
|
||||||
this._umap.permissions.edit,
|
|
||||||
this._umap.permissions
|
|
||||||
)
|
|
||||||
DomEvent.on(shareStatusButton, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: translate('Update who can see and edit the map'),
|
|
||||||
anchor: shareStatusButton,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (this.options.editMode === 'advanced') {
|
|
||||||
DomEvent.on(nameButton, 'click', this._umap.editCaption, this._umap)
|
|
||||||
DomEvent.on(
|
|
||||||
shareStatusButton,
|
|
||||||
'click',
|
|
||||||
this._umap.permissions.edit,
|
|
||||||
this._umap.permissions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (this.options.user?.id) {
|
|
||||||
const button = U.Utils.loadTemplate(`
|
|
||||||
<button class="umap-user flat" type="button">
|
|
||||||
<i class="icon icon-16 icon-profile"></i>
|
|
||||||
<span>${this.options.user.name}</span>
|
|
||||||
</button>
|
|
||||||
`)
|
|
||||||
rightContainer.appendChild(button)
|
|
||||||
const menu = new ContextMenu({ className: 'dark', fixed: true })
|
|
||||||
const actions = [
|
|
||||||
{
|
|
||||||
label: translate('New map'),
|
|
||||||
action: this._umap.urls.get('map_new'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: translate('My maps'),
|
|
||||||
action: this._umap.urls.get('user_dashboard'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: translate('My teams'),
|
|
||||||
action: this._umap.urls.get('user_teams'),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
if (this._umap.urls.has('user_profile')) {
|
|
||||||
actions.push({
|
|
||||||
label: translate('My profile'),
|
|
||||||
action: this._umap.urls.get('user_profile'),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
button.addEventListener('click', () => {
|
|
||||||
menu.openBelow(button, actions)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const connectedPeers = this._umap.sync.getNumberOfConnectedPeers()
|
|
||||||
if (connectedPeers !== 0) {
|
|
||||||
const connectedPeersCount = DomUtil.createButton(
|
|
||||||
'leaflet-control-connected-peers',
|
|
||||||
rightContainer,
|
|
||||||
''
|
|
||||||
)
|
|
||||||
DomEvent.on(connectedPeersCount, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: translate(
|
|
||||||
'{connectedPeers} peer(s) currently connected to this map',
|
|
||||||
{
|
|
||||||
connectedPeers: connectedPeers,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
anchor: connectedPeersCount,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const updateConnectedPeersCount = () => {
|
|
||||||
connectedPeersCount.innerHTML = this._umap.sync.getNumberOfConnectedPeers()
|
|
||||||
}
|
|
||||||
updateConnectedPeersCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
this._umap.help.getStartedLink(rightContainer)
|
|
||||||
const controlEditCancel = DomUtil.createButton(
|
|
||||||
'leaflet-control-edit-cancel',
|
|
||||||
rightContainer,
|
|
||||||
DomUtil.add('span', '', null, translate('Cancel edits')),
|
|
||||||
() => this._umap.askForReset()
|
|
||||||
)
|
|
||||||
DomEvent.on(controlEditCancel, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: this._umap.help.displayLabel('CANCEL'),
|
|
||||||
anchor: controlEditCancel,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const controlEditDisable = DomUtil.createButton(
|
|
||||||
'leaflet-control-edit-disable',
|
|
||||||
rightContainer,
|
|
||||||
DomUtil.add('span', '', null, translate('View')),
|
|
||||||
this._umap.disableEdit,
|
|
||||||
this._umap
|
|
||||||
)
|
|
||||||
DomEvent.on(controlEditDisable, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: this._umap.help.displayLabel('PREVIEW'),
|
|
||||||
anchor: controlEditDisable,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const controlEditSave = DomUtil.createButton(
|
|
||||||
'leaflet-control-edit-save button',
|
|
||||||
rightContainer,
|
|
||||||
DomUtil.add('span', '', null, translate('Save')),
|
|
||||||
() => this._umap.saveAll()
|
|
||||||
)
|
|
||||||
DomEvent.on(controlEditSave, 'mouseover', () => {
|
|
||||||
this._umap.tooltip.open({
|
|
||||||
content: this._umap.help.displayLabel('SAVE'),
|
|
||||||
anchor: controlEditSave,
|
|
||||||
position: 'bottom',
|
|
||||||
delay: 500,
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
renderCaptionBar: function () {
|
|
||||||
if (this.options.noControl) return
|
|
||||||
const container =
|
|
||||||
this._controlContainer.querySelector('.umap-caption-bar') ||
|
|
||||||
DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
|
|
||||||
container.innerHTML = ''
|
|
||||||
const name = DomUtil.create('h3', 'map-name', container)
|
|
||||||
DomEvent.disableClickPropagation(container)
|
|
||||||
this._umap.addAuthorLink(container)
|
|
||||||
if (this._umap.getProperty('captionMenus')) {
|
|
||||||
DomUtil.createButton(
|
|
||||||
'umap-about-link flat',
|
|
||||||
container,
|
|
||||||
translate('Open caption'),
|
|
||||||
() => this._umap.openCaption()
|
|
||||||
)
|
|
||||||
DomUtil.createButton(
|
|
||||||
'umap-open-browser-link flat',
|
|
||||||
container,
|
|
||||||
translate('Browse data'),
|
|
||||||
() => this.openBrowser('data')
|
|
||||||
)
|
|
||||||
if (this.options.facetKey) {
|
|
||||||
DomUtil.createButton(
|
|
||||||
'umap-open-filter-link flat',
|
|
||||||
container,
|
|
||||||
translate('Filter data'),
|
|
||||||
() => this.openBrowser('filters')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._umap.onceDatalayersLoaded(() => {
|
|
||||||
this._umap.slideshow.renderToolbox(container)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const ManageTilelayerMixin = {
|
|
||||||
initTileLayers: function () {
|
|
||||||
this.tilelayers = []
|
|
||||||
for (const props of this.options.tilelayers) {
|
|
||||||
const layer = this.createTileLayer(props)
|
|
||||||
this.tilelayers.push(layer)
|
|
||||||
if (
|
|
||||||
this.options.tilelayer &&
|
|
||||||
this.options.tilelayer.url_template === props.url_template
|
|
||||||
) {
|
|
||||||
// Keep control over the displayed attribution for non custom tilelayers
|
|
||||||
this.options.tilelayer.attribution = props.attribution
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.options.tilelayer?.url_template && this.options.tilelayer.attribution) {
|
|
||||||
this.customTilelayer = this.createTileLayer(this.options.tilelayer)
|
|
||||||
this.selectTileLayer(this.customTilelayer)
|
|
||||||
} else {
|
|
||||||
this.selectTileLayer(this.tilelayers[0])
|
|
||||||
}
|
|
||||||
if (this._controls) this._controls.tilelayers.setLayers()
|
|
||||||
},
|
|
||||||
|
|
||||||
createTileLayer: (tilelayer) => new L.TileLayer(tilelayer.url_template, tilelayer),
|
|
||||||
|
|
||||||
selectTileLayer: function (tilelayer) {
|
|
||||||
if (tilelayer === this.selectedTilelayer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.addLayer(tilelayer)
|
|
||||||
this.fire('baselayerchange', { layer: tilelayer })
|
|
||||||
if (this.selectedTilelayer) {
|
|
||||||
this.removeLayer(this.selectedTilelayer)
|
|
||||||
}
|
|
||||||
this.selectedTilelayer = tilelayer
|
|
||||||
if (
|
|
||||||
!Number.isNaN(this.selectedTilelayer.options.minZoom) &&
|
|
||||||
this.getZoom() < this.selectedTilelayer.options.minZoom
|
|
||||||
) {
|
|
||||||
this.setZoom(this.selectedTilelayer.options.minZoom)
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!Number.isNaN(this.selectedTilelayer.options.maxZoom) &&
|
|
||||||
this.getZoom() > this.selectedTilelayer.options.maxZoom
|
|
||||||
) {
|
|
||||||
this.setZoom(this.selectedTilelayer.options.maxZoom)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
this.removeLayer(tilelayer)
|
|
||||||
Alert.error(`${translate('Error in the tilelayer URL')}: ${tilelayer._url}`)
|
|
||||||
// Users can put tilelayer URLs by hand, and if they add wrong {variable},
|
|
||||||
// Leaflet throw an error, and then the map is no more editable
|
|
||||||
}
|
|
||||||
this.setOverlay()
|
|
||||||
},
|
|
||||||
|
|
||||||
eachTileLayer: function (callback, context) {
|
|
||||||
const urls = []
|
|
||||||
const callOne = (layer) => {
|
|
||||||
// Prevent adding a duplicate background,
|
|
||||||
// while adding selected/custom on top of the list
|
|
||||||
const url = layer.options.url_template
|
|
||||||
if (urls.indexOf(url) !== -1) return
|
|
||||||
callback.call(context, layer)
|
|
||||||
urls.push(url)
|
|
||||||
}
|
|
||||||
if (this.selectedTilelayer) callOne(this.selectedTilelayer)
|
|
||||||
if (this.customTilelayer) callOne(this.customTilelayer)
|
|
||||||
this.tilelayers.forEach(callOne)
|
|
||||||
},
|
|
||||||
|
|
||||||
setOverlay: function () {
|
|
||||||
if (!this.options.overlay || !this.options.overlay.url_template) return
|
|
||||||
const overlay = this.createTileLayer(this.options.overlay)
|
|
||||||
try {
|
|
||||||
this.addLayer(overlay)
|
|
||||||
if (this.overlay) this.removeLayer(this.overlay)
|
|
||||||
this.overlay = overlay
|
|
||||||
} catch (e) {
|
|
||||||
this.removeLayer(overlay)
|
|
||||||
console.error(e)
|
|
||||||
Alert.error(`${translate('Error in the overlay URL')}: ${overlay._url}`)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTileLayers: function () {
|
|
||||||
const callback = (tilelayer) => {
|
|
||||||
this.options.tilelayer = tilelayer.toJSON()
|
|
||||||
this._umap.isDirty = true
|
|
||||||
}
|
|
||||||
if (this._controls.tilelayersChooser) {
|
|
||||||
this._controls.tilelayersChooser.openSwitcher({ callback, edit: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LeafletMap = BaseMap.extend({
|
|
||||||
includes: [ControlsMixin, ManageTilelayerMixin],
|
|
||||||
|
|
||||||
// The initialize and the setup method might seem similar, but they
|
|
||||||
// serve two different purposes:
|
|
||||||
// initialize is for Leaflet internal, when we do "new LeafletMap",
|
|
||||||
// while setup is the public API for the LeafletMap to actually
|
|
||||||
// render to the DOM.
|
|
||||||
initialize: function (umap, element) {
|
|
||||||
this._umap = umap
|
|
||||||
const options = this._umap.properties
|
|
||||||
|
|
||||||
BaseMap.prototype.initialize.call(this, element, options)
|
|
||||||
|
|
||||||
// After calling parent initialize, as we are doing initCenter our-selves
|
|
||||||
|
|
||||||
this.loader = new Control.Loading()
|
|
||||||
this.loader.onAdd(this)
|
|
||||||
|
|
||||||
if (!this.options.noControl) {
|
|
||||||
DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e))
|
|
||||||
DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e))
|
|
||||||
this.on('click', this.closeInplaceToolbar)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.on('baselayerchange', (e) => {
|
|
||||||
if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
setup: function () {
|
|
||||||
this.initControls()
|
|
||||||
// Needs locate control and hash to exist
|
|
||||||
this.initCenter()
|
|
||||||
this.renderUI()
|
|
||||||
},
|
|
||||||
|
|
||||||
renderUI: function () {
|
|
||||||
setOptions(this, this._umap.properties)
|
|
||||||
this.initTileLayers()
|
|
||||||
this.renderCaptionBar()
|
|
||||||
this.renderEditToolbar()
|
|
||||||
// Needs tilelayer to exist for minimap
|
|
||||||
this.renderControls()
|
|
||||||
this.handleLimitBounds()
|
|
||||||
},
|
|
||||||
|
|
||||||
closeInplaceToolbar: function () {
|
|
||||||
const toolbar = this._toolbars[L.Toolbar.Popup._toolbar_class_id]
|
|
||||||
if (toolbar) toolbar.remove()
|
|
||||||
},
|
|
||||||
|
|
||||||
latLng: (a, b, c) => {
|
|
||||||
// manage geojson case and call original method
|
|
||||||
if (!(a instanceof L.LatLng) && a.coordinates) {
|
|
||||||
// Guess it's a geojson
|
|
||||||
a = [a.coordinates[1], a.coordinates[0]]
|
|
||||||
}
|
|
||||||
return latLng(a, b, c)
|
|
||||||
},
|
|
||||||
|
|
||||||
_setDefaultCenter: function () {
|
|
||||||
this.options.center = this.latLng(this.options.center)
|
|
||||||
this.setView(this.options.center, this.options.zoom)
|
|
||||||
},
|
|
||||||
|
|
||||||
initCenter: function () {
|
|
||||||
this._setDefaultCenter()
|
|
||||||
if (this.options.hash) this.addHash()
|
|
||||||
if (this.options.hash && this._hash.parseHash(location.hash)) {
|
|
||||||
// FIXME An invalid hash will cause the load to fail
|
|
||||||
this._hash.update()
|
|
||||||
} else if (this.options.defaultView === 'locate' && !this.options.noControl) {
|
|
||||||
this._controls.locate.start()
|
|
||||||
} else if (this.options.defaultView === 'data') {
|
|
||||||
this._umap.onceDataLoaded(this._umap.fitDataBounds)
|
|
||||||
} else if (this.options.defaultView === 'latest') {
|
|
||||||
this._umap.onceDataLoaded(() => {
|
|
||||||
if (!this._umap.hasData()) return
|
|
||||||
const datalayer = this._umap.firstVisibleDatalayer()
|
|
||||||
let feature
|
|
||||||
if (datalayer) {
|
|
||||||
const feature = datalayer.getFeatureByIndex(-1)
|
|
||||||
if (feature) {
|
|
||||||
feature.zoomTo({ callback: this.options.noControl ? null : feature.view })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleLimitBounds: function () {
|
|
||||||
const south = Number.parseFloat(this.options.limitBounds.south)
|
|
||||||
const west = Number.parseFloat(this.options.limitBounds.west)
|
|
||||||
const north = Number.parseFloat(this.options.limitBounds.north)
|
|
||||||
const east = Number.parseFloat(this.options.limitBounds.east)
|
|
||||||
if (
|
|
||||||
!Number.isNaN(south) &&
|
|
||||||
!Number.isNaN(west) &&
|
|
||||||
!Number.isNaN(north) &&
|
|
||||||
!Number.isNaN(east)
|
|
||||||
) {
|
|
||||||
const bounds = latLngBounds([
|
|
||||||
[south, west],
|
|
||||||
[north, east],
|
|
||||||
])
|
|
||||||
this.options.minZoom = this.getBoundsZoom(bounds, false)
|
|
||||||
try {
|
|
||||||
this.setMaxBounds(bounds)
|
|
||||||
} catch (e) {
|
|
||||||
// Unusable bounds, like -2 -2 -2 -2?
|
|
||||||
console.error('Error limiting bounds', e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.options.minZoom = 0
|
|
||||||
this.setMaxBounds()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setMaxBounds: function (bounds) {
|
|
||||||
// Hack. Remove me when fix is released:
|
|
||||||
// https://github.com/Leaflet/Leaflet/pull/4494
|
|
||||||
bounds = latLngBounds(bounds)
|
|
||||||
|
|
||||||
if (!bounds.isValid()) {
|
|
||||||
this.options.maxBounds = null
|
|
||||||
return this.off('moveend', this._panInsideMaxBounds)
|
|
||||||
}
|
|
||||||
return BaseMap.prototype.setMaxBounds.call(this, bounds)
|
|
||||||
},
|
|
||||||
|
|
||||||
initEditTools: function () {
|
|
||||||
this.editTools = new U.Editable(this._umap)
|
|
||||||
this.renderEditToolbar()
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -61,15 +61,15 @@ const Panel = Popup.extend({
|
||||||
zoomAnimation: false,
|
zoomAnimation: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
onAdd: function (leafletMap) {
|
onAdd: function (map) {
|
||||||
leafletMap._umap.panel.setDefaultMode('expanded')
|
map.panel.setDefaultMode('expanded')
|
||||||
leafletMap._umap.panel.open({
|
map.panel.open({
|
||||||
content: this._content,
|
content: this._content,
|
||||||
actions: [Browser.backButton(leafletMap._umap)],
|
actions: [Browser.backButton(map)],
|
||||||
})
|
})
|
||||||
|
|
||||||
// fire events as in base class Popup.js:onAdd
|
// fire events as in base class Popup.js:onAdd
|
||||||
leafletMap.fire('popupopen', { popup: this })
|
map.fire('popupopen', { popup: this })
|
||||||
if (this._source) {
|
if (this._source) {
|
||||||
this._source.fire('popupopen', { popup: this }, true)
|
this._source.fire('popupopen', { popup: this }, true)
|
||||||
if (!(this._source instanceof Path)) {
|
if (!(this._source instanceof Path)) {
|
||||||
|
@ -78,11 +78,11 @@ const Panel = Popup.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemove: function (leafletMap) {
|
onRemove: function (map) {
|
||||||
leafletMap._umap.panel.close()
|
map.panel.close()
|
||||||
|
|
||||||
// fire events as in base class Popup.js:onRemove
|
// fire events as in base class Popup.js:onRemove
|
||||||
leafletMap.fire('popupclose', { popup: this })
|
map.fire('popupclose', { popup: this })
|
||||||
if (this._source) {
|
if (this._source) {
|
||||||
this._source.fire('popupclose', { popup: this }, true)
|
this._source.fire('popupclose', { popup: this }, true)
|
||||||
if (!(this._source instanceof Path)) {
|
if (!(this._source instanceof Path)) {
|
||||||
|
|
|
@ -52,7 +52,7 @@ const FeatureMixin = {
|
||||||
onClick: function (event) {
|
onClick: function (event) {
|
||||||
if (this._map.measureTools?.enabled()) return
|
if (this._map.measureTools?.enabled()) return
|
||||||
this._popupHandlersAdded = true // Prevent leaflet from managing event
|
this._popupHandlersAdded = true // Prevent leaflet from managing event
|
||||||
if (!this._map._umap.editEnabled) {
|
if (!this._map.editEnabled) {
|
||||||
this.feature.view(event)
|
this.feature.view(event)
|
||||||
} else if (!this.feature.isReadOnly()) {
|
} else if (!this.feature.isReadOnly()) {
|
||||||
if (event.originalEvent.shiftKey) {
|
if (event.originalEvent.shiftKey) {
|
||||||
|
@ -96,8 +96,8 @@ const FeatureMixin = {
|
||||||
DomEvent.stop(event)
|
DomEvent.stop(event)
|
||||||
const items = this.feature
|
const items = this.feature
|
||||||
.getContextMenuItems(event)
|
.getContextMenuItems(event)
|
||||||
.concat(this._map._umap.getSharedContextMenuItems(event))
|
.concat(this._map.getContextMenuItems(event))
|
||||||
this._map._umap.contextmenu.open(event.originalEvent, items)
|
this._map.contextmenu.open(event.originalEvent, items)
|
||||||
},
|
},
|
||||||
|
|
||||||
onCommit: function () {
|
onCommit: function () {
|
||||||
|
@ -134,7 +134,7 @@ const PointMixin = {
|
||||||
|
|
||||||
_enableDragging: function () {
|
_enableDragging: function () {
|
||||||
// TODO: start dragging after 1 second on mouse down
|
// TODO: start dragging after 1 second on mouse down
|
||||||
if (this._map._umap.editEnabled) {
|
if (this._map.editEnabled) {
|
||||||
if (!this.editEnabled()) this.enableEdit()
|
if (!this.editEnabled()) this.enableEdit()
|
||||||
// Enabling dragging on the marker override the Draggable._OnDown
|
// Enabling dragging on the marker override the Draggable._OnDown
|
||||||
// event, which, as it stopPropagation, refrain the call of
|
// event, which, as it stopPropagation, refrain the call of
|
||||||
|
@ -146,7 +146,7 @@ const PointMixin = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_disableDragging: function () {
|
_disableDragging: function () {
|
||||||
if (this._map._umap.editEnabled) {
|
if (this._map.editEnabled) {
|
||||||
if (this.editor?.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
|
if (this.editor?.drawing) return // when creating a new marker, the mouse can trigger the mouseover/mouseout event
|
||||||
// do not listen to them
|
// do not listen to them
|
||||||
this.disableEdit()
|
this.disableEdit()
|
||||||
|
@ -253,21 +253,21 @@ export const LeafletMarker = Marker.extend({
|
||||||
const PathMixin = {
|
const PathMixin = {
|
||||||
_onMouseOver: function () {
|
_onMouseOver: function () {
|
||||||
if (this._map.measureTools?.enabled()) {
|
if (this._map.measureTools?.enabled()) {
|
||||||
this._map._umap.tooltip.open({ content: this.getMeasure(), anchor: this })
|
this._map.tooltip.open({ content: this.getMeasure(), anchor: this })
|
||||||
} else if (this._map._umap.editEnabled && !this._map._umap.editedFeature) {
|
} else if (this._map.editEnabled && !this._map.editedFeature) {
|
||||||
this._map._umap.tooltip.open({ content: translate('Click to edit'), anchor: this })
|
this._map.tooltip.open({ content: translate('Click to edit'), anchor: this })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
makeGeometryEditable: function () {
|
makeGeometryEditable: function () {
|
||||||
if (this._map._umap.editedFeature !== this.feature) {
|
if (this._map.editedFeature !== this.feature) {
|
||||||
this.disableEdit()
|
this.disableEdit()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this._map.once('moveend', this.makeGeometryEditable, this)
|
this._map.once('moveend', this.makeGeometryEditable, this)
|
||||||
const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
|
const pointsCount = this._parts.reduce((acc, part) => acc + part.length, 0)
|
||||||
if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) {
|
if (pointsCount > 100 && this._map.getZoom() < this._map.getMaxZoom()) {
|
||||||
this._map._umap.tooltip.open({ content: L._('Please zoom in to edit the geometry') })
|
this._map.tooltip.open({ content: L._('Please zoom in to edit the geometry') })
|
||||||
this.disableEdit()
|
this.disableEdit()
|
||||||
} else {
|
} else {
|
||||||
this.enableEdit()
|
this.enableEdit()
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import { AutocompleteDatalist } from './autocomplete.js'
|
import { AutocompleteDatalist } from './autocomplete.js'
|
||||||
import Orderable from './orderable.js'
|
|
||||||
|
|
||||||
const EMPTY_VALUES = ['', undefined, null]
|
const EMPTY_VALUES = ['', undefined, null]
|
||||||
|
|
||||||
|
@ -22,10 +21,10 @@ class Rule {
|
||||||
|
|
||||||
set isDirty(status) {
|
set isDirty(status) {
|
||||||
this._isDirty = status
|
this._isDirty = status
|
||||||
if (status) this._umap.isDirty = status
|
if (status) this.map.isDirty = status
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(umap, condition = '', options = {}) {
|
constructor(map, condition = '', options = {}) {
|
||||||
// TODO make this public properties when browser coverage is ok
|
// TODO make this public properties when browser coverage is ok
|
||||||
// cf https://caniuse.com/?search=public%20class%20field
|
// cf https://caniuse.com/?search=public%20class%20field
|
||||||
this._condition = null
|
this._condition = null
|
||||||
|
@ -38,14 +37,14 @@ class Rule {
|
||||||
['!=', this.not_equal],
|
['!=', this.not_equal],
|
||||||
['=', this.equal],
|
['=', this.equal],
|
||||||
]
|
]
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.active = true
|
this.active = true
|
||||||
this.options = options
|
this.options = options
|
||||||
this.condition = condition
|
this.condition = condition
|
||||||
}
|
}
|
||||||
|
|
||||||
render(fields) {
|
render(fields) {
|
||||||
this._umap.render(fields)
|
this.map.render(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
equal(other) {
|
equal(other) {
|
||||||
|
@ -102,6 +101,10 @@ class Rule {
|
||||||
return this.operator(this.cast(props[this.key]))
|
return this.operator(this.cast(props[this.key]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
getOption(option) {
|
getOption(option) {
|
||||||
return this.options[option]
|
return this.options[option]
|
||||||
}
|
}
|
||||||
|
@ -133,7 +136,7 @@ class Rule {
|
||||||
const defaultShapeProperties = DomUtil.add('div', '', container)
|
const defaultShapeProperties = DomUtil.add('div', '', container)
|
||||||
defaultShapeProperties.appendChild(builder.build())
|
defaultShapeProperties.appendChild(builder.build())
|
||||||
const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
|
const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
|
||||||
const properties = this._umap.allProperties()
|
const properties = this.map.allProperties()
|
||||||
autocomplete.suggestions = properties
|
autocomplete.suggestions = properties
|
||||||
autocomplete.input.addEventListener('input', (event) => {
|
autocomplete.input.addEventListener('input', (event) => {
|
||||||
const value = event.target.value
|
const value = event.target.value
|
||||||
|
@ -141,12 +144,12 @@ class Rule {
|
||||||
autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
|
autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
|
||||||
} else if (value.endsWith('=')) {
|
} else if (value.endsWith('=')) {
|
||||||
const key = value.split('!')[0].split('=')[0]
|
const key = value.split('!')[0].split('=')[0]
|
||||||
autocomplete.suggestions = this._umap
|
autocomplete.suggestions = this.map
|
||||||
.sortedValues(key)
|
.sortedValues(key)
|
||||||
.map((str) => `${value}${str || ''}`)
|
.map((str) => `${value}${str || ''}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this._umap.editPanel.open({ content: container })
|
this.map.editPanel.open({ content: container })
|
||||||
}
|
}
|
||||||
|
|
||||||
renderToolbox(row) {
|
renderToolbox(row) {
|
||||||
|
@ -173,7 +176,7 @@ class Rule {
|
||||||
function () {
|
function () {
|
||||||
if (!confirm(translate('Are you sure you want to delete this rule?'))) return
|
if (!confirm(translate('Are you sure you want to delete this rule?'))) return
|
||||||
this._delete()
|
this._delete()
|
||||||
this._umap.editPanel.close()
|
this.map.editPanel.close()
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
@ -183,27 +186,27 @@ class Rule {
|
||||||
DomEvent.on(toggle, 'click', () => {
|
DomEvent.on(toggle, 'click', () => {
|
||||||
this.active = !this.active
|
this.active = !this.active
|
||||||
row.classList.toggle('off', !this.active)
|
row.classList.toggle('off', !this.active)
|
||||||
this._umap.render(['rules'])
|
this.map.render(['rules'])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_delete() {
|
_delete() {
|
||||||
this._umap.rules.rules = this._umap.rules.rules.filter((rule) => rule !== this)
|
this.map.rules.rules = this.map.rules.rules.filter((rule) => rule !== this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Rules {
|
export default class Rules {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.rules = []
|
this.rules = []
|
||||||
this.loadRules()
|
this.loadRules()
|
||||||
}
|
}
|
||||||
|
|
||||||
loadRules() {
|
loadRules() {
|
||||||
if (!this._umap.properties.rules?.length) return
|
if (!this.map.options.rules?.length) return
|
||||||
for (const { condition, options } of this._umap.properties.rules) {
|
for (const { condition, options } of this.map.options.rules) {
|
||||||
if (!condition) continue
|
if (!condition) continue
|
||||||
this.rules.push(new Rule(this._umap, condition, options))
|
this.rules.push(new Rule(this.map, condition, options))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +225,7 @@ export default class Rules {
|
||||||
else newIdx = referenceIdx + 1
|
else newIdx = referenceIdx + 1
|
||||||
this.rules.splice(newIdx, 0, moved)
|
this.rules.splice(newIdx, 0, moved)
|
||||||
moved.isDirty = true
|
moved.isDirty = true
|
||||||
this._umap.render(['rules'])
|
this.map.render(['rules'])
|
||||||
}
|
}
|
||||||
|
|
||||||
edit(container) {
|
edit(container) {
|
||||||
|
@ -233,21 +236,21 @@ export default class Rules {
|
||||||
rule.renderToolbox(DomUtil.create('li', 'orderable', ul))
|
rule.renderToolbox(DomUtil.create('li', 'orderable', ul))
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderable = new Orderable(ul, this.onReorder.bind(this))
|
const orderable = new U.Orderable(ul, this.onReorder.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)
|
DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
addRule() {
|
addRule() {
|
||||||
const rule = new Rule(this._umap)
|
const rule = new Rule(this.map)
|
||||||
rule.isDirty = true
|
rule.isDirty = true
|
||||||
this.rules.push(rule)
|
this.rules.push(rule)
|
||||||
rule.edit(map)
|
rule.edit(map)
|
||||||
}
|
}
|
||||||
|
|
||||||
commit() {
|
commit() {
|
||||||
this._umap.properties.rules = this.rules.map((rule) => {
|
this.map.options.rules = this.rules.map((rule) => {
|
||||||
return {
|
return {
|
||||||
condition: rule.condition,
|
condition: rule.condition,
|
||||||
options: rule.options,
|
options: rule.options,
|
||||||
|
|
|
@ -10,21 +10,22 @@ export async function save() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function add(obj) {
|
export function add(obj) {
|
||||||
_queue.add(obj)
|
_queue.add(obj)
|
||||||
onUpdate()
|
_onUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove(obj) {
|
export function remove(obj) {
|
||||||
_queue.delete(obj)
|
_queue.delete(obj)
|
||||||
onUpdate()
|
_onUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
function has(obj) {
|
export function has(obj) {
|
||||||
return _queue.has(obj)
|
return _queue.has(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUpdate() {
|
function _onUpdate() {
|
||||||
|
console.log(_queue)
|
||||||
isDirty = Boolean(_queue.size)
|
isDirty = Boolean(_queue.size)
|
||||||
document.body.classList.toggle('umap-is-dirty', isDirty)
|
document.body.classList.toggle('umap-is-dirty', isDirty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
|
|
||||||
export default class Share {
|
export default class Share {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
@ -22,11 +22,11 @@ export default class Share {
|
||||||
window.location.protocol + Utils.getBaseUrl()
|
window.location.protocol + Utils.getBaseUrl()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (this._umap.properties.shortUrl) {
|
if (this.map.options.shortUrl) {
|
||||||
DomUtil.createCopiableInput(
|
DomUtil.createCopiableInput(
|
||||||
this.container,
|
this.container,
|
||||||
translate('Short link'),
|
translate('Short link'),
|
||||||
this._umap.properties.shortUrl
|
this.map.options.shortUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,8 @@ export default class Share {
|
||||||
this.container,
|
this.container,
|
||||||
translate('All data and settings of the map')
|
translate('All data and settings of the map')
|
||||||
)
|
)
|
||||||
const downloadUrl = this._umap.urls.get('map_download', {
|
const downloadUrl = Utils.template(this.map.options.urls.map_download, {
|
||||||
map_id: this._umap.properties.umap_id,
|
map_id: this.map.options.umap_id,
|
||||||
})
|
})
|
||||||
const link = Utils.loadTemplate(`
|
const link = Utils.loadTemplate(`
|
||||||
<div>
|
<div>
|
||||||
|
@ -115,11 +115,10 @@ export default class Share {
|
||||||
'queryString.captionBar',
|
'queryString.captionBar',
|
||||||
'queryString.captionMenus',
|
'queryString.captionMenus',
|
||||||
]
|
]
|
||||||
// TODO: move HIDDABLE_CONTROLS to SCHEMA ?
|
for (let i = 0; i < this.map.HIDDABLE_CONTROLS.length; i++) {
|
||||||
for (const name of this._umap._leafletMap.HIDDABLE_CONTROLS) {
|
UIFields.push(`queryString.${this.map.HIDDABLE_CONTROLS[i]}Control`)
|
||||||
UIFields.push(`queryString.${name}Control`)
|
|
||||||
}
|
}
|
||||||
const iframeExporter = new IframeExporter(this._umap)
|
const iframeExporter = new IframeExporter(this.map)
|
||||||
const buildIframeCode = () => {
|
const buildIframeCode = () => {
|
||||||
iframe.textContent = iframeExporter.build()
|
iframe.textContent = iframeExporter.build()
|
||||||
exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
|
exportUrl.value = window.location.protocol + iframeExporter.buildUrl()
|
||||||
|
@ -137,13 +136,13 @@ export default class Share {
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
if (!this.container) this.build()
|
if (!this.container) this.build()
|
||||||
this._umap.panel.open({ content: this.container })
|
this.map.panel.open({ content: this.container })
|
||||||
}
|
}
|
||||||
|
|
||||||
async format(mode) {
|
async format(mode) {
|
||||||
const type = EXPORT_FORMATS[mode]
|
const type = EXPORT_FORMATS[mode]
|
||||||
const content = await type.formatter(this._umap)
|
const content = await type.formatter(this.map)
|
||||||
const filename = Utils.slugify(this._umap.properties.name) + type.ext
|
const filename = Utils.slugify(this.map.options.name) + type.ext
|
||||||
return { content, filetype: type.filetype, filename }
|
return { content, filetype: type.filetype, filename }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,8 +161,8 @@ export default class Share {
|
||||||
}
|
}
|
||||||
|
|
||||||
class IframeExporter {
|
class IframeExporter {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this.baseUrl = Utils.getBaseUrl()
|
this.baseUrl = Utils.getBaseUrl()
|
||||||
this.options = {
|
this.options = {
|
||||||
includeFullScreenLink: true,
|
includeFullScreenLink: true,
|
||||||
|
@ -193,18 +192,22 @@ class IframeExporter {
|
||||||
height: '300px',
|
height: '300px',
|
||||||
}
|
}
|
||||||
// Use map default, not generic default
|
// Use map default, not generic default
|
||||||
this.queryString.onLoadPanel = this._umap.getProperty('onLoadPanel')
|
this.queryString.onLoadPanel = this.map.getOption('onLoadPanel')
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
}
|
}
|
||||||
|
|
||||||
buildUrl(options) {
|
buildUrl(options) {
|
||||||
const datalayers = []
|
const datalayers = []
|
||||||
if (this.options.viewCurrentFeature && this._umap.currentFeature) {
|
if (this.options.viewCurrentFeature && this.map.currentFeature) {
|
||||||
this.queryString.feature = this._umap.currentFeature.getSlug()
|
this.queryString.feature = this.map.currentFeature.getSlug()
|
||||||
} else {
|
} else {
|
||||||
delete this.queryString.feature
|
delete this.queryString.feature
|
||||||
}
|
}
|
||||||
if (this.options.keepCurrentDatalayers) {
|
if (this.options.keepCurrentDatalayers) {
|
||||||
this._umap.eachDataLayer((datalayer) => {
|
this.map.eachDataLayer((datalayer) => {
|
||||||
if (datalayer.isVisible() && datalayer.umap_id) {
|
if (datalayer.isVisible() && datalayer.umap_id) {
|
||||||
datalayers.push(datalayer.umap_id)
|
datalayers.push(datalayer.umap_id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,20 @@ const TOOLBOX_TEMPLATE = `
|
||||||
`
|
`
|
||||||
|
|
||||||
export default class Slideshow extends WithTemplate {
|
export default class Slideshow extends WithTemplate {
|
||||||
constructor(umap, leafletMap, properties) {
|
constructor(map, options) {
|
||||||
super()
|
super()
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this._id = null
|
this._id = null
|
||||||
this.CLASSNAME = 'umap-slideshow-active'
|
this.CLASSNAME = 'umap-slideshow-active'
|
||||||
this.setProperties(properties)
|
this.setOptions(options)
|
||||||
this._current = null
|
this._current = null
|
||||||
|
|
||||||
if (this.properties.autoplay) {
|
if (this.options.autoplay) {
|
||||||
this._umap.onceDataLoaded(function () {
|
this.map.onceDataLoaded(function () {
|
||||||
this.play()
|
this.play()
|
||||||
}, this)
|
}, this)
|
||||||
}
|
}
|
||||||
leafletMap.on(
|
this.map.on(
|
||||||
'edit:enabled',
|
'edit:enabled',
|
||||||
function () {
|
function () {
|
||||||
this.stop()
|
this.stop()
|
||||||
|
@ -54,22 +54,22 @@ export default class Slideshow extends WithTemplate {
|
||||||
return this.current.getNext()
|
return this.current.getNext()
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(properties) {
|
setOptions(options) {
|
||||||
this.properties = Object.assign(
|
this.options = Object.assign(
|
||||||
{
|
{
|
||||||
delay: 5000,
|
delay: 5000,
|
||||||
autoplay: false,
|
autoplay: false,
|
||||||
},
|
},
|
||||||
properties
|
options
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultDatalayer() {
|
defaultDatalayer() {
|
||||||
return this._umap.findDataLayer((d) => d.canBrowse())
|
return this.map.findDataLayer((d) => d.canBrowse())
|
||||||
}
|
}
|
||||||
|
|
||||||
startSpinner() {
|
startSpinner() {
|
||||||
const time = Number.parseInt(this.properties.delay, 10)
|
const time = Number.parseInt(this.options.delay, 10)
|
||||||
if (!time) return
|
if (!time) return
|
||||||
const css = `rotation ${time / 1000}s infinite linear`
|
const css = `rotation ${time / 1000}s infinite linear`
|
||||||
const spinner = document.querySelector('.umap-slideshow-toolbox .play .spinner')
|
const spinner = document.querySelector('.umap-slideshow-toolbox .play .spinner')
|
||||||
|
@ -83,9 +83,9 @@ export default class Slideshow extends WithTemplate {
|
||||||
|
|
||||||
play() {
|
play() {
|
||||||
if (this._id) return
|
if (this._id) return
|
||||||
if (this._umap.editEnabled || !this._umap.properties.slideshow.active) return
|
if (this.map.editEnabled || !this.map.options.slideshow.active) return
|
||||||
L.DomUtil.addClass(document.body, this.CLASSNAME)
|
L.DomUtil.addClass(document.body, this.CLASSNAME)
|
||||||
this._id = window.setInterval(L.bind(this.loop, this), this.properties.delay)
|
this._id = window.setInterval(L.bind(this.loop, this), this.options.delay)
|
||||||
this.startSpinner()
|
this.startSpinner()
|
||||||
this.loop()
|
this.loop()
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ export default class Slideshow extends WithTemplate {
|
||||||
|
|
||||||
step() {
|
step() {
|
||||||
if (!this.current) return this.stop()
|
if (!this.current) return this.stop()
|
||||||
this.current.zoomTo({ easing: this.properties.easing })
|
this.current.zoomTo({ easing: this.options.easing })
|
||||||
this.current.view()
|
this.current.view()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { fieldInSchema } from '../utils.js'
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BaseUpdater {
|
class BaseUpdater {
|
||||||
constructor(umap) {
|
constructor(map) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
}
|
}
|
||||||
|
|
||||||
updateObjectValue(obj, key, value) {
|
updateObjectValue(obj, key, value) {
|
||||||
|
@ -32,8 +32,8 @@ class BaseUpdater {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataLayerFromID(layerId) {
|
getDataLayerFromID(layerId) {
|
||||||
if (layerId) return this._umap.getDataLayerByUmapId(layerId)
|
if (layerId) return this.map.getDataLayerByUmapId(layerId)
|
||||||
return this._umap.defaultEditDataLayer()
|
return this.map.defaultEditDataLayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
applyMessage(payload) {
|
applyMessage(payload) {
|
||||||
|
@ -45,18 +45,18 @@ class BaseUpdater {
|
||||||
export class MapUpdater extends BaseUpdater {
|
export class MapUpdater extends BaseUpdater {
|
||||||
update({ key, value }) {
|
update({ key, value }) {
|
||||||
if (fieldInSchema(key)) {
|
if (fieldInSchema(key)) {
|
||||||
this.updateObjectValue(this._umap, key, value)
|
this.updateObjectValue(this.map, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._umap.render([key])
|
this.map.render([key])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataLayerUpdater extends BaseUpdater {
|
export class DataLayerUpdater extends BaseUpdater {
|
||||||
upsert({ value }) {
|
upsert({ value }) {
|
||||||
// Inserts does not happen (we use multiple updates instead).
|
// Inserts does not happen (we use multiple updates instead).
|
||||||
this._umap.createDataLayer(value, false)
|
this.map.createDataLayer(value, false)
|
||||||
this._umap.render([])
|
this.map.render([])
|
||||||
}
|
}
|
||||||
|
|
||||||
update({ key, metadata, value }) {
|
update({ key, metadata, value }) {
|
||||||
|
|
|
@ -14,11 +14,10 @@ const TEMPLATE = `
|
||||||
`
|
`
|
||||||
|
|
||||||
export default class TableEditor extends WithTemplate {
|
export default class TableEditor extends WithTemplate {
|
||||||
constructor(umap, datalayer, leafletMap) {
|
constructor(datalayer) {
|
||||||
super()
|
super()
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
this._umap = umap
|
this.map = this.datalayer.map
|
||||||
this._leafletMap = leafletMap
|
|
||||||
this.contextmenu = new ContextMenu({ className: 'dark' })
|
this.contextmenu = new ContextMenu({ className: 'dark' })
|
||||||
this.table = this.loadTemplate(TEMPLATE)
|
this.table = this.loadTemplate(TEMPLATE)
|
||||||
if (!this.datalayer.isRemoteLayer()) {
|
if (!this.datalayer.isRemoteLayer()) {
|
||||||
|
@ -37,20 +36,20 @@ export default class TableEditor extends WithTemplate {
|
||||||
openHeaderMenu(property) {
|
openHeaderMenu(property) {
|
||||||
const actions = []
|
const actions = []
|
||||||
let filterItem
|
let filterItem
|
||||||
if (this._umap.facets.has(property)) {
|
if (this.map.facets.has(property)) {
|
||||||
filterItem = {
|
filterItem = {
|
||||||
label: translate('Remove filter for this column'),
|
label: translate('Remove filter for this column'),
|
||||||
action: () => {
|
action: () => {
|
||||||
this._umap.facets.remove(property)
|
this.map.facets.remove(property)
|
||||||
this._umap.browser.open('filters')
|
this.map.browser.open('filters')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filterItem = {
|
filterItem = {
|
||||||
label: translate('Add filter for this column'),
|
label: translate('Add filter for this column'),
|
||||||
action: () => {
|
action: () => {
|
||||||
this._umap.facets.add(property)
|
this.map.facets.add(property)
|
||||||
this._umap.browser.open('filters')
|
this.map.browser.open('filters')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,8 +86,8 @@ export default class TableEditor extends WithTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBody() {
|
renderBody() {
|
||||||
const bounds = this._leafletMap.getBounds()
|
const bounds = this.map.getBounds()
|
||||||
const inBbox = this._umap.browser.options.inBbox
|
const inBbox = this.map.browser.options.inBbox
|
||||||
let html = ''
|
let html = ''
|
||||||
this.datalayer.eachFeature((feature) => {
|
this.datalayer.eachFeature((feature) => {
|
||||||
if (feature.isFiltered()) return
|
if (feature.isFiltered()) return
|
||||||
|
@ -122,7 +121,7 @@ export default class TableEditor extends WithTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
renameProperty(property) {
|
renameProperty(property) {
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.prompt(translate('Please enter the new name of this property'))
|
.prompt(translate('Please enter the new name of this property'))
|
||||||
.then(({ prompt }) => {
|
.then(({ prompt }) => {
|
||||||
if (!prompt || !this.validateName(prompt)) return
|
if (!prompt || !this.validateName(prompt)) return
|
||||||
|
@ -136,7 +135,7 @@ export default class TableEditor extends WithTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProperty(property) {
|
deleteProperty(property) {
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.confirm(
|
.confirm(
|
||||||
translate('Are you sure you want to delete this property on all the features?')
|
translate('Are you sure you want to delete this property on all the features?')
|
||||||
)
|
)
|
||||||
|
@ -151,7 +150,7 @@ export default class TableEditor extends WithTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
addProperty() {
|
addProperty() {
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.prompt(translate('Please enter the name of the property'))
|
.prompt(translate('Please enter the name of the property'))
|
||||||
.then(({ prompt }) => {
|
.then(({ prompt }) => {
|
||||||
if (!prompt || !this.validateName(prompt)) return
|
if (!prompt || !this.validateName(prompt)) return
|
||||||
|
@ -188,10 +187,10 @@ export default class TableEditor extends WithTemplate {
|
||||||
<button class="flat" type="button" data-ref="filters">
|
<button class="flat" type="button" data-ref="filters">
|
||||||
<i class="icon icon-16 icon-filters"></i>${translate('Filter data')}
|
<i class="icon icon-16 icon-filters"></i>${translate('Filter data')}
|
||||||
</button>`)
|
</button>`)
|
||||||
filterButton.addEventListener('click', () => this._umap.browser.open('filters'))
|
filterButton.addEventListener('click', () => this.map.browser.open('filters'))
|
||||||
actions.push(filterButton)
|
actions.push(filterButton)
|
||||||
|
|
||||||
this._umap.fullPanel.open({
|
this.map.fullPanel.open({
|
||||||
content: this.table,
|
content: this.table,
|
||||||
className: 'umap-table-editor',
|
className: 'umap-table-editor',
|
||||||
actions: actions,
|
actions: actions,
|
||||||
|
@ -305,7 +304,7 @@ export default class TableEditor extends WithTemplate {
|
||||||
deleteRows() {
|
deleteRows() {
|
||||||
const selectedRows = this.getSelectedRows()
|
const selectedRows = this.getSelectedRows()
|
||||||
if (!selectedRows.length) return
|
if (!selectedRows.length) return
|
||||||
this._umap.dialog
|
this.map.dialog
|
||||||
.confirm(
|
.confirm(
|
||||||
translate('Found {count} rows. Are you sure you want to delete all?', {
|
translate('Found {count} rows. Are you sure you want to delete all?', {
|
||||||
count: selectedRows.length,
|
count: selectedRows.length,
|
||||||
|
@ -321,9 +320,9 @@ export default class TableEditor extends WithTemplate {
|
||||||
this.datalayer.show()
|
this.datalayer.show()
|
||||||
this.datalayer.dataChanged()
|
this.datalayer.dataChanged()
|
||||||
this.renderBody()
|
this.renderBody()
|
||||||
if (this._umap.browser.isOpen()) {
|
if (this.map.browser.isOpen()) {
|
||||||
this._umap.browser.resetFilters()
|
this.map.browser.resetFilters()
|
||||||
this._umap.browser.open('filters')
|
this.map.browser.open('filters')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,9 @@ import { DomEvent, DomUtil } from '../../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from '../i18n.js'
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
export class Panel {
|
export class Panel {
|
||||||
constructor(umap, leafletMap) {
|
constructor(map) {
|
||||||
this.parent = leafletMap._controlContainer
|
this.parent = map._controlContainer
|
||||||
this._umap = umap
|
this.map = map
|
||||||
this._leafletMap = leafletMap
|
|
||||||
this.container = DomUtil.create('div', '', this.parent)
|
this.container = DomUtil.create('div', '', this.parent)
|
||||||
// This will be set once according to the panel configurated at load
|
// This will be set once according to the panel configurated at load
|
||||||
// or by using panels as popups
|
// or by using panels as popups
|
||||||
|
@ -81,26 +80,26 @@ export class Panel {
|
||||||
onClose() {
|
onClose() {
|
||||||
if (DomUtil.hasClass(this.container, 'on')) {
|
if (DomUtil.hasClass(this.container, 'on')) {
|
||||||
DomUtil.removeClass(this.container, 'on')
|
DomUtil.removeClass(this.container, 'on')
|
||||||
this._leafletMap.invalidateSize({ pan: false })
|
this.map.invalidateSize({ pan: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EditPanel extends Panel {
|
export class EditPanel extends Panel {
|
||||||
constructor(umap, leafletMap) {
|
constructor(map) {
|
||||||
super(umap, leafletMap)
|
super(map)
|
||||||
this.className = 'right dark'
|
this.className = 'right dark'
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
super.onClose()
|
super.onClose()
|
||||||
this._umap.editedFeature = null
|
this.map.editedFeature = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FullPanel extends Panel {
|
export class FullPanel extends Panel {
|
||||||
constructor(umap, leafletMap) {
|
constructor(map) {
|
||||||
super(umap, leafletMap)
|
super(map)
|
||||||
this.className = 'full dark'
|
this.className = 'full dark'
|
||||||
this.mode = 'expanded'
|
this.mode = 'expanded'
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,16 +25,6 @@ export function checkId(string) {
|
||||||
return /^[A-Za-z0-9]{5}$/.test(string)
|
return /^[A-Za-z0-9]{5}$/.test(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _getPropertyName(field) {
|
|
||||||
const filtered_field = ['options.', 'properties.'].reduce(
|
|
||||||
(acc, prefix) => acc.replace(prefix, ''),
|
|
||||||
field
|
|
||||||
)
|
|
||||||
return filtered_field.split('.')[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the impacts for a given list of fields.
|
* Compute the impacts for a given list of fields.
|
||||||
*
|
*
|
||||||
|
@ -51,7 +41,7 @@ export function getImpactsFromSchema(fields, schema) {
|
||||||
// remove the option prefix for fields
|
// remove the option prefix for fields
|
||||||
// And only keep the first part in case of a subfield
|
// And only keep the first part in case of a subfield
|
||||||
// (e.g "options.limitBounds.foobar" will just return "limitBounds")
|
// (e.g "options.limitBounds.foobar" will just return "limitBounds")
|
||||||
return _getPropertyName(field)
|
return field.replace('options.', '').split('.')[0]
|
||||||
})
|
})
|
||||||
.reduce((acc, field) => {
|
.reduce((acc, field) => {
|
||||||
// retrieve the "impacts" field from the schema
|
// retrieve the "impacts" field from the schema
|
||||||
|
@ -76,7 +66,7 @@ export function getImpactsFromSchema(fields, schema) {
|
||||||
export function fieldInSchema(field, schema) {
|
export function fieldInSchema(field, schema) {
|
||||||
const current_schema = schema || U.SCHEMA
|
const current_schema = schema || U.SCHEMA
|
||||||
if (typeof field !== 'string') return false
|
if (typeof field !== 'string') return false
|
||||||
const field_name = _getPropertyName(field)
|
const field_name = field.replace('options.', '').split('.')[0]
|
||||||
return current_schema[field_name] !== undefined
|
return current_schema[field_name] !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ U.BaseAction = L.ToolbarAction.extend({
|
||||||
initialize: function (map) {
|
initialize: function (map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
if (this.options.label) {
|
if (this.options.label) {
|
||||||
this.options.tooltip = this.map._umap.help.displayLabel(
|
this.options.tooltip = this.map.help.displayLabel(
|
||||||
this.options.label,
|
this.options.label,
|
||||||
(withKbdTag = false)
|
(withKbdTag = false)
|
||||||
)
|
)
|
||||||
|
@ -12,8 +12,8 @@ U.BaseAction = L.ToolbarAction.extend({
|
||||||
tooltip: this.options.tooltip,
|
tooltip: this.options.tooltip,
|
||||||
}
|
}
|
||||||
L.ToolbarAction.prototype.initialize.call(this)
|
L.ToolbarAction.prototype.initialize.call(this)
|
||||||
if (this.options.helpMenu && !U.Help.MENU_ACTIONS[this.options.className])
|
if (this.options.helpMenu && !this.map.helpMenuActions[this.options.className])
|
||||||
U.Help.MENU_ACTIONS[this.options.className] = this
|
this.map.helpMenuActions[this.options.className] = this
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ U.ImportAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.importer.open()
|
this.map.importer.open()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ U.EditLayersAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.editDatalayers()
|
this.map.editDatalayers()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ U.EditCaptionAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.editCaption()
|
this.map.editCaption()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ U.EditPropertiesAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.edit()
|
this.map.edit()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ U.UpdateExtentAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.setCenterAndZoom()
|
this.map.setCenterAndZoom()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ U.UpdatePermsAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map._umap.permissions.edit()
|
this.map.permissions.edit()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ U.DrawMarkerAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map.editTools.startMarker()
|
this.map.startMarker()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ U.DrawPolylineAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map.editTools.startPolyline()
|
this.map.startPolyline()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ U.DrawPolygonAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
this.map.editTools.startPolygon()
|
this.map.startPolygon()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -142,8 +142,7 @@ U.AddPolylineShapeAction = U.BaseAction.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
addHooks: function () {
|
addHooks: function () {
|
||||||
// FIXME: smells bad
|
this.map.editedFeature.ui.editor.newShape()
|
||||||
this.map._umap.editedFeature.ui.editor.newShape()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -306,24 +305,18 @@ U.DrawToolbar = L.Toolbar.Control.extend({
|
||||||
|
|
||||||
appendToContainer: function (container) {
|
appendToContainer: function (container) {
|
||||||
this.options.actions = []
|
this.options.actions = []
|
||||||
if (this.map._umap.properties.enableMarkerDraw) {
|
if (this.map.options.enableMarkerDraw) {
|
||||||
this.options.actions.push(U.DrawMarkerAction)
|
this.options.actions.push(U.DrawMarkerAction)
|
||||||
}
|
}
|
||||||
if (this.map._umap.properties.enablePolylineDraw) {
|
if (this.map.options.enablePolylineDraw) {
|
||||||
this.options.actions.push(U.DrawPolylineAction)
|
this.options.actions.push(U.DrawPolylineAction)
|
||||||
if (
|
if (this.map.editedFeature && this.map.editedFeature instanceof U.LineString) {
|
||||||
this.map._umap.editedFeature &&
|
|
||||||
this.map._umap.editedFeature instanceof U.LineString
|
|
||||||
) {
|
|
||||||
this.options.actions.push(U.AddPolylineShapeAction)
|
this.options.actions.push(U.AddPolylineShapeAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.map._umap.properties.enablePolygonDraw) {
|
if (this.map.options.enablePolygonDraw) {
|
||||||
this.options.actions.push(U.DrawPolygonAction)
|
this.options.actions.push(U.DrawPolygonAction)
|
||||||
if (
|
if (this.map.editedFeature && this.map.editedFeature instanceof U.Polygon) {
|
||||||
this.map._umap.editedFeature &&
|
|
||||||
this.map._umap.editedFeature instanceof U.Polygon
|
|
||||||
) {
|
|
||||||
this.options.actions.push(U.AddPolygonShapeAction)
|
this.options.actions.push(U.AddPolygonShapeAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,14 +360,14 @@ U.DropControl = L.Class.extend({
|
||||||
L.DomEvent.stop(e)
|
L.DomEvent.stop(e)
|
||||||
},
|
},
|
||||||
|
|
||||||
drop: function (event) {
|
drop: function (e) {
|
||||||
this.map.scrollWheelZoom.enable()
|
this.map.scrollWheelZoom.enable()
|
||||||
this.dropzone.classList.remove('umap-dragover')
|
this.dropzone.classList.remove('umap-dragover')
|
||||||
L.DomEvent.stop(e)
|
L.DomEvent.stop(e)
|
||||||
for (const file of event.dataTransfer.files) {
|
for (let i = 0, file; (file = e.dataTransfer.files[i]); i++) {
|
||||||
this.map._umap.processFileToImport(file)
|
this.map.processFileToImport(file)
|
||||||
}
|
}
|
||||||
this.map._umap.onceDataLoaded(this.map._umap.fitDataBounds)
|
this.map.onceDataLoaded(this.map.fitDataBounds)
|
||||||
},
|
},
|
||||||
|
|
||||||
dragleave: function () {
|
dragleave: function () {
|
||||||
|
@ -394,15 +387,15 @@ U.EditControl = L.Control.extend({
|
||||||
'',
|
'',
|
||||||
container,
|
container,
|
||||||
L._('Edit'),
|
L._('Edit'),
|
||||||
map._umap.enableEdit,
|
map.enableEdit,
|
||||||
map._umap
|
map
|
||||||
)
|
)
|
||||||
L.DomEvent.on(
|
L.DomEvent.on(
|
||||||
enableEditing,
|
enableEditing,
|
||||||
'mouseover',
|
'mouseover',
|
||||||
() => {
|
() => {
|
||||||
map._umap.tooltip.open({
|
map.tooltip.open({
|
||||||
content: map._umap.help.displayLabel('TOGGLE_EDIT'),
|
content: map.help.displayLabel('TOGGLE_EDIT'),
|
||||||
anchor: enableEditing,
|
anchor: enableEditing,
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
delay: 750,
|
delay: 750,
|
||||||
|
@ -483,8 +476,8 @@ U.PermanentCreditsControl = L.Control.extend({
|
||||||
})
|
})
|
||||||
|
|
||||||
L.Control.Button = L.Control.extend({
|
L.Control.Button = L.Control.extend({
|
||||||
initialize: function (umap, options) {
|
initialize: function (map, options) {
|
||||||
this._umap = umap
|
this.map = map
|
||||||
L.Control.prototype.initialize.call(this, options)
|
L.Control.prototype.initialize.call(this, options)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -517,11 +510,11 @@ U.DataLayersControl = L.Control.Button.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
afterAdd: function (container) {
|
afterAdd: function (container) {
|
||||||
U.Utils.toggleBadge(container, this._umap.browser?.hasFilters())
|
U.Utils.toggleBadge(container, this.map.browser.hasFilters())
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
this._umap.openBrowser()
|
this.map.openBrowser()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -533,7 +526,7 @@ U.CaptionControl = L.Control.Button.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
this._umap.openCaption()
|
this.map.openCaption()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -544,12 +537,12 @@ U.StarControl = L.Control.Button.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
getClassName: function () {
|
getClassName: function () {
|
||||||
const status = this._umap.properties.starred ? ' starred' : ''
|
const status = this.map.options.starred ? ' starred' : ''
|
||||||
return `leaflet-control-star umap-control${status}`
|
return `leaflet-control-star umap-control${status}`
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
this._umap.star()
|
this.map.star()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -561,10 +554,248 @@ L.Control.Embed = L.Control.Button.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick: function () {
|
onClick: function () {
|
||||||
this._umap.share.open()
|
this.map.share.open()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ControlsMixin = {
|
||||||
|
HIDDABLE_CONTROLS: [
|
||||||
|
'zoom',
|
||||||
|
'search',
|
||||||
|
'fullscreen',
|
||||||
|
'embed',
|
||||||
|
'datalayers',
|
||||||
|
'caption',
|
||||||
|
'locate',
|
||||||
|
'measure',
|
||||||
|
'editinosm',
|
||||||
|
'star',
|
||||||
|
'tilelayers',
|
||||||
|
],
|
||||||
|
|
||||||
|
renderEditToolbar: function () {
|
||||||
|
const className = 'umap-main-edit-toolbox'
|
||||||
|
const container =
|
||||||
|
document.querySelector(`.${className}`) ||
|
||||||
|
L.DomUtil.create(
|
||||||
|
'div',
|
||||||
|
`${className} with-transition dark`,
|
||||||
|
this._controlContainer
|
||||||
|
)
|
||||||
|
container.innerHTML = ''
|
||||||
|
const leftContainer = L.DomUtil.create('div', 'umap-left-edit-toolbox', container)
|
||||||
|
const rightContainer = L.DomUtil.create('div', 'umap-right-edit-toolbox', container)
|
||||||
|
const logo = L.DomUtil.create('div', 'logo', leftContainer)
|
||||||
|
L.DomUtil.createLink('', logo, 'uMap', '/', null, L._('Go to the homepage'))
|
||||||
|
const nameButton = L.DomUtil.createButton('map-name', leftContainer, '')
|
||||||
|
L.DomEvent.on(
|
||||||
|
nameButton,
|
||||||
|
'mouseover',
|
||||||
|
function () {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: L._('Edit the title of the map'),
|
||||||
|
anchor: nameButton,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
const shareStatusButton = L.DomUtil.createButton(
|
||||||
|
'share-status',
|
||||||
|
leftContainer,
|
||||||
|
'',
|
||||||
|
this.permissions.edit,
|
||||||
|
this.permissions
|
||||||
|
)
|
||||||
|
L.DomEvent.on(
|
||||||
|
shareStatusButton,
|
||||||
|
'mouseover',
|
||||||
|
function () {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: L._('Update who can see and edit the map'),
|
||||||
|
anchor: shareStatusButton,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
if (this.options.editMode === 'advanced') {
|
||||||
|
L.DomEvent.on(nameButton, 'click', this.editCaption, this)
|
||||||
|
L.DomEvent.on(shareStatusButton, 'click', this.permissions.edit, this.permissions)
|
||||||
|
}
|
||||||
|
if (this.options.user?.id) {
|
||||||
|
const button = U.Utils.loadTemplate(`
|
||||||
|
<button class="umap-user flat" type="button">
|
||||||
|
<i class="icon icon-16 icon-profile"></i>
|
||||||
|
<span>${this.options.user.name}</span>
|
||||||
|
</button>
|
||||||
|
`)
|
||||||
|
rightContainer.appendChild(button)
|
||||||
|
const menu = new U.ContextMenu({ className: 'dark', fixed: true })
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
label: L._('New map'),
|
||||||
|
action: this.urls.get('map_new'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: L._('My maps'),
|
||||||
|
action: this.urls.get('user_dashboard'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: L._('My teams'),
|
||||||
|
action: this.urls.get('user_teams'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
if (this.urls.has('user_profile')) {
|
||||||
|
actions.push({
|
||||||
|
label: L._('My profile'),
|
||||||
|
action: this.urls.get('user_profile'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
menu.openBelow(button, actions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectedPeers = this.sync.getNumberOfConnectedPeers()
|
||||||
|
if (connectedPeers !== 0) {
|
||||||
|
const connectedPeersCount = L.DomUtil.createButton(
|
||||||
|
'leaflet-control-connected-peers',
|
||||||
|
rightContainer,
|
||||||
|
''
|
||||||
|
)
|
||||||
|
L.DomEvent.on(connectedPeersCount, 'mouseover', () => {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: L._('{connectedPeers} peer(s) currently connected to this map', {
|
||||||
|
connectedPeers: connectedPeers,
|
||||||
|
}),
|
||||||
|
anchor: connectedPeersCount,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateConnectedPeersCount = () => {
|
||||||
|
connectedPeersCount.innerHTML =
|
||||||
|
'<span>' + this.sync.getNumberOfConnectedPeers() + '</span>'
|
||||||
|
}
|
||||||
|
updateConnectedPeersCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.help.getStartedLink(rightContainer)
|
||||||
|
const controlEditCancel = L.DomUtil.createButton(
|
||||||
|
'leaflet-control-edit-cancel',
|
||||||
|
rightContainer,
|
||||||
|
L.DomUtil.add('span', '', null, L._('Cancel edits')),
|
||||||
|
this.askForReset,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
L.DomEvent.on(
|
||||||
|
controlEditCancel,
|
||||||
|
'mouseover',
|
||||||
|
function () {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: this.help.displayLabel('CANCEL'),
|
||||||
|
anchor: controlEditCancel,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
const controlEditDisable = L.DomUtil.createButton(
|
||||||
|
'leaflet-control-edit-disable',
|
||||||
|
rightContainer,
|
||||||
|
L.DomUtil.add('span', '', null, L._('View')),
|
||||||
|
this.disableEdit,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
L.DomEvent.on(
|
||||||
|
controlEditDisable,
|
||||||
|
'mouseover',
|
||||||
|
function () {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: this.help.displayLabel('PREVIEW'),
|
||||||
|
anchor: controlEditDisable,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
const controlEditSave = L.DomUtil.createButton(
|
||||||
|
'leaflet-control-edit-save button',
|
||||||
|
rightContainer,
|
||||||
|
L.DomUtil.add('span', '', null, L._('Save')),
|
||||||
|
this.saveAll,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
L.DomEvent.on(
|
||||||
|
controlEditSave,
|
||||||
|
'mouseover',
|
||||||
|
function () {
|
||||||
|
this.tooltip.open({
|
||||||
|
content: this.help.displayLabel('SAVE'),
|
||||||
|
anchor: controlEditSave,
|
||||||
|
position: 'bottom',
|
||||||
|
delay: 500,
|
||||||
|
duration: 5000,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
editDatalayers: function () {
|
||||||
|
if (!this.editEnabled) return
|
||||||
|
const container = L.DomUtil.create('div')
|
||||||
|
L.DomUtil.createTitle(container, L._('Manage layers'), 'icon-layers')
|
||||||
|
const ul = L.DomUtil.create('ul', '', container)
|
||||||
|
this.eachDataLayerReverse((datalayer) => {
|
||||||
|
const row = L.DomUtil.create('li', 'orderable', ul)
|
||||||
|
L.DomUtil.createIcon(row, 'icon-drag', L._('Drag to reorder'))
|
||||||
|
datalayer.renderToolbox(row)
|
||||||
|
const title = L.DomUtil.add('span', '', row, datalayer.options.name)
|
||||||
|
row.classList.toggle('off', !datalayer.isVisible())
|
||||||
|
title.textContent = datalayer.options.name
|
||||||
|
row.dataset.id = L.stamp(datalayer)
|
||||||
|
})
|
||||||
|
const onReorder = (src, dst, initialIndex, finalIndex) => {
|
||||||
|
const layer = this.datalayers[src.dataset.id]
|
||||||
|
const other = this.datalayers[dst.dataset.id]
|
||||||
|
const minIndex = Math.min(layer.getRank(), other.getRank())
|
||||||
|
const maxIndex = Math.max(layer.getRank(), other.getRank())
|
||||||
|
if (finalIndex === 0) layer.bringToTop()
|
||||||
|
else if (finalIndex > initialIndex) layer.insertBefore(other)
|
||||||
|
else layer.insertAfter(other)
|
||||||
|
this.eachDataLayerReverse((datalayer) => {
|
||||||
|
if (datalayer.getRank() >= minIndex && datalayer.getRank() <= maxIndex)
|
||||||
|
datalayer.isDirty = true
|
||||||
|
})
|
||||||
|
this.indexDatalayers()
|
||||||
|
}
|
||||||
|
const orderable = new U.Orderable(ul, onReorder)
|
||||||
|
|
||||||
|
const bar = L.DomUtil.create('div', 'button-bar', container)
|
||||||
|
L.DomUtil.createButton(
|
||||||
|
'show-on-edit block add-datalayer button',
|
||||||
|
bar,
|
||||||
|
L._('Add a layer'),
|
||||||
|
this.newDataLayer,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
|
this.editPanel.open({ content: container })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/* Used in view mode to define the current tilelayer */
|
/* Used in view mode to define the current tilelayer */
|
||||||
U.TileLayerControl = L.Control.IconLayers.extend({
|
U.TileLayerControl = L.Control.IconLayers.extend({
|
||||||
initialize: function (map, options) {
|
initialize: function (map, options) {
|
||||||
|
@ -588,7 +819,7 @@ U.TileLayerControl = L.Control.IconLayers.extend({
|
||||||
// Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
|
// Fixme when https://github.com/Leaflet/Leaflet/pull/9201 is released
|
||||||
const icon = U.Utils.template(
|
const icon = U.Utils.template(
|
||||||
layer.options.url_template,
|
layer.options.url_template,
|
||||||
this.map.options.demoTileInfos
|
this.map.demoTileInfos
|
||||||
)
|
)
|
||||||
layers.push({
|
layers.push({
|
||||||
title: layer.options.name,
|
title: layer.options.name,
|
||||||
|
@ -654,7 +885,7 @@ U.TileLayerChooser = L.Control.extend({
|
||||||
L.DomUtil.createTitle(container, L._('Change tilelayers'), 'icon-tilelayer')
|
L.DomUtil.createTitle(container, L._('Change tilelayers'), 'icon-tilelayer')
|
||||||
this._tilelayers_container = L.DomUtil.create('ul', '', container)
|
this._tilelayers_container = L.DomUtil.create('ul', '', container)
|
||||||
this.buildList(options)
|
this.buildList(options)
|
||||||
const panel = options.edit ? this.map._umap.editPanel : this.map._umap.panel
|
const panel = options.edit ? this.map.editPanel : this.map.panel
|
||||||
panel.open({ content: container })
|
panel.open({ content: container })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -674,10 +905,7 @@ U.TileLayerChooser = L.Control.extend({
|
||||||
const el = L.DomUtil.create('li', selectedClass, this._tilelayers_container)
|
const el = L.DomUtil.create('li', selectedClass, this._tilelayers_container)
|
||||||
const img = L.DomUtil.create('img', '', el)
|
const img = L.DomUtil.create('img', '', el)
|
||||||
const name = L.DomUtil.create('div', '', el)
|
const name = L.DomUtil.create('div', '', el)
|
||||||
img.src = U.Utils.template(
|
img.src = U.Utils.template(tilelayer.options.url_template, this.map.demoTileInfos)
|
||||||
tilelayer.options.url_template,
|
|
||||||
this.map.options.demoTileInfos
|
|
||||||
)
|
|
||||||
img.loading = 'lazy'
|
img.loading = 'lazy'
|
||||||
name.textContent = tilelayer.options.name
|
name.textContent = tilelayer.options.name
|
||||||
L.DomEvent.on(
|
L.DomEvent.on(
|
||||||
|
@ -707,8 +935,8 @@ U.AttributionControl = L.Control.Attribution.extend({
|
||||||
this._container.innerHTML = ''
|
this._container.innerHTML = ''
|
||||||
const container = L.DomUtil.create('div', 'attribution-container', this._container)
|
const container = L.DomUtil.create('div', 'attribution-container', this._container)
|
||||||
container.innerHTML = credits
|
container.innerHTML = credits
|
||||||
const shortCredit = this._map._umap.getProperty('shortCredit')
|
const shortCredit = this._map.getOption('shortCredit')
|
||||||
const captionMenus = this._map._umap.getProperty('captionMenus')
|
const captionMenus = this._map.getOption('captionMenus')
|
||||||
if (shortCredit) {
|
if (shortCredit) {
|
||||||
L.DomUtil.element({
|
L.DomUtil.element({
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
|
@ -719,7 +947,7 @@ U.AttributionControl = L.Control.Attribution.extend({
|
||||||
if (captionMenus) {
|
if (captionMenus) {
|
||||||
const link = L.DomUtil.add('a', '', container, ` — ${L._('Open caption')}`)
|
const link = L.DomUtil.add('a', '', container, ` — ${L._('Open caption')}`)
|
||||||
L.DomEvent.on(link, 'click', L.DomEvent.stop)
|
L.DomEvent.on(link, 'click', L.DomEvent.stop)
|
||||||
.on(link, 'click', () => this._map._umap.openCaption())
|
.on(link, 'click', this._map.openCaption, this._map)
|
||||||
.on(link, 'dblclick', L.DomEvent.stop)
|
.on(link, 'dblclick', L.DomEvent.stop)
|
||||||
}
|
}
|
||||||
if (window.top === window.self && captionMenus) {
|
if (window.top === window.self && captionMenus) {
|
||||||
|
@ -911,7 +1139,7 @@ U.SearchControl = L.Control.extend({
|
||||||
this.map.fire('dataload', { id: id })
|
this.map.fire('dataload', { id: id })
|
||||||
})
|
})
|
||||||
this.search.resultsContainer = resultsContainer
|
this.search.resultsContainer = resultsContainer
|
||||||
this.map._umap.panel.open({ content: container }).then(input.focus())
|
this.map.panel.open({ content: container }).then(input.focus())
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -951,9 +1179,8 @@ L.Control.Loading.include({
|
||||||
})
|
})
|
||||||
|
|
||||||
U.Editable = L.Editable.extend({
|
U.Editable = L.Editable.extend({
|
||||||
initialize: function (umap, options) {
|
initialize: function (map, options) {
|
||||||
this._umap = umap
|
L.Editable.prototype.initialize.call(this, map, options)
|
||||||
L.Editable.prototype.initialize.call(this, umap._leafletMap, options)
|
|
||||||
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
this.on('editable:drawing:click editable:drawing:move', this.drawingTooltip)
|
||||||
// Layer for items added by users
|
// Layer for items added by users
|
||||||
this.on('editable:drawing:cancel', (event) => {
|
this.on('editable:drawing:cancel', (event) => {
|
||||||
|
@ -961,7 +1188,7 @@ U.Editable = L.Editable.extend({
|
||||||
})
|
})
|
||||||
this.on('editable:drawing:commit', function (event) {
|
this.on('editable:drawing:commit', function (event) {
|
||||||
event.layer.feature.isDirty = true
|
event.layer.feature.isDirty = true
|
||||||
if (this._umap.editedFeature !== event.layer) event.layer.feature.edit(event)
|
if (this.map.editedFeature !== event.layer) event.layer.feature.edit(event)
|
||||||
})
|
})
|
||||||
this.on('editable:editing', (event) => {
|
this.on('editable:editing', (event) => {
|
||||||
const feature = event.layer.feature
|
const feature = event.layer.feature
|
||||||
|
@ -983,24 +1210,24 @@ U.Editable = L.Editable.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
createPolyline: function (latlngs) {
|
createPolyline: function (latlngs) {
|
||||||
const datalayer = this._umap.defaultEditDataLayer()
|
const datalayer = this.map.defaultEditDataLayer()
|
||||||
const point = new U.LineString(this._umap, datalayer, {
|
const point = new U.LineString(datalayer, {
|
||||||
geometry: { type: 'LineString', coordinates: [] },
|
geometry: { type: 'LineString', coordinates: [] },
|
||||||
})
|
})
|
||||||
return point.ui
|
return point.ui
|
||||||
},
|
},
|
||||||
|
|
||||||
createPolygon: function (latlngs) {
|
createPolygon: function (latlngs) {
|
||||||
const datalayer = this._umap.defaultEditDataLayer()
|
const datalayer = this.map.defaultEditDataLayer()
|
||||||
const point = new U.Polygon(this._umap, datalayer, {
|
const point = new U.Polygon(datalayer, {
|
||||||
geometry: { type: 'Polygon', coordinates: [] },
|
geometry: { type: 'Polygon', coordinates: [] },
|
||||||
})
|
})
|
||||||
return point.ui
|
return point.ui
|
||||||
},
|
},
|
||||||
|
|
||||||
createMarker: function (latlng) {
|
createMarker: function (latlng) {
|
||||||
const datalayer = this._umap.defaultEditDataLayer()
|
const datalayer = this.map.defaultEditDataLayer()
|
||||||
const point = new U.Point(this._umap, datalayer, {
|
const point = new U.Point(datalayer, {
|
||||||
geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] },
|
geometry: { type: 'Point', coordinates: [latlng.lng, latlng.lat] },
|
||||||
})
|
})
|
||||||
return point.ui
|
return point.ui
|
||||||
|
@ -1008,15 +1235,15 @@ U.Editable = L.Editable.extend({
|
||||||
|
|
||||||
_getDefaultProperties: function () {
|
_getDefaultProperties: function () {
|
||||||
const result = {}
|
const result = {}
|
||||||
if (this._umap.properties.featuresHaveOwner?.user) {
|
if (this.map.options.featuresHaveOwner?.user) {
|
||||||
result.geojson = { properties: { owner: this._umap.properties.user.id } }
|
result.geojson = { properties: { owner: this.map.options.user.id } }
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
|
|
||||||
connectCreatedToMap: function (layer) {
|
connectCreatedToMap: function (layer) {
|
||||||
// Overrided from Leaflet.Editable
|
// Overrided from Leaflet.Editable
|
||||||
const datalayer = this._umap.defaultEditDataLayer()
|
const datalayer = this.map.defaultEditDataLayer()
|
||||||
datalayer.addFeature(layer.feature)
|
datalayer.addFeature(layer.feature)
|
||||||
layer.isDirty = true
|
layer.isDirty = true
|
||||||
return layer
|
return layer
|
||||||
|
@ -1024,7 +1251,7 @@ U.Editable = L.Editable.extend({
|
||||||
|
|
||||||
drawingTooltip: function (e) {
|
drawingTooltip: function (e) {
|
||||||
if (e.layer instanceof L.Marker && e.type === 'editable:drawing:start') {
|
if (e.layer instanceof L.Marker && e.type === 'editable:drawing:start') {
|
||||||
this._umap.tooltip.open({ content: L._('Click to add a marker') })
|
this.map.tooltip.open({ content: L._('Click to add a marker') })
|
||||||
}
|
}
|
||||||
if (!(e.layer instanceof L.Polyline)) {
|
if (!(e.layer instanceof L.Polyline)) {
|
||||||
// only continue with Polylines and Polygons
|
// only continue with Polylines and Polygons
|
||||||
|
@ -1071,12 +1298,12 @@ U.Editable = L.Editable.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (content) {
|
if (content) {
|
||||||
this._umap.tooltip.open({ content: content })
|
this.map.tooltip.open({ content: content })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
closeTooltip: function () {
|
closeTooltip: function () {
|
||||||
this._umap.closeTooltip()
|
this.map.ui.closeTooltip()
|
||||||
},
|
},
|
||||||
|
|
||||||
onVertexRawClick: (e) => {
|
onVertexRawClick: (e) => {
|
||||||
|
@ -1087,7 +1314,7 @@ U.Editable = L.Editable.extend({
|
||||||
|
|
||||||
onEscape: function () {
|
onEscape: function () {
|
||||||
this.once('editable:drawing:end', (event) => {
|
this.once('editable:drawing:end', (event) => {
|
||||||
this._umap.tooltip.close()
|
this.map.tooltip.close()
|
||||||
// Leaflet.Editable will delete the drawn shape if invalid
|
// Leaflet.Editable will delete the drawn shape if invalid
|
||||||
// (eg. line has only one drawn point)
|
// (eg. line has only one drawn point)
|
||||||
// So let's check if the layer has no more shape
|
// So let's check if the layer has no more shape
|
||||||
|
|
|
@ -25,6 +25,48 @@ L.Util.copyToClipboard = (textToCopy) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
L.Util.queryString = (name, fallback) => {
|
||||||
|
const decode = (s) => decodeURIComponent(s.replace(/\+/g, ' '))
|
||||||
|
const qs = window.location.search.slice(1).split('&')
|
||||||
|
const qa = {}
|
||||||
|
for (const i in qs) {
|
||||||
|
const key = qs[i].split('=')
|
||||||
|
if (!key) continue
|
||||||
|
qa[decode(key[0])] = key[1] ? decode(key[1]) : 1
|
||||||
|
}
|
||||||
|
return qa[name] || fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.booleanFromQueryString = (name) => {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
return value === '1' || value === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setFromQueryString = (options, name) => {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') options[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setBooleanFromQueryString = (options, name) => {
|
||||||
|
const value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') options[name] = value === '1' || value === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setNumberFromQueryString = (options, name) => {
|
||||||
|
const value = +L.Util.queryString(name)
|
||||||
|
if (!Number.isNaN(value)) options[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Util.setNullableBooleanFromQueryString = (options, name) => {
|
||||||
|
let value = L.Util.queryString(name)
|
||||||
|
if (typeof value !== 'undefined') {
|
||||||
|
if (value === 'null') value = null
|
||||||
|
else if (value === '0' || value === 'false') value = false
|
||||||
|
else value = true
|
||||||
|
options[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
L.DomUtil.add = (tagName, className, container, content) => {
|
L.DomUtil.add = (tagName, className, container, content) => {
|
||||||
const el = L.DomUtil.create(tagName, className, container)
|
const el = L.DomUtil.create(tagName, className, container)
|
||||||
if (content) {
|
if (content) {
|
||||||
|
|
|
@ -221,16 +221,21 @@ L.FormBuilder.Element.include({
|
||||||
this.label = L.DomUtil.create('label', '', this.getLabelParent())
|
this.label = L.DomUtil.create('label', '', this.getLabelParent())
|
||||||
this.label.textContent = this.label.title = this.options.label
|
this.label.textContent = this.label.title = this.options.label
|
||||||
if (this.options.helpEntries) {
|
if (this.options.helpEntries) {
|
||||||
this.builder._umap.help.button(this.label, this.options.helpEntries)
|
this.builder.map.help.button(this.label, this.options.helpEntries)
|
||||||
} else if (this.options.helpTooltip) {
|
} else if (this.options.helpTooltip) {
|
||||||
const info = L.DomUtil.create('i', 'info', this.label)
|
const info = L.DomUtil.create('i', 'info', this.label)
|
||||||
L.DomEvent.on(info, 'mouseover', () => {
|
L.DomEvent.on(
|
||||||
this.builder._umap.tooltip.open({
|
info,
|
||||||
anchor: info,
|
'mouseover',
|
||||||
content: this.options.helpTooltip,
|
function () {
|
||||||
position: 'top',
|
this.builder.map.tooltip.open({
|
||||||
})
|
anchor: info,
|
||||||
})
|
content: this.options.helpTooltip,
|
||||||
|
position: 'top',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -354,7 +359,7 @@ L.FormBuilder.SlideshowDelay = L.FormBuilder.IntSelect.extend({
|
||||||
L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({
|
L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({
|
||||||
getOptions: function () {
|
getOptions: function () {
|
||||||
const options = []
|
const options = []
|
||||||
this.builder._umap.eachDataLayerReverse((datalayer) => {
|
this.builder.map.eachDataLayerReverse((datalayer) => {
|
||||||
if (
|
if (
|
||||||
datalayer.isLoaded() &&
|
datalayer.isLoaded() &&
|
||||||
!datalayer.isDataReadOnly() &&
|
!datalayer.isDataReadOnly() &&
|
||||||
|
@ -371,11 +376,11 @@ L.FormBuilder.DataLayerSwitcher = L.FormBuilder.Select.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
toJS: function () {
|
toJS: function () {
|
||||||
return this.builder._umap.datalayers[this.value()]
|
return this.builder.map.datalayers[this.value()]
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function () {
|
set: function () {
|
||||||
this.builder._umap.lastUsedDataLayer = this.toJS()
|
this.builder.map.lastUsedDataLayer = this.toJS()
|
||||||
this.obj.changeDataLayer(this.toJS())
|
this.obj.changeDataLayer(this.toJS())
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -395,7 +400,7 @@ L.FormBuilder.DataFormat = L.FormBuilder.Select.extend({
|
||||||
L.FormBuilder.LicenceChooser = L.FormBuilder.Select.extend({
|
L.FormBuilder.LicenceChooser = L.FormBuilder.Select.extend({
|
||||||
getOptions: function () {
|
getOptions: function () {
|
||||||
const licences = []
|
const licences = []
|
||||||
const licencesList = this.builder.obj.properties.licences
|
const licencesList = this.builder.obj.options.licences
|
||||||
let licence
|
let licence
|
||||||
for (const i in licencesList) {
|
for (const i in licencesList) {
|
||||||
licence = licencesList[i]
|
licence = licencesList[i]
|
||||||
|
@ -409,7 +414,7 @@ L.FormBuilder.LicenceChooser = L.FormBuilder.Select.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
toJS: function () {
|
toJS: function () {
|
||||||
return this.builder.obj.properties.licences[this.value()]
|
return this.builder.obj.options.licences[this.value()]
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -464,8 +469,8 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
onDefine: async function () {
|
onDefine: async function () {
|
||||||
this.buttons.innerHTML = ''
|
this.buttons.innerHTML = ''
|
||||||
this.footer.innerHTML = ''
|
this.footer.innerHTML = ''
|
||||||
const [{ pictogram_list }, response, error] = await this.builder._umap.server.get(
|
const [{ pictogram_list }, response, error] = await this.builder.map.server.get(
|
||||||
this.builder._umap.properties.urls.pictogram_list_json
|
this.builder.map.options.urls.pictogram_list_json
|
||||||
)
|
)
|
||||||
if (!error) this.pictogram_list = pictogram_list
|
if (!error) this.pictogram_list = pictogram_list
|
||||||
this.buildTabs()
|
this.buildTabs()
|
||||||
|
@ -611,7 +616,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
|
||||||
categories[category].push(props)
|
categories[category].push(props)
|
||||||
}
|
}
|
||||||
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
|
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
|
||||||
U.Utils.naturalSort(a, b, U.lang)
|
U.Utils.naturalSort(a, b, L.lang)
|
||||||
)
|
)
|
||||||
for (const [name, items] of sorted) {
|
for (const [name, items] of sorted) {
|
||||||
this.addCategory(items, name)
|
this.addCategory(items, name)
|
||||||
|
@ -1162,8 +1167,8 @@ U.FormBuilder = L.FormBuilder.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function (obj, fields, options = {}) {
|
initialize: function (obj, fields, options) {
|
||||||
this._umap = obj._umap || options.umap
|
this.map = obj.map || obj.getMap()
|
||||||
this.computeDefaultOptions()
|
this.computeDefaultOptions()
|
||||||
L.FormBuilder.prototype.initialize.call(this, obj, fields, options)
|
L.FormBuilder.prototype.initialize.call(this, obj, fields, options)
|
||||||
this.on('finish', this.finish)
|
this.on('finish', this.finish)
|
||||||
|
|
1893
umap/static/umap/js/umap.js
Normal file
1893
umap/static/umap/js/umap.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -63,11 +63,7 @@ L.FormBuilder = L.Evented.extend({
|
||||||
const path = field.split('.')
|
const path = field.split('.')
|
||||||
let value = this.obj
|
let value = this.obj
|
||||||
for (const sub of path) {
|
for (const sub of path) {
|
||||||
try {
|
value = value[sub]
|
||||||
value = value[sub]
|
|
||||||
} catch {
|
|
||||||
console.log(field)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,4 +41,5 @@
|
||||||
<script src="{% static 'umap/js/umap.core.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.core.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
||||||
<script type="module" src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
||||||
|
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
{% load umap_tags static %}
|
{% load umap_tags %}
|
||||||
|
|
||||||
{% include "umap/messages.html" %}
|
{% include "umap/messages.html" %}
|
||||||
<div id="map">
|
<div id="map">
|
||||||
</div>
|
</div>
|
||||||
<!-- djlint:off -->
|
<!-- djlint:off -->
|
||||||
<script defer type="module">
|
<script defer type="text/javascript">
|
||||||
import Umap from '{% static "umap/js/modules/umap.js" %}'
|
window.addEventListener('DOMContentLoaded', (event) => {
|
||||||
U.MAP = new Umap("map", {{ map_settings|notag|safe }})
|
U.MAP = new U.Map("map", {{ map_settings|notag|safe }})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<!-- djlint:on -->
|
<!-- djlint:on -->
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "umap/content.html" %}
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
{% load i18n static %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block head_title %}
|
{% block head_title %}
|
||||||
{{ SITE_NAME }} - {% trans "My Dashboard" %}
|
{{ SITE_NAME }} - {% trans "My Dashboard" %}
|
||||||
|
@ -45,22 +45,23 @@
|
||||||
{% endblock maincontent %}
|
{% endblock maincontent %}
|
||||||
{% block bottom_js %}
|
{% block bottom_js %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script type="module">
|
<script type="text/javascript">
|
||||||
import Umap from '{% static "umap/js/modules/umap.js" %}'
|
!(function () {
|
||||||
const CACHE = {}
|
const CACHE = {}
|
||||||
for (const mapOpener of document.querySelectorAll("button.map-opener")) {
|
for (const mapOpener of document.querySelectorAll("button.map-opener")) {
|
||||||
mapOpener.addEventListener('click', (event) => {
|
mapOpener.addEventListener('click', (event) => {
|
||||||
const button = event.target.closest('button')
|
const button = event.target.closest('button')
|
||||||
button.nextElementSibling.showModal()
|
button.nextElementSibling.showModal()
|
||||||
const mapId = button.dataset.mapId
|
const mapId = button.dataset.mapId
|
||||||
if (!document.querySelector(`#${mapId}_target`).children.length) {
|
if (!document.querySelector(`#${mapId}_target`).children.length) {
|
||||||
const previewSettings = JSON.parse(document.getElementById(mapId).textContent)
|
const previewSettings = JSON.parse(document.getElementById(mapId).textContent)
|
||||||
const map = new Umap(`${mapId}_target`, previewSettings)
|
const map = new U.Map(`${mapId}_target`, previewSettings)
|
||||||
CACHE[mapId] = map
|
CACHE[mapId] = map
|
||||||
} else {
|
} else {
|
||||||
CACHE[mapId].invalidateSize()
|
CACHE[mapId].invalidateSize()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
{% endblock bottom_js %}
|
{% endblock bottom_js %}
|
||||||
|
|
|
@ -93,10 +93,10 @@ def test_umap_import_from_textarea(live_server, tilelayer, page, settings):
|
||||||
page.locator('img[src="https://tile.openstreetmap.fr/hot/6/32/21.png"]')
|
page.locator('img[src="https://tile.openstreetmap.fr/hot/6/32/21.png"]')
|
||||||
).to_be_visible()
|
).to_be_visible()
|
||||||
# Should not have imported umap_id, while in the file options
|
# Should not have imported umap_id, while in the file options
|
||||||
assert not page.evaluate("U.MAP.properties.umap_id")
|
assert not page.evaluate("U.MAP.options.umap_id")
|
||||||
with page.expect_response(re.compile(r".*/datalayer/create/.*")):
|
with page.expect_response(re.compile(r".*/datalayer/create/.*")):
|
||||||
page.get_by_role("button", name="Save").click()
|
page.get_by_role("button", name="Save").click()
|
||||||
assert page.evaluate("U.MAP.properties.umap_id")
|
assert page.evaluate("U.MAP.options.umap_id")
|
||||||
|
|
||||||
|
|
||||||
def test_import_geojson_from_textarea(tilelayer, live_server, page):
|
def test_import_geojson_from_textarea(tilelayer, live_server, page):
|
||||||
|
|
|
@ -161,7 +161,7 @@ def test_websocket_connection_can_sync_polygons(
|
||||||
|
|
||||||
@pytest.mark.xdist_group(name="websockets")
|
@pytest.mark.xdist_group(name="websockets")
|
||||||
def test_websocket_connection_can_sync_map_properties(
|
def test_websocket_connection_can_sync_map_properties(
|
||||||
new_page, live_server, websocket_server, tilelayer
|
context, live_server, websocket_server, tilelayer
|
||||||
):
|
):
|
||||||
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
||||||
map.settings["properties"]["syncEnabled"] = True
|
map.settings["properties"]["syncEnabled"] = True
|
||||||
|
@ -169,9 +169,9 @@ def test_websocket_connection_can_sync_map_properties(
|
||||||
DataLayerFactory(map=map, data={})
|
DataLayerFactory(map=map, data={})
|
||||||
|
|
||||||
# Create two tabs
|
# Create two tabs
|
||||||
peerA = new_page()
|
peerA = context.new_page()
|
||||||
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
peerB = new_page()
|
peerB = context.new_page()
|
||||||
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
|
||||||
# Name change is synced
|
# Name change is synced
|
||||||
|
@ -193,7 +193,7 @@ def test_websocket_connection_can_sync_map_properties(
|
||||||
|
|
||||||
@pytest.mark.xdist_group(name="websockets")
|
@pytest.mark.xdist_group(name="websockets")
|
||||||
def test_websocket_connection_can_sync_datalayer_properties(
|
def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
new_page, live_server, websocket_server, tilelayer
|
context, live_server, websocket_server, tilelayer
|
||||||
):
|
):
|
||||||
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
|
||||||
map.settings["properties"]["syncEnabled"] = True
|
map.settings["properties"]["syncEnabled"] = True
|
||||||
|
@ -201,9 +201,9 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
DataLayerFactory(map=map, data={})
|
DataLayerFactory(map=map, data={})
|
||||||
|
|
||||||
# Create two tabs
|
# Create two tabs
|
||||||
peerA = new_page()
|
peerA = context.new_page()
|
||||||
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
peerB = new_page()
|
peerB = context.new_page()
|
||||||
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
||||||
|
|
||||||
# Layer addition, name and type are synced
|
# Layer addition, name and type are synced
|
||||||
|
@ -215,7 +215,7 @@ def test_websocket_connection_can_sync_datalayer_properties(
|
||||||
peerA.locator("body").press("Escape")
|
peerA.locator("body").press("Escape")
|
||||||
|
|
||||||
peerB.get_by_role("link", name="Manage layers").click()
|
peerB.get_by_role("link", name="Manage layers").click()
|
||||||
peerB.locator(".panel.right").get_by_role("button", name="Edit").first.click()
|
peerB.get_by_role("button", name="Edit").first.click()
|
||||||
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
|
||||||
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue