umap/umap/static/umap/js/modules/formatter.js
2024-11-12 10:17:12 +01:00

163 lines
4.4 KiB
JavaScript

/* Uses globals for: csv2geojson, osmtogeojson (not available as ESM) */
import { translate } from './i18n.js'
export const EXPORT_FORMATS = {
geojson: {
formatter: async (umap) => JSON.stringify(umap.toGeoJSON(), null, 2),
ext: '.geojson',
filetype: 'application/json',
},
gpx: {
formatter: async (umap) => await umap.formatter.toGPX(umap.toGeoJSON()),
ext: '.gpx',
filetype: 'application/gpx+xml',
},
kml: {
formatter: async (umap) => await umap.formatter.toKML(umap.toGeoJSON()),
ext: '.kml',
filetype: 'application/vnd.google-earth.kml+xml',
},
csv: {
formatter: async (umap) => {
const table = []
umap.eachFeature((feature) => {
const row = feature.toGeoJSON().properties
const center = feature.center
delete row._umap_options
row.Latitude = center.lat
row.Longitude = center.lng
table.push(row)
})
return csv2geojson.dsv.csvFormat(table)
},
ext: '.csv',
filetype: 'text/csv',
},
}
export class Formatter {
async fromGPX(str) {
const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
const data = togeojson.gpx(this.toDom(str))
for (const feature of data.features || []) {
feature.properties.description = feature.properties.desc
for (const key in feature.properties) {
if (key.startsWith('_') || typeof feature.properties[key] === 'object') {
delete feature.properties[key]
}
}
}
return data
}
async fromKML(str) {
const togeojson = await import('../../vendors/togeojson/togeojson.es.js')
return togeojson.kml(this.toDom(str), {
skipNullGeometry: true,
})
}
async fromGeoJSON(str) {
try {
return JSON.parse(str)
} catch (err) {
U.Alert.error(`Invalid JSON file: ${err}`)
}
}
async fromOSM(str) {
let src
try {
src = JSON.parse(str)
} catch (e) {
src = this.toDom(str)
}
return osmtogeojson(src, { flatProperties: true })
}
fromCSV(str, callback) {
csv2geojson.csv2geojson(
str,
{
delimiter: 'auto',
includeLatLon: false,
},
(err, result) => {
// csv2geojson fallback to null geometries when it cannot determine
// lat or lon columns. This is valid geojson, but unwanted from a user
// point of view.
if (result?.features.length) {
if (result.features[0].geometry === null) {
err = {
type: 'Error',
message: translate('Cannot determine latitude and longitude columns.'),
}
}
}
if (err) {
let message
if (err.type === 'Error') {
message = err.message
} else {
message = translate('{count} errors during import: {message}', {
count: err.length,
message: err[0].message,
})
}
U.Alert.error(message, 10000)
console.error(err)
}
if (result?.features.length) {
callback(result)
}
}
)
}
async fromGeoRSS(str) {
const GeoRSSToGeoJSON = await import('../../vendors/georsstogeojson/GeoRSSToGeoJSON.js')
return GeoRSSToGeoJSON.parse(this.toDom(str))
}
toDom(x) {
const doc = new DOMParser().parseFromString(x, 'text/xml')
const errorNode = doc.querySelector('parsererror')
if (errorNode) {
U.Alert.error(translate('Cannot parse data'))
}
return doc
}
async parse(str, format) {
switch (format) {
case 'csv':
return new Promise((resolve, reject) => {
return this.fromCSV(str, (data) => resolve(data))
})
case 'gpx':
return await this.fromGPX(str)
case 'kml':
return await this.fromKML(str)
case 'osm':
return await this.fromOSM(str)
case 'georss':
return await this.fromGeoRSS(str)
case 'geojson':
return await this.fromGeoJSON(str)
}
}
async toGPX(geojson) {
const togpx = await import('../../vendors/geojson-to-gpx/index.js')
for (const feature of geojson.features) {
feature.properties.desc = feature.properties.description
}
const gpx = togpx.default(geojson)
return new XMLSerializer().serializeToString(gpx)
}
async toKML(geojson) {
const tokml = await import('../../vendors/tokml/tokml.es.js')
return tokml.toKML(geojson)
}
}