mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
343 lines
9.8 KiB
JavaScript
343 lines
9.8 KiB
JavaScript
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
|
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
|
import { translate } from './i18n.js'
|
|
import { SCHEMA } from './schema.js'
|
|
import Dialog from './ui/dialog.js'
|
|
import * as Utils from './utils.js'
|
|
|
|
const TEMPLATE = `
|
|
<div class="umap-upload">
|
|
<h3><i class="icon icon-16 icon-upload"></i><span>${translate('Import data')}</span></h3>
|
|
<fieldset class="formbox">
|
|
<legend class="counter">${translate('Choose data')}</legend>
|
|
<input type="file" multiple autofocus onchange />
|
|
<textarea onchange placeholder="${translate('Paste your data here')}"></textarea>
|
|
<input class="highlightable" type="url" placeholder="${translate('Provide an URL here')}" onchange />
|
|
<button class="flat importers" hidden data-ref="importersButton"><i class="icon icon-16 icon-magic"></i>${translate('Import helpers')}</button>
|
|
</fieldset>
|
|
<fieldset class="formbox">
|
|
<legend class="counter" data-help="importFormats">${translate(
|
|
'Choose the format'
|
|
)}</legend>
|
|
<select name="format" onchange></select>
|
|
</fieldset>
|
|
<fieldset id="destination" class="formbox">
|
|
<legend class="counter">${translate('Choose the layer')}</legend>
|
|
<select name="layer-id" onchange></select>
|
|
<label id="clear">
|
|
<input type="checkbox" name="clear" />
|
|
${translate('Replace layer content')}
|
|
</label>
|
|
<input type="text" name="layer-name" placeholder="${translate('Layer name')}" />
|
|
</fieldset>
|
|
<fieldset id="import-mode" class="formbox">
|
|
<legend class="counter" data-help="importMode">${translate('Choose import mode')}</legend>
|
|
<label>
|
|
<input type="radio" name="action" value="copy" checked onchange />
|
|
${translate('Copy into the layer')}
|
|
</label>
|
|
<label>
|
|
<input type="radio" name="action" value="link" onchange />
|
|
${translate('Link to the layer as remote data')}
|
|
</label>
|
|
</fieldset>
|
|
<input type="button" class="button primary" name="submit" value="${translate('Import data')}" disabled />
|
|
</div>
|
|
`
|
|
|
|
const GRID_TEMPLATE = `
|
|
<div>
|
|
<h3><i class="icon icon-16 icon-magic"></i>${translate('Import helpers')}</h3>
|
|
<p>${translate('Import helpers will fill the URL field for you.')}</p>
|
|
<ul class="grid-container by4" data-ref="grid"></ul>
|
|
</div>
|
|
`
|
|
|
|
export default class Importer extends Utils.WithTemplate {
|
|
constructor(umap) {
|
|
super()
|
|
this._umap = umap
|
|
this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
|
|
this.IMPORTERS = []
|
|
this.loadImporters()
|
|
this.dialog = new Dialog({ className: 'importers dark' })
|
|
}
|
|
|
|
loadImporters() {
|
|
for (const [name, config] of Object.entries(
|
|
this._umap.properties.importers || {}
|
|
)) {
|
|
const register = (mod) => {
|
|
this.IMPORTERS.push(new mod.Importer(this._umap, config))
|
|
}
|
|
// We need to have explicit static paths for Django's collectstatic with hashes.
|
|
switch (name) {
|
|
case 'geodatamine':
|
|
import('./importers/geodatamine.js').then(register)
|
|
break
|
|
case 'communesfr':
|
|
import('./importers/communesfr.js').then(register)
|
|
break
|
|
case 'cadastrefr':
|
|
import('./importers/cadastrefr.js').then(register)
|
|
break
|
|
case 'overpass':
|
|
import('./importers/overpass.js').then(register)
|
|
break
|
|
case 'datasets':
|
|
import('./importers/datasets.js').then(register)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
qs(query) {
|
|
return this.container.querySelector(query)
|
|
}
|
|
|
|
get url() {
|
|
return this.qs('[type=url]').value
|
|
}
|
|
|
|
set url(value) {
|
|
this.qs('[type=url]').value = value
|
|
this.onChange()
|
|
}
|
|
|
|
get format() {
|
|
return this.qs('[name=format]').value
|
|
}
|
|
|
|
set format(value) {
|
|
this.qs('[name=format]').value = value
|
|
this.onChange()
|
|
}
|
|
|
|
get files() {
|
|
return this.qs('[type=file]').files
|
|
}
|
|
|
|
get raw() {
|
|
return this.qs('textarea').value
|
|
}
|
|
|
|
set raw(value) {
|
|
this.qs('textarea').value = value
|
|
this.onChange()
|
|
}
|
|
|
|
get clear() {
|
|
return Boolean(this.qs('[name=clear]').checked)
|
|
}
|
|
|
|
get action() {
|
|
return this.qs('[name=action]:checked')?.value
|
|
}
|
|
|
|
get layerId() {
|
|
return this.qs('[name=layer-id]').value
|
|
}
|
|
|
|
set layerId(value) {
|
|
this.qs('[name=layer-id]').value = value
|
|
}
|
|
|
|
get layerName() {
|
|
return this.qs('[name=layer-name]').value
|
|
}
|
|
|
|
set layerName(name) {
|
|
this.qs('[name=layer-name]').value = name
|
|
this.onChange()
|
|
}
|
|
|
|
get layer() {
|
|
return (
|
|
this._umap.datalayers[this.layerId] ||
|
|
this._umap.createDataLayer({ name: this.layerName })
|
|
)
|
|
}
|
|
|
|
showImporters() {
|
|
if (!this.IMPORTERS.length) return
|
|
const [element, { grid }] = Utils.loadTemplateWithRefs(GRID_TEMPLATE)
|
|
for (const plugin of this.IMPORTERS.sort((a, b) => (a.id > b.id ? 1 : -1))) {
|
|
const button = Utils.loadTemplate(
|
|
`<li><button type="button" class="${plugin.id}">${plugin.name}</button></li>`
|
|
)
|
|
button.addEventListener('click', () => plugin.open(this))
|
|
grid.appendChild(button)
|
|
}
|
|
this.dialog.open({ template: element, cancel: false, accept: false })
|
|
}
|
|
|
|
build() {
|
|
this.container = this.loadTemplate(TEMPLATE)
|
|
if (this.IMPORTERS.length) {
|
|
// TODO use this.elements instead of this.qs
|
|
const button = this.qs('[data-ref=importersButton]')
|
|
button.addEventListener('click', () => this.showImporters())
|
|
button.toggleAttribute('hidden', false)
|
|
}
|
|
for (const type of this.TYPES) {
|
|
DomUtil.element({
|
|
tagName: 'option',
|
|
parent: this.qs('[name=format]'),
|
|
value: type,
|
|
textContent: type,
|
|
})
|
|
}
|
|
this._umap.help.parse(this.container)
|
|
this.qs('[name=submit]').addEventListener('click', () => this.submit())
|
|
DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this)
|
|
for (const element of this.container.querySelectorAll('[onchange]')) {
|
|
DomEvent.on(element, 'change', this.onChange, this)
|
|
}
|
|
}
|
|
|
|
onChange() {
|
|
this.qs('#destination').toggleAttribute('hidden', this.format === 'umap')
|
|
this.qs('#import-mode').toggleAttribute(
|
|
'hidden',
|
|
this.format === 'umap' || !this.url
|
|
)
|
|
this.qs('[name=layer-name]').toggleAttribute('hidden', Boolean(this.layerId))
|
|
this.qs('#clear').toggleAttribute('hidden', !this.layerId)
|
|
this.qs('[name=submit').toggleAttribute('disabled', !this.canSubmit())
|
|
}
|
|
|
|
onFileChange(e) {
|
|
let type = ''
|
|
let newType
|
|
for (const file of e.target.files) {
|
|
newType = U.Utils.detectFileType(file)
|
|
if (!type && newType) type = newType
|
|
if (type && newType !== type) {
|
|
type = ''
|
|
break
|
|
}
|
|
}
|
|
this.format = type
|
|
}
|
|
|
|
onLoad() {
|
|
this.qs('[type=file]').value = null
|
|
this.url = null
|
|
this.format = undefined
|
|
this.layerName = null
|
|
this.raw = null
|
|
const layerSelect = this.qs('[name="layer-id"]')
|
|
layerSelect.innerHTML = ''
|
|
this._umap.eachDataLayerReverse((datalayer) => {
|
|
if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
|
|
DomUtil.element({
|
|
tagName: 'option',
|
|
parent: layerSelect,
|
|
textContent: datalayer.options.name,
|
|
value: L.stamp(datalayer),
|
|
})
|
|
}
|
|
})
|
|
DomUtil.element({
|
|
tagName: 'option',
|
|
value: '',
|
|
textContent: translate('Import in a new layer'),
|
|
parent: layerSelect,
|
|
selected: true,
|
|
})
|
|
}
|
|
|
|
open() {
|
|
if (!this.container) this.build()
|
|
const onLoad = this._umap.editPanel.open({ content: this.container })
|
|
onLoad.then(() => this.onLoad())
|
|
}
|
|
|
|
openFiles() {
|
|
this.open()
|
|
this.qs('[type=file]').showPicker()
|
|
}
|
|
|
|
canSubmit() {
|
|
if (!this.format) return false
|
|
const hasFiles = Boolean(this.files.length)
|
|
const hasRaw = Boolean(this.raw)
|
|
const hasUrl = Boolean(this.url)
|
|
const hasAction = Boolean(this.action)
|
|
if (!hasFiles && !hasRaw && !hasUrl) return false
|
|
if (this.url) return hasAction
|
|
return true
|
|
}
|
|
|
|
submit() {
|
|
let hasErrors
|
|
if (this.format === 'umap') {
|
|
hasErrors = !this.full()
|
|
} else if (!this.url) {
|
|
hasErrors = !this.copy()
|
|
} else if (this.action) {
|
|
hasErrors = !this[this.action]()
|
|
}
|
|
if (hasErrors === false) {
|
|
Alert.info(translate('Data successfully imported!'))
|
|
}
|
|
}
|
|
|
|
full() {
|
|
try {
|
|
if (this.files.length) {
|
|
for (const file of this.files) {
|
|
this._umap.processFileToImport(file, null, 'umap')
|
|
}
|
|
} else if (this.raw) {
|
|
this._umap.importRaw(this.raw)
|
|
} else if (this.url) {
|
|
this._umap.importFromUrl(this.url, this.format)
|
|
}
|
|
} catch (e) {
|
|
Alert.error(translate('Invalid umap data'))
|
|
console.error(e)
|
|
return false
|
|
}
|
|
}
|
|
|
|
link() {
|
|
if (!this.url) {
|
|
return false
|
|
}
|
|
if (!this.format) {
|
|
Alert.error(translate('Please choose a format'))
|
|
return false
|
|
}
|
|
const layer = this.layer
|
|
layer.options.remoteData = {
|
|
url: this.url,
|
|
format: this.format,
|
|
}
|
|
if (this._umap.properties.urls.ajax_proxy) {
|
|
layer.options.remoteData.proxy = true
|
|
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
|
}
|
|
layer.fetchRemoteData(true)
|
|
}
|
|
|
|
copy() {
|
|
// Format may be guessed from file later.
|
|
// Usefull in case of multiple files with different formats.
|
|
if (!this.format && !this.files.length) {
|
|
Alert.error(translate('Please choose a format'))
|
|
return false
|
|
}
|
|
const layer = this.layer
|
|
if (this.clear) layer.empty()
|
|
if (this.files.length) {
|
|
for (const file of this.files) {
|
|
this._umap.processFileToImport(file, layer, this.format)
|
|
}
|
|
} else if (this.raw) {
|
|
layer.importRaw(this.raw, this.format)
|
|
} else if (this.url) {
|
|
layer.importFromUrl(this.url, this.format)
|
|
}
|
|
}
|
|
}
|