mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 03:42:37 +02:00
feat: refactor importer feedback (#2363)
We basically make the all import chain return the results as promise, so the importer can act at the end of the process and: - zoom only to the imported data (in case the layer already as some) - display a counter of imported data when import is successfull - display an alert when nothing has been imported cf #564
This commit is contained in:
commit
cd24bf0b3d
3 changed files with 100 additions and 39 deletions
|
@ -252,10 +252,11 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
fromGeoJSON(geojson, sync = true) {
|
fromGeoJSON(geojson, sync = true) {
|
||||||
this.addData(geojson, sync)
|
const features = this.addData(geojson, sync)
|
||||||
this._geojson = geojson
|
this._geojson = geojson
|
||||||
this.onDataLoaded()
|
this.onDataLoaded()
|
||||||
this.dataChanged()
|
this.dataChanged()
|
||||||
|
return features
|
||||||
}
|
}
|
||||||
|
|
||||||
onDataLoaded() {
|
onDataLoaded() {
|
||||||
|
@ -315,7 +316,7 @@ export class DataLayer extends ServerStored {
|
||||||
const response = await this._umap.request.get(url)
|
const response = await this._umap.request.get(url)
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
this.clear()
|
this.clear()
|
||||||
this._umap.formatter
|
return this._umap.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))
|
||||||
}
|
}
|
||||||
|
@ -443,10 +444,11 @@ export class DataLayer extends ServerStored {
|
||||||
try {
|
try {
|
||||||
// Do not fail if remote data is somehow invalid,
|
// Do not fail if remote data is somehow invalid,
|
||||||
// otherwise the layer becomes uneditable.
|
// otherwise the layer becomes uneditable.
|
||||||
this.makeFeatures(geojson, sync)
|
return this.makeFeatures(geojson, sync)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Error with DataLayer', this.id)
|
console.log('Error with DataLayer', this.id)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,10 +465,13 @@ export class DataLayer extends ServerStored {
|
||||||
? geojson
|
? geojson
|
||||||
: geojson.features || geojson.geometries
|
: geojson.features || geojson.geometries
|
||||||
if (!collection) return
|
if (!collection) return
|
||||||
|
const features = []
|
||||||
this.sortFeatures(collection)
|
this.sortFeatures(collection)
|
||||||
for (const feature of collection) {
|
for (const featureJson of collection) {
|
||||||
this.makeFeature(feature, sync)
|
const feature = this.makeFeature(featureJson, sync)
|
||||||
|
if (feature) features.push(feature)
|
||||||
}
|
}
|
||||||
|
return features
|
||||||
}
|
}
|
||||||
|
|
||||||
makeFeature(geojson = {}, sync = true, id = null) {
|
makeFeature(geojson = {}, sync = true, id = null) {
|
||||||
|
@ -503,31 +508,47 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
async importRaw(raw, format) {
|
async importRaw(raw, format) {
|
||||||
this._umap.formatter
|
return this._umap.formatter
|
||||||
.parse(raw, format)
|
.parse(raw, format)
|
||||||
.then((geojson) => this.addData(geojson))
|
.then((geojson) => this.addData(geojson))
|
||||||
.then(() => this.zoomTo())
|
.then((data) => {
|
||||||
this.isDirty = true
|
if (data?.length) this.isDirty = true
|
||||||
|
return data
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
importFromFiles(files, type) {
|
readFile(f) {
|
||||||
for (const f of files) {
|
return new Promise((resolve) => {
|
||||||
this.importFromFile(f, type)
|
const reader = new FileReader()
|
||||||
|
reader.onloadend = () => resolve(reader.result)
|
||||||
|
reader.readAsText(f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async importFromFiles(files, type) {
|
||||||
|
let all = []
|
||||||
|
for (const file of files) {
|
||||||
|
const features = await this.importFromFile(file, type)
|
||||||
|
if (features) {
|
||||||
|
all = all.concat(features)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve(all)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
importFromFile(f, type) {
|
async importFromFile(file, type) {
|
||||||
const reader = new FileReader()
|
|
||||||
type = type || Utils.detectFileType(f)
|
type = type || Utils.detectFileType(f)
|
||||||
reader.readAsText(f)
|
const raw = await this.readFile(file)
|
||||||
reader.onload = (e) => this.importRaw(e.target.result, type)
|
return this.importRaw(raw, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
async importFromUrl(uri, type) {
|
async importFromUrl(uri, type) {
|
||||||
uri = this._umap.renderUrl(uri)
|
uri = this._umap.renderUrl(uri)
|
||||||
const response = await this._umap.request.get(uri)
|
const response = await this._umap.request.get(uri)
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
this.importRaw(await response.text(), type)
|
return this.importRaw(await response.text(), type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -930,9 +951,9 @@ export class DataLayer extends ServerStored {
|
||||||
else this.hide()
|
else this.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
zoomTo() {
|
zoomTo(bounds) {
|
||||||
if (!this.isVisible()) return
|
if (!this.isVisible()) return
|
||||||
const bounds = this.layer.getBounds()
|
bounds = 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._leafletMap.fitBounds(bounds, options)
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import {
|
||||||
|
DomEvent,
|
||||||
|
DomUtil,
|
||||||
|
LatLngBounds,
|
||||||
|
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
import { uMapAlert as Alert } from '../components/alerts/alert.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import { SCHEMA } from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
|
@ -270,16 +274,12 @@ export default class Importer extends Utils.WithTemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
let hasErrors
|
|
||||||
if (this.format === 'umap') {
|
if (this.format === 'umap') {
|
||||||
hasErrors = !this.full()
|
this.full()
|
||||||
} else if (!this.url) {
|
} else if (!this.url) {
|
||||||
hasErrors = !this.copy()
|
this.copy()
|
||||||
} else if (this.action) {
|
} else if (this.action) {
|
||||||
hasErrors = !this[this.action]()
|
this[this.action]()
|
||||||
}
|
|
||||||
if (hasErrors === false) {
|
|
||||||
Alert.info(translate('Data successfully imported!'))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,8 +294,9 @@ export default class Importer extends Utils.WithTemplate {
|
||||||
} else if (this.url) {
|
} else if (this.url) {
|
||||||
this._umap.importFromUrl(this.url, this.format)
|
this._umap.importFromUrl(this.url, this.format)
|
||||||
}
|
}
|
||||||
|
this.onSuccess()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Alert.error(translate('Invalid umap data'))
|
this.onError(translate('Invalid umap data'))
|
||||||
console.error(e)
|
console.error(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -306,7 +307,7 @@ export default class Importer extends Utils.WithTemplate {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!this.format) {
|
if (!this.format) {
|
||||||
Alert.error(translate('Please choose a format'))
|
this.onError(translate('Please choose a format'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const layer = this.layer
|
const layer = this.layer
|
||||||
|
@ -318,26 +319,63 @@ export default class Importer extends Utils.WithTemplate {
|
||||||
layer.options.remoteData.proxy = true
|
layer.options.remoteData.proxy = true
|
||||||
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
layer.options.remoteData.ttl = SCHEMA.ttl.default
|
||||||
}
|
}
|
||||||
layer.fetchRemoteData(true)
|
layer.fetchRemoteData(true).then((features) => {
|
||||||
|
if (features?.length) {
|
||||||
|
layer.zoomTo()
|
||||||
|
this.onSuccess()
|
||||||
|
} else {
|
||||||
|
this.onError()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
copy() {
|
async copy() {
|
||||||
// Format may be guessed from file later.
|
// Format may be guessed from file later.
|
||||||
// Usefull in case of multiple files with different formats.
|
// Usefull in case of multiple files with different formats.
|
||||||
if (!this.format && !this.files.length) {
|
if (!this.format && !this.files.length) {
|
||||||
Alert.error(translate('Please choose a format'))
|
this.onError(translate('Please choose a format'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
let promise
|
||||||
const layer = this.layer
|
const layer = this.layer
|
||||||
if (this.clear) layer.empty()
|
if (this.clear) layer.empty()
|
||||||
if (this.files.length) {
|
if (this.files.length) {
|
||||||
for (const file of this.files) {
|
promise = layer.importFromFiles(this.files, this.format)
|
||||||
this._umap.processFileToImport(file, layer, this.format)
|
|
||||||
}
|
|
||||||
} else if (this.raw) {
|
} else if (this.raw) {
|
||||||
layer.importRaw(this.raw, this.format)
|
promise = layer.importRaw(this.raw, this.format)
|
||||||
} else if (this.url) {
|
} else if (this.url) {
|
||||||
layer.importFromUrl(this.url, this.format)
|
promise = layer.importFromUrl(this.url, this.format)
|
||||||
|
}
|
||||||
|
if (promise) promise.then((data) => this.onCopyFinished(layer, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
onError(message = translate('No data has been found for import')) {
|
||||||
|
Alert.error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess(count) {
|
||||||
|
if (count) {
|
||||||
|
Alert.success(translate(`Successfully imported ${count} feature(s)`))
|
||||||
|
} else {
|
||||||
|
Alert.success(translate('Data successfully imported!'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCopyFinished(layer, features) {
|
||||||
|
// undefined features means error, let original error message pop
|
||||||
|
if (!features) return
|
||||||
|
if (!features.length) {
|
||||||
|
this.onError()
|
||||||
|
} else {
|
||||||
|
const bounds = new LatLngBounds()
|
||||||
|
for (const feature of features) {
|
||||||
|
const featureBounds = feature.ui.getBounds
|
||||||
|
? feature.ui.getBounds()
|
||||||
|
: feature.ui.getCenter()
|
||||||
|
bounds.extend(featureBounds)
|
||||||
|
}
|
||||||
|
this.onSuccess(features.length)
|
||||||
|
layer.zoomTo(bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,12 +316,14 @@ export default class Umap extends ServerStored {
|
||||||
dataUrl = this.renderUrl(dataUrl)
|
dataUrl = this.renderUrl(dataUrl)
|
||||||
dataUrl = this.proxyUrl(dataUrl)
|
dataUrl = this.proxyUrl(dataUrl)
|
||||||
const datalayer = this.createDataLayer()
|
const datalayer = this.createDataLayer()
|
||||||
await datalayer.importFromUrl(dataUrl, dataFormat)
|
await datalayer
|
||||||
|
.importFromUrl(dataUrl, dataFormat)
|
||||||
|
.then(() => datalayer.zoomTo())
|
||||||
}
|
}
|
||||||
} else if (data) {
|
} else if (data) {
|
||||||
data = decodeURIComponent(data)
|
data = decodeURIComponent(data)
|
||||||
const datalayer = this.createDataLayer()
|
const datalayer = this.createDataLayer()
|
||||||
await datalayer.importRaw(data, dataFormat)
|
await datalayer.importRaw(data, dataFormat).then(() => datalayer.zoomTo())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1514,7 +1516,7 @@ export default class Umap extends ServerStored {
|
||||||
processFileToImport(file, layer, type) {
|
processFileToImport(file, layer, type) {
|
||||||
type = type || Utils.detectFileType(file)
|
type = type || Utils.detectFileType(file)
|
||||||
if (!type) {
|
if (!type) {
|
||||||
U.Alert.error(
|
Alert.error(
|
||||||
translate('Unable to detect format of file {filename}', {
|
translate('Unable to detect format of file {filename}', {
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue