diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js
index b905e13f..ad6cff9f 100644
--- a/umap/static/umap/js/umap.controls.js
+++ b/umap/static/umap/js/umap.controls.js
@@ -19,7 +19,7 @@ L.U.ImportAction = L.U.BaseAction.extend({
},
addHooks: function () {
- this.map.importPanel()
+ this.map.importer.open()
},
})
@@ -1172,134 +1172,6 @@ L.U.Map.include({
this.ui.openPanel({ data: { html: container } })
},
- importPanel: function () {
- const container = L.DomUtil.create('div', 'umap-upload')
- const title = L.DomUtil.create('h4', '', container)
- const presetBox = L.DomUtil.create('div', 'formbox', container)
- const presetSelect = L.DomUtil.create('select', '', presetBox)
- const fileBox = L.DomUtil.create('div', 'formbox', container)
- const fileInput = L.DomUtil.create('input', '', fileBox)
- const urlInput = L.DomUtil.create('input', '', container)
- const rawInput = L.DomUtil.create('textarea', '', container)
- const typeLabel = L.DomUtil.create('label', '', container)
- const layerLabel = L.DomUtil.create('label', '', container)
- const clearLabel = L.DomUtil.create('label', '', container)
- const submitInput = L.DomUtil.create('input', '', container)
- const map = this
- let option
- const types = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap']
- title.textContent = L._('Import data')
- fileInput.type = 'file'
- fileInput.multiple = 'multiple'
- submitInput.type = 'button'
- submitInput.value = L._('Import')
- submitInput.className = 'button'
- typeLabel.textContent = L._('Choose the format of the data to import')
- this.help.button(typeLabel, 'importFormats')
- const typeInput = L.DomUtil.create('select', '', typeLabel)
- typeInput.name = 'format'
- layerLabel.textContent = L._('Choose the layer to import in')
- const layerInput = L.DomUtil.create('select', '', layerLabel)
- layerInput.name = 'datalayer'
- urlInput.type = 'text'
- urlInput.placeholder = L._('Provide an URL here')
- rawInput.placeholder = L._('Paste your data here')
- clearLabel.textContent = L._('Replace layer content')
- const clearFlag = L.DomUtil.create('input', '', clearLabel)
- clearFlag.type = 'checkbox'
- clearFlag.name = 'clear'
- this.eachDataLayerReverse((datalayer) => {
- if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
- const id = L.stamp(datalayer)
- option = L.DomUtil.create('option', '', layerInput)
- option.value = id
- option.textContent = datalayer.options.name
- }
- })
- L.DomUtil.element(
- 'option',
- { value: '', textContent: L._('Import in a new layer') },
- layerInput
- )
- L.DomUtil.element(
- 'option',
- { value: '', textContent: L._('Choose the data format') },
- typeInput
- )
- for (let i = 0; i < types.length; i++) {
- option = L.DomUtil.create('option', '', typeInput)
- option.value = option.textContent = types[i]
- }
- if (this.options.importPresets.length) {
- const noPreset = L.DomUtil.create('option', '', presetSelect)
- noPreset.value = noPreset.textContent = L._('Choose a preset')
- for (let j = 0; j < this.options.importPresets.length; j++) {
- option = L.DomUtil.create('option', '', presetSelect)
- option.value = this.options.importPresets[j].url
- option.textContent = this.options.importPresets[j].label
- }
- } else {
- presetBox.style.display = 'none'
- }
-
- const submit = function () {
- let type = typeInput.value
- const layerId = layerInput[layerInput.selectedIndex].value
- let layer
- if (type === 'umap') {
- this.once('postsync', function () {
- this.setView(this.latLng(this.options.center), this.options.zoom)
- })
- }
- if (layerId) layer = map.datalayers[layerId]
- if (layer && clearFlag.checked) layer.empty()
- if (fileInput.files.length) {
- for (let i = 0, file; (file = fileInput.files[i]); i++) {
- this.processFileToImport(file, layer, type)
- }
- } else {
- if (!type)
- return this.ui.alert({
- content: L._('Please choose a format'),
- level: 'error',
- })
- if (rawInput.value && type === 'umap') {
- try {
- this.importRaw(rawInput.value, type)
- } catch (e) {
- this.ui.alert({ content: L._('Invalid umap data'), level: 'error' })
- console.error(e)
- }
- } else {
- if (!layer) layer = this.createDataLayer()
- if (rawInput.value) layer.importRaw(rawInput.value, type)
- else if (urlInput.value) layer.importFromUrl(urlInput.value, type)
- else if (presetSelect.selectedIndex > 0)
- layer.importFromUrl(presetSelect[presetSelect.selectedIndex].value, type)
- }
- }
- }
- L.DomEvent.on(submitInput, 'click', submit, this)
- L.DomEvent.on(
- fileInput,
- 'change',
- (e) => {
- let type = '',
- newType
- for (let i = 0; i < e.target.files.length; i++) {
- newType = L.Util.detectFileType(e.target.files[i])
- if (!type && newType) type = newType
- if (type && newType !== type) {
- type = ''
- break
- }
- }
- typeInput.value = type
- },
- this
- )
- this.ui.openPanel({ data: { html: container }, className: 'dark' })
- },
})
L.U.TileLayerControl = L.Control.extend({
diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js
index 02384ec0..7b0fbb10 100644
--- a/umap/static/umap/js/umap.core.js
+++ b/umap/static/umap/js/umap.core.js
@@ -426,6 +426,7 @@ L.U.Keys = {
I: 73,
L: 76,
M: 77,
+ O: 79,
P: 80,
S: 83,
Z: 90,
diff --git a/umap/static/umap/js/umap.importer.js b/umap/static/umap/js/umap.importer.js
new file mode 100644
index 00000000..9cd96c76
--- /dev/null
+++ b/umap/static/umap/js/umap.importer.js
@@ -0,0 +1,166 @@
+L.U.Importer = L.Class.extend({
+ TYPES: ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'],
+ initialize: function (map) {
+ this.map = map
+ this.presets = map.options.importPresets
+ },
+
+ build: function () {
+ this.container = L.DomUtil.create('div', 'umap-upload')
+ this.title = L.DomUtil.add('h4', '', this.container, L._('Import data'))
+ this.presetBox = L.DomUtil.create('div', 'formbox', this.container)
+ this.presetSelect = L.DomUtil.create('select', '', this.presetBox)
+ this.fileBox = L.DomUtil.create('div', 'formbox', this.container)
+ this.fileInput = L.DomUtil.element(
+ 'input',
+ { type: 'file', multiple: 'multiple', autofocus: true },
+ this.fileBox
+ )
+ this.urlInput = L.DomUtil.element(
+ 'input',
+ { type: 'text', placeholder: L._('Provide an URL here') },
+ this.container
+ )
+ this.rawInput = L.DomUtil.element(
+ 'textarea',
+ { placeholder: L._('Paste your data here') },
+ this.container
+ )
+ this.typeLabel = L.DomUtil.add(
+ 'label',
+ '',
+ this.container,
+ L._('Choose the format of the data to import')
+ )
+ this.layerLabel = L.DomUtil.add(
+ 'label',
+ '',
+ this.container,
+ L._('Choose the layer to import in')
+ )
+ this.clearLabel = L.DomUtil.add(
+ 'label',
+ '',
+ this.container,
+ L._('Replace layer content')
+ )
+ this.submitInput = L.DomUtil.element(
+ 'input',
+ { type: 'button', value: L._('Import'), className: 'button' },
+ this.container
+ )
+ this.map.help.button(this.typeLabel, 'importFormats')
+ this.typeInput = L.DomUtil.element('select', { name: 'format' }, this.typeLabel)
+ this.layerInput = L.DomUtil.element(
+ 'select',
+ { name: 'datalayer' },
+ this.layerLabel
+ )
+ this.clearFlag = L.DomUtil.element(
+ 'input',
+ { type: 'checkbox', name: 'clear' },
+ this.clearLabel
+ )
+ let option
+ this.map.eachDataLayerReverse((datalayer) => {
+ if (datalayer.isLoaded() && !datalayer.isRemoteLayer()) {
+ const id = L.stamp(datalayer)
+ option = L.DomUtil.add('option', '', this.layerInput, datalayer.options.name)
+ option.value = id
+ }
+ })
+ L.DomUtil.element(
+ 'option',
+ { value: '', textContent: L._('Import in a new layer') },
+ this.layerInput
+ )
+ L.DomUtil.element(
+ 'option',
+ { value: '', textContent: L._('Choose the data format') },
+ this.typeInput
+ )
+ for (let i = 0; i < this.TYPES.length; i++) {
+ option = L.DomUtil.create('option', '', this.typeInput)
+ option.value = option.textContent = this.TYPES[i]
+ }
+ if (this.presets.length) {
+ const noPreset = L.DomUtil.create('option', '', this.presetSelect)
+ noPreset.value = noPreset.textContent = L._('Choose a preset')
+ for (let j = 0; j < this.presets.length; j++) {
+ option = L.DomUtil.create('option', '', presetSelect)
+ option.value = this.presets[j].url
+ option.textContent = this.presets[j].label
+ }
+ } else {
+ this.presetBox.style.display = 'none'
+ }
+ L.DomEvent.on(this.submitInput, 'click', this.submit, this)
+ L.DomEvent.on(
+ this.fileInput,
+ 'change',
+ (e) => {
+ let type = '',
+ newType
+ for (let i = 0; i < e.target.files.length; i++) {
+ newType = L.Util.detectFileType(e.target.files[i])
+ if (!type && newType) type = newType
+ if (type && newType !== type) {
+ type = ''
+ break
+ }
+ }
+ this.typeInput.value = type
+ },
+ this
+ )
+ },
+
+ open: function () {
+ if (!this.container) this.build()
+ this.map.ui.openPanel({ data: { html: this.container }, className: 'dark' })
+ },
+
+ openFiles: function () {
+ this.open()
+ this.fileInput.showPicker()
+ },
+
+ submit: function () {
+ let type = this.typeInput.value
+ const layerId = this.layerInput[this.layerInput.selectedIndex].value
+ let layer
+ if (type === 'umap') {
+ this.map.once('postsync', this.map._setDefaultCenter)
+ }
+ if (layerId) layer = this.map.datalayers[layerId]
+ if (layer && this.clearFlag.checked) layer.empty()
+ if (this.fileInput.files.length) {
+ for (let i = 0, file; (file = this.fileInput.files[i]); i++) {
+ this.map.processFileToImport(file, layer, type)
+ }
+ } else {
+ if (!type)
+ return this.map.ui.alert({
+ content: L._('Please choose a format'),
+ level: 'error',
+ })
+ if (this.rawInput.value && type === 'umap') {
+ try {
+ this.map.importRaw(this.rawInput.value, type)
+ } catch (e) {
+ this.ui.alert({ content: L._('Invalid umap data'), level: 'error' })
+ console.error(e)
+ }
+ } else {
+ if (!layer) layer = this.map.createDataLayer()
+ if (this.rawInput.value) layer.importRaw(this.rawInput.value, type)
+ else if (this.urlInput.value) layer.importFromUrl(this.urlInput.value, type)
+ else if (this.presetSelect.selectedIndex > 0)
+ layer.importFromUrl(
+ this.presetSelect[this.presetSelect.selectedIndex].value,
+ type
+ )
+ }
+ }
+ },
+})
diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js
index 44be35cc..b865a778 100644
--- a/umap/static/umap/js/umap.js
+++ b/umap/static/umap/js/umap.js
@@ -343,6 +343,7 @@ L.U.Map.include({
if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable()
else this.scrollWheelZoom.disable()
this.browser = new L.U.Browser(this)
+ this.importer = new L.U.Importer(this)
this.drop = new L.U.DropControl(this)
this.renderControls()
},
@@ -560,7 +561,11 @@ L.U.Map.include({
}
if (key === L.U.Keys.I && modifierKey && this.editEnabled) {
L.DomEvent.stop(e)
- this.importPanel()
+ this.importer.open()
+ }
+ if (key === L.U.Keys.O && modifierKey && this.editEnabled) {
+ L.DomEvent.stop(e)
+ this.importer.openFiles()
}
if (key === L.U.Keys.H && modifierKey && this.editEnabled) {
L.DomEvent.stop(e)
diff --git a/umap/static/umap/test/index.html b/umap/static/umap/test/index.html
index e56ff9b5..4f11268b 100644
--- a/umap/static/umap/test/index.html
+++ b/umap/static/umap/test/index.html
@@ -41,6 +41,7 @@
+
diff --git a/umap/templates/umap/js.html b/umap/templates/umap/js.html
index 11693494..6a5b29af 100644
--- a/umap/templates/umap/js.html
+++ b/umap/templates/umap/js.html
@@ -43,6 +43,7 @@
+
{% endcompress %}
diff --git a/umap/tests/integration/test_import.py b/umap/tests/integration/test_import.py
index 0d492221..fbc21418 100644
--- a/umap/tests/integration/test_import.py
+++ b/umap/tests/integration/test_import.py
@@ -6,7 +6,7 @@ from playwright.sync_api import expect
pytestmark = pytest.mark.django_db
-def test_umap_import(live_server, datalayer, page):
+def test_umap_import_from_file(live_server, datalayer, page):
page.goto(f"{live_server.url}/map/new/")
button = page.get_by_title("Import data (Ctrl+I)")
expect(button).to_be_visible()
@@ -23,3 +23,27 @@ def test_umap_import(live_server, datalayer, page):
expect(layers).to_have_count(3)
nonloaded = page.locator(".umap-browse-datalayers li.off")
expect(nonloaded).to_have_count(1)
+
+
+def test_umap_import_geojson_from_textarea(live_server, datalayer, page):
+ page.goto(f"{live_server.url}/map/new/")
+ layers = page.locator(".umap-browse-datalayers li")
+ markers = page.locator(".leaflet-marker-icon")
+ paths = page.locator("path")
+ expect(markers).to_have_count(0)
+ expect(paths).to_have_count(0)
+ expect(layers).to_have_count(1)
+ button = page.get_by_title("Import data (Ctrl+I)")
+ expect(button).to_be_visible()
+ button.click()
+ textarea = page.locator(".umap-upload textarea")
+ path = Path(__file__).parent.parent / "fixtures/test_upload_data.json"
+ textarea.fill(path.read_text())
+ page.locator('select[name="format"]').select_option("geojson")
+ button = page.get_by_role("button", name="Import", exact=True)
+ expect(button).to_be_visible()
+ button.click()
+ # No layer has been created
+ expect(layers).to_have_count(1)
+ expect(markers).to_have_count(2)
+ expect(paths).to_have_count(3)