mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
Merge pull request #1945 from umap-project/formatters
move formatters and share to a module, and use new GPX library
This commit is contained in:
commit
b68ca858ca
11 changed files with 264 additions and 216 deletions
|
@ -37,6 +37,8 @@
|
||||||
},
|
},
|
||||||
"homepage": "http://wiki.openstreetmap.org/wiki/UMap",
|
"homepage": "http://wiki.openstreetmap.org/wiki/UMap",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@dwayneparton/geojson-to-gpx": "^0.2.0",
|
||||||
|
"@placemarkio/tokml": "^0.3.3",
|
||||||
"@tmcw/togeojson": "^5.8.0",
|
"@tmcw/togeojson": "^5.8.0",
|
||||||
"colorbrewer": "^1.5.6",
|
"colorbrewer": "^1.5.6",
|
||||||
"csv2geojson": "5.1.1",
|
"csv2geojson": "5.1.1",
|
||||||
|
@ -62,9 +64,7 @@
|
||||||
"leaflet.path.drag": "0.0.6",
|
"leaflet.path.drag": "0.0.6",
|
||||||
"leaflet.photon": "0.9.1",
|
"leaflet.photon": "0.9.1",
|
||||||
"osmtogeojson": "^3.0.0-beta.3",
|
"osmtogeojson": "^3.0.0-beta.3",
|
||||||
"simple-statistics": "^7.8.3",
|
"simple-statistics": "^7.8.3"
|
||||||
"togpx": "^0.5.4",
|
|
||||||
"tokml": "0.4.0"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 0.5%, last 2 versions, Firefox ESR, not dead, not op_mini all"
|
"> 0.5%, last 2 versions, Firefox ESR, not dead, not op_mini all"
|
||||||
|
|
|
@ -19,16 +19,18 @@ mkdir -p umap/static/umap/vendors/formbuilder/ && cp -r node_modules/leaflet-for
|
||||||
mkdir -p umap/static/umap/vendors/measurable/ && cp -r node_modules/leaflet-measurable/Leaflet.Measurable.* umap/static/umap/vendors/measurable/
|
mkdir -p umap/static/umap/vendors/measurable/ && cp -r node_modules/leaflet-measurable/Leaflet.Measurable.* umap/static/umap/vendors/measurable/
|
||||||
mkdir -p umap/static/umap/vendors/photon/ && cp -r node_modules/leaflet.photon/leaflet.photon.js umap/static/umap/vendors/photon/
|
mkdir -p umap/static/umap/vendors/photon/ && cp -r node_modules/leaflet.photon/leaflet.photon.js umap/static/umap/vendors/photon/
|
||||||
mkdir -p umap/static/umap/vendors/csv2geojson/ && cp -r node_modules/csv2geojson/csv2geojson.js umap/static/umap/vendors/csv2geojson/
|
mkdir -p umap/static/umap/vendors/csv2geojson/ && cp -r node_modules/csv2geojson/csv2geojson.js umap/static/umap/vendors/csv2geojson/
|
||||||
mkdir -p umap/static/umap/vendors/togeojson/ && cp -r node_modules/@tmcw/togeojson/dist/togeojson.umd.* umap/static/umap/vendors/togeojson/
|
mkdir -p umap/static/umap/vendors/togeojson/ && cp node_modules/@tmcw/togeojson/dist/togeojson.es.mjs umap/static/umap/vendors/togeojson/togeojson.es.js
|
||||||
|
mkdir -p umap/static/umap/vendors/togeojson/ && cp node_modules/@tmcw/togeojson/dist/togeojson.es.mjs.map umap/static/umap/vendors/togeojson/togeojson.es.mjs.map
|
||||||
|
mkdir -p umap/static/umap/vendors/tokml/ && cp node_modules/@placemarkio/tokml/dist/tokml.es.mjs umap/static/umap/vendors/tokml/tokml.es.js
|
||||||
|
mkdir -p umap/static/umap/vendors/tokml/ && cp node_modules/@placemarkio/tokml/dist/tokml.es.mjs.map umap/static/umap/vendors/tokml/tokml.es.mjs.map
|
||||||
mkdir -p umap/static/umap/vendors/osmtogeojson/ && cp -r node_modules/osmtogeojson/osmtogeojson.js umap/static/umap/vendors/osmtogeojson/
|
mkdir -p umap/static/umap/vendors/osmtogeojson/ && cp -r node_modules/osmtogeojson/osmtogeojson.js umap/static/umap/vendors/osmtogeojson/
|
||||||
mkdir -p umap/static/umap/vendors/georsstogeojson/ && cp -r node_modules/georsstogeojson/GeoRSSToGeoJSON.js umap/static/umap/vendors/georsstogeojson/
|
mkdir -p umap/static/umap/vendors/georsstogeojson/ && cp -r node_modules/georsstogeojson/GeoRSSToGeoJSON.js umap/static/umap/vendors/georsstogeojson/
|
||||||
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
|
|
||||||
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
|
|
||||||
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.* umap/static/umap/vendors/locatecontrol/
|
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.* umap/static/umap/vendors/locatecontrol/
|
||||||
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.es.mjs umap/static/umap/vendors/dompurify/purify.es.js
|
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.es.mjs umap/static/umap/vendors/dompurify/purify.es.js
|
||||||
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.es.mjs.map umap/static/umap/vendors/dompurify/purify.es.mjs.map
|
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.es.mjs.map umap/static/umap/vendors/dompurify/purify.es.mjs.map
|
||||||
mkdir -p umap/static/umap/vendors/colorbrewer/ && cp node_modules/colorbrewer/index.js umap/static/umap/vendors/colorbrewer/colorbrewer.js
|
mkdir -p umap/static/umap/vendors/colorbrewer/ && cp node_modules/colorbrewer/index.js umap/static/umap/vendors/colorbrewer/colorbrewer.js
|
||||||
mkdir -p umap/static/umap/vendors/simple-statistics/ && cp node_modules/simple-statistics/dist/simple-statistics.min.* umap/static/umap/vendors/simple-statistics/
|
mkdir -p umap/static/umap/vendors/simple-statistics/ && cp node_modules/simple-statistics/dist/simple-statistics.min.* umap/static/umap/vendors/simple-statistics/
|
||||||
mkdir -p umap/static/umap/vendors/iconlayers/ && cp node_modules/leaflet-iconlayers/dist/* umap/static/umap/vendors/iconlayers/
|
mkdir -p umap/static/umap/vendors/iconlayers/ && cp node_modules/leaflet-iconlayers/dist/* umap/static/umap/vendors/iconlayers/
|
||||||
|
mkdir -p umap/static/umap/vendors/geojson-to-gpx/ && cp node_modules/@dwayneparton/geojson-to-gpx/dist/index.js umap/static/umap/vendors/geojson-to-gpx/
|
||||||
|
|
||||||
echo 'Done!'
|
echo 'Done!'
|
||||||
|
|
153
umap/static/umap/js/modules/formatter.js
Normal file
153
umap/static/umap/js/modules/formatter.js
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/* Uses globals for: csv2geojson, osmtogeojson, GeoRSSToGeoJSON (not available as ESM) */
|
||||||
|
import { translate } from './i18n.js'
|
||||||
|
|
||||||
|
export const EXPORT_FORMATS = {
|
||||||
|
geojson: {
|
||||||
|
formatter: async (map) => JSON.stringify(map.toGeoJSON(), null, 2),
|
||||||
|
ext: '.geojson',
|
||||||
|
filetype: 'application/json',
|
||||||
|
},
|
||||||
|
gpx: {
|
||||||
|
formatter: async (map) => await map.formatter.toGPX(map.toGeoJSON()),
|
||||||
|
ext: '.gpx',
|
||||||
|
filetype: 'application/gpx+xml',
|
||||||
|
},
|
||||||
|
kml: {
|
||||||
|
formatter: async (map) => await map.formatter.toKML(map.toGeoJSON()),
|
||||||
|
ext: '.kml',
|
||||||
|
filetype: 'application/vnd.google-earth.kml+xml',
|
||||||
|
},
|
||||||
|
csv: {
|
||||||
|
formatter: async (map) => {
|
||||||
|
const table = []
|
||||||
|
map.eachFeature((feature) => {
|
||||||
|
const row = feature.toGeoJSON().properties
|
||||||
|
const center = feature.getCenter()
|
||||||
|
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')
|
||||||
|
return togeojson.gpx(this.toDom(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return GeoRSSToGeoJSON(this.toDom(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,12 +7,14 @@ import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
||||||
import Browser from './browser.js'
|
import Browser from './browser.js'
|
||||||
import Caption from './caption.js'
|
import Caption from './caption.js'
|
||||||
import Facets from './facets.js'
|
import Facets from './facets.js'
|
||||||
|
import { Formatter } from './formatter.js'
|
||||||
import Help from './help.js'
|
import Help from './help.js'
|
||||||
import Importer from './importer.js'
|
import Importer from './importer.js'
|
||||||
import Orderable from './orderable.js'
|
import Orderable from './orderable.js'
|
||||||
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
||||||
import Rules from './rules.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 Slideshow from './slideshow.js'
|
||||||
import { SyncEngine } from './sync/engine.js'
|
import { SyncEngine } from './sync/engine.js'
|
||||||
import Dialog from './ui/dialog.js'
|
import Dialog from './ui/dialog.js'
|
||||||
|
@ -36,6 +38,7 @@ window.U = {
|
||||||
Dialog,
|
Dialog,
|
||||||
EditPanel,
|
EditPanel,
|
||||||
Facets,
|
Facets,
|
||||||
|
Formatter,
|
||||||
FullPanel,
|
FullPanel,
|
||||||
Help,
|
Help,
|
||||||
HTTPError,
|
HTTPError,
|
||||||
|
@ -48,6 +51,7 @@ window.U = {
|
||||||
Rules,
|
Rules,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
ServerRequest,
|
ServerRequest,
|
||||||
|
Share,
|
||||||
Slideshow,
|
Slideshow,
|
||||||
SyncEngine,
|
SyncEngine,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
|
|
@ -1,43 +1,11 @@
|
||||||
U.Share = L.Class.extend({
|
import { EXPORT_FORMATS } from './formatter.js'
|
||||||
EXPORT_TYPES: {
|
|
||||||
geojson: {
|
|
||||||
formatter: (map) => JSON.stringify(map.toGeoJSON(), null, 2),
|
|
||||||
ext: '.geojson',
|
|
||||||
filetype: 'application/json',
|
|
||||||
},
|
|
||||||
gpx: {
|
|
||||||
formatter: (map) => togpx(map.toGeoJSON()),
|
|
||||||
ext: '.gpx',
|
|
||||||
filetype: 'application/gpx+xml',
|
|
||||||
},
|
|
||||||
kml: {
|
|
||||||
formatter: (map) => tokml(map.toGeoJSON()),
|
|
||||||
ext: '.kml',
|
|
||||||
filetype: 'application/vnd.google-earth.kml+xml',
|
|
||||||
},
|
|
||||||
csv: {
|
|
||||||
formatter: (map) => {
|
|
||||||
const table = []
|
|
||||||
map.eachFeature((feature) => {
|
|
||||||
const row = feature.toGeoJSON().properties
|
|
||||||
const center = feature.getCenter()
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function (map) {
|
export default class Share {
|
||||||
|
constructor(map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
},
|
}
|
||||||
|
|
||||||
build: function () {
|
build() {
|
||||||
this.container = L.DomUtil.create('div', '')
|
this.container = L.DomUtil.create('div', '')
|
||||||
this.title = L.DomUtil.createTitle(
|
this.title = L.DomUtil.createTitle(
|
||||||
this.container,
|
this.container,
|
||||||
|
@ -63,16 +31,10 @@ U.Share = L.Class.extend({
|
||||||
|
|
||||||
L.DomUtil.add('h4', '', this.container, L._('Download'))
|
L.DomUtil.add('h4', '', this.container, L._('Download'))
|
||||||
L.DomUtil.add('small', 'label', this.container, L._("Only visible layers' data"))
|
L.DomUtil.add('small', 'label', this.container, L._("Only visible layers' data"))
|
||||||
for (const key in this.EXPORT_TYPES) {
|
for (const format of Object.keys(EXPORT_FORMATS)) {
|
||||||
if (this.EXPORT_TYPES.hasOwnProperty(key)) {
|
L.DomUtil.createButton('download-file', this.container, format, () =>
|
||||||
L.DomUtil.createButton(
|
this.download(format)
|
||||||
'download-file',
|
)
|
||||||
this.container,
|
|
||||||
this.EXPORT_TYPES[key].name || key,
|
|
||||||
() => this.download(key),
|
|
||||||
this
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
L.DomUtil.create('div', 'vspace', this.container)
|
L.DomUtil.create('div', 'vspace', this.container)
|
||||||
L.DomUtil.add(
|
L.DomUtil.add(
|
||||||
|
@ -135,7 +97,7 @@ U.Share = L.Class.extend({
|
||||||
for (let i = 0; i < this.map.HIDDABLE_CONTROLS.length; i++) {
|
for (let i = 0; i < this.map.HIDDABLE_CONTROLS.length; i++) {
|
||||||
UIFields.push(`queryString.${this.map.HIDDABLE_CONTROLS[i]}Control`)
|
UIFields.push(`queryString.${this.map.HIDDABLE_CONTROLS[i]}Control`)
|
||||||
}
|
}
|
||||||
const iframeExporter = new U.IframeExporter(this.map)
|
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()
|
||||||
|
@ -149,24 +111,24 @@ U.Share = L.Class.extend({
|
||||||
L._('Embed and link options')
|
L._('Embed and link options')
|
||||||
)
|
)
|
||||||
iframeOptions.appendChild(builder.build())
|
iframeOptions.appendChild(builder.build())
|
||||||
},
|
}
|
||||||
|
|
||||||
open: function () {
|
open() {
|
||||||
if (!this.container) this.build()
|
if (!this.container) this.build()
|
||||||
this.map.panel.open({ content: this.container })
|
this.map.panel.open({ content: this.container })
|
||||||
},
|
}
|
||||||
|
|
||||||
format: function (mode) {
|
async format(mode) {
|
||||||
const type = this.EXPORT_TYPES[mode]
|
const type = EXPORT_FORMATS[mode]
|
||||||
const content = type.formatter(this.map)
|
const content = await type.formatter(this.map)
|
||||||
let name = this.map.options.name || 'data'
|
let name = this.map.options.name || 'data'
|
||||||
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
name = name.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
||||||
const filename = name + type.ext
|
const filename = name + type.ext
|
||||||
return { content, filetype: type.filetype, filename }
|
return { content, filetype: type.filetype, filename }
|
||||||
},
|
}
|
||||||
|
|
||||||
download: function (mode) {
|
async download(mode) {
|
||||||
const { content, filetype, filename } = this.format(mode)
|
const { content, filetype, filename } = await this.format(mode)
|
||||||
const blob = new Blob([content], { type: filetype })
|
const blob = new Blob([content], { type: filetype })
|
||||||
window.URL = window.URL || window.webkitURL
|
window.URL = window.URL || window.webkitURL
|
||||||
const el = document.createElement('a')
|
const el = document.createElement('a')
|
||||||
|
@ -176,50 +138,49 @@ U.Share = L.Class.extend({
|
||||||
document.body.appendChild(el)
|
document.body.appendChild(el)
|
||||||
el.click()
|
el.click()
|
||||||
document.body.removeChild(el)
|
document.body.removeChild(el)
|
||||||
},
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
U.IframeExporter = L.Evented.extend({
|
class IframeExporter {
|
||||||
options: {
|
constructor(map) {
|
||||||
includeFullScreenLink: true,
|
|
||||||
currentView: false,
|
|
||||||
keepCurrentDatalayers: false,
|
|
||||||
viewCurrentFeature: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
queryString: {
|
|
||||||
scaleControl: false,
|
|
||||||
miniMap: false,
|
|
||||||
scrollWheelZoom: false,
|
|
||||||
zoomControl: true,
|
|
||||||
editMode: 'disabled',
|
|
||||||
moreControl: true,
|
|
||||||
searchControl: null,
|
|
||||||
tilelayersControl: null,
|
|
||||||
embedControl: null,
|
|
||||||
datalayersControl: true,
|
|
||||||
onLoadPanel: 'none',
|
|
||||||
captionBar: false,
|
|
||||||
captionMenus: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
dimensions: {
|
|
||||||
width: '100%',
|
|
||||||
height: '300px',
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize: function (map) {
|
|
||||||
this.map = map
|
this.map = map
|
||||||
this.baseUrl = U.Utils.getBaseUrl()
|
this.baseUrl = U.Utils.getBaseUrl()
|
||||||
|
this.options = {
|
||||||
|
includeFullScreenLink: true,
|
||||||
|
currentView: false,
|
||||||
|
keepCurrentDatalayers: false,
|
||||||
|
viewCurrentFeature: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queryString = {
|
||||||
|
scaleControl: false,
|
||||||
|
miniMap: false,
|
||||||
|
scrollWheelZoom: false,
|
||||||
|
zoomControl: true,
|
||||||
|
editMode: 'disabled',
|
||||||
|
moreControl: true,
|
||||||
|
searchControl: null,
|
||||||
|
tilelayersControl: null,
|
||||||
|
embedControl: null,
|
||||||
|
datalayersControl: true,
|
||||||
|
onLoadPanel: 'none',
|
||||||
|
captionBar: false,
|
||||||
|
captionMenus: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dimensions = {
|
||||||
|
width: '100%',
|
||||||
|
height: '300px',
|
||||||
|
}
|
||||||
// Use map default, not generic default
|
// Use map default, not generic default
|
||||||
this.queryString.onLoadPanel = this.map.getOption('onLoadPanel')
|
this.queryString.onLoadPanel = this.map.getOption('onLoadPanel')
|
||||||
},
|
}
|
||||||
|
|
||||||
getMap: function () {
|
getMap() {
|
||||||
return this.map
|
return this.map
|
||||||
},
|
}
|
||||||
|
|
||||||
buildUrl: function (options) {
|
buildUrl(options) {
|
||||||
const datalayers = []
|
const datalayers = []
|
||||||
if (this.options.viewCurrentFeature && this.map.currentFeature) {
|
if (this.options.viewCurrentFeature && this.map.currentFeature) {
|
||||||
this.queryString.feature = this.map.currentFeature.getSlug()
|
this.queryString.feature = this.map.currentFeature.getSlug()
|
||||||
|
@ -239,9 +200,9 @@ U.IframeExporter = L.Evented.extend({
|
||||||
const currentView = this.options.currentView ? window.location.hash : ''
|
const currentView = this.options.currentView ? window.location.hash : ''
|
||||||
const queryString = L.extend({}, this.queryString, options)
|
const queryString = L.extend({}, this.queryString, options)
|
||||||
return `${this.baseUrl}?${U.Utils.buildQueryString(queryString)}${currentView}`
|
return `${this.baseUrl}?${U.Utils.buildQueryString(queryString)}${currentView}`
|
||||||
},
|
}
|
||||||
|
|
||||||
build: function () {
|
build() {
|
||||||
const iframeUrl = this.buildUrl()
|
const iframeUrl = this.buildUrl()
|
||||||
let code = `<iframe width="${this.dimensions.width}" height="${this.dimensions.height}" frameborder="0" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>`
|
let code = `<iframe width="${this.dimensions.width}" height="${this.dimensions.height}" frameborder="0" allowfullscreen allow="geolocation" src="${iframeUrl}"></iframe>`
|
||||||
if (this.options.includeFullScreenLink) {
|
if (this.options.includeFullScreenLink) {
|
||||||
|
@ -249,5 +210,5 @@ U.IframeExporter = L.Evented.extend({
|
||||||
code += `<p><a href="${fullUrl}">${L._('See full screen')}</a></p>`
|
code += `<p><a href="${fullUrl}">${L._('See full screen')}</a></p>`
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
},
|
}
|
||||||
})
|
}
|
|
@ -29,7 +29,7 @@ L.Map.mergeOptions({
|
||||||
U.Map = L.Map.extend({
|
U.Map = L.Map.extend({
|
||||||
includes: [ControlsMixin],
|
includes: [ControlsMixin],
|
||||||
|
|
||||||
initialize: function (el, geojson) {
|
initialize: async function (el, geojson) {
|
||||||
this.sync_engine = new U.SyncEngine(this)
|
this.sync_engine = new U.SyncEngine(this)
|
||||||
this.sync = this.sync_engine.proxy(this)
|
this.sync = this.sync_engine.proxy(this)
|
||||||
// Locale name (pt_PT, en_US…)
|
// Locale name (pt_PT, en_US…)
|
||||||
|
@ -116,6 +116,8 @@ U.Map = L.Map.extend({
|
||||||
// Needed for actions labels
|
// Needed for actions labels
|
||||||
this.help = new U.Help(this)
|
this.help = new U.Help(this)
|
||||||
|
|
||||||
|
this.formatter = new U.Formatter(this)
|
||||||
|
|
||||||
this.initControls()
|
this.initControls()
|
||||||
// Needs locate control and hash to exist
|
// Needs locate control and hash to exist
|
||||||
this.initCenter()
|
this.initCenter()
|
||||||
|
@ -174,23 +176,7 @@ U.Map = L.Map.extend({
|
||||||
}
|
}
|
||||||
this._default_extent = true
|
this._default_extent = true
|
||||||
this.options.name = L._('Untitled map')
|
this.options.name = L._('Untitled map')
|
||||||
let data = L.Util.queryString('data', null)
|
await this.loadDataFromQueryString()
|
||||||
const url = new URL(window.location.href)
|
|
||||||
const dataUrls = new URLSearchParams(url.search).getAll('dataUrl')
|
|
||||||
const dataFormat = L.Util.queryString('dataFormat', 'geojson')
|
|
||||||
if (dataUrls.length) {
|
|
||||||
for (let dataUrl of dataUrls) {
|
|
||||||
dataUrl = decodeURIComponent(dataUrl)
|
|
||||||
dataUrl = this.localizeUrl(dataUrl)
|
|
||||||
dataUrl = this.proxyUrl(dataUrl)
|
|
||||||
const datalayer = this.createDataLayer()
|
|
||||||
datalayer.importFromUrl(dataUrl, dataFormat)
|
|
||||||
}
|
|
||||||
} else if (data) {
|
|
||||||
data = decodeURIComponent(data)
|
|
||||||
const datalayer = this.createDataLayer()
|
|
||||||
datalayer.importRaw(data, dataFormat)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.slideshow = new U.Slideshow(this, this.options.slideshow)
|
this.slideshow = new U.Slideshow(this, this.options.slideshow)
|
||||||
|
@ -295,7 +281,27 @@ U.Map = L.Map.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setViewFromQueryString: function () {
|
loadDataFromQueryString: async function() {
|
||||||
|
let data = L.Util.queryString('data', null)
|
||||||
|
const url = new URL(window.location.href)
|
||||||
|
const dataUrls = new URLSearchParams(url.search).getAll('dataUrl')
|
||||||
|
const dataFormat = L.Util.queryString('dataFormat', 'geojson')
|
||||||
|
if (dataUrls.length) {
|
||||||
|
for (let dataUrl of dataUrls) {
|
||||||
|
dataUrl = decodeURIComponent(dataUrl)
|
||||||
|
dataUrl = this.localizeUrl(dataUrl)
|
||||||
|
dataUrl = this.proxyUrl(dataUrl)
|
||||||
|
const datalayer = this.createDataLayer()
|
||||||
|
await datalayer.importFromUrl(dataUrl, dataFormat)
|
||||||
|
}
|
||||||
|
} else if (data) {
|
||||||
|
data = decodeURIComponent(data)
|
||||||
|
const datalayer = this.createDataLayer()
|
||||||
|
await datalayer.importRaw(data, dataFormat)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setViewFromQueryString: async function () {
|
||||||
if (this.options.noControl) return
|
if (this.options.noControl) return
|
||||||
this.initCaptionBar()
|
this.initCaptionBar()
|
||||||
if (L.Util.queryString('share')) {
|
if (L.Util.queryString('share')) {
|
||||||
|
|
|
@ -792,11 +792,9 @@ U.DataLayer = L.Evented.extend({
|
||||||
const response = await this.map.request.get(url)
|
const response = await this.map.request.get(url)
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
this.clear()
|
this.clear()
|
||||||
this.rawToGeoJSON(
|
this.map.formatter
|
||||||
await response.text(),
|
.parse(await response.text(), this.options.remoteData.format)
|
||||||
this.options.remoteData.format,
|
.then((geojson) => this.fromGeoJSON(geojson))
|
||||||
(geojson) => this.fromGeoJSON(geojson)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -930,83 +928,6 @@ U.DataLayer = L.Evented.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addRawData: function (c, type) {
|
|
||||||
this.rawToGeoJSON(c, type, (geojson) => this.addData(geojson))
|
|
||||||
},
|
|
||||||
|
|
||||||
rawToGeoJSON: (c, type, callback) => {
|
|
||||||
const toDom = (x) => {
|
|
||||||
const doc = new DOMParser().parseFromString(x, 'text/xml')
|
|
||||||
const errorNode = doc.querySelector('parsererror')
|
|
||||||
if (errorNode) {
|
|
||||||
U.Alert.error(L._('Cannot parse data'))
|
|
||||||
}
|
|
||||||
return doc
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add a duck typing guessType
|
|
||||||
if (type === 'csv') {
|
|
||||||
csv2geojson.csv2geojson(
|
|
||||||
c,
|
|
||||||
{
|
|
||||||
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: L._('Cannot determine latitude and longitude columns.'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (err) {
|
|
||||||
let message
|
|
||||||
if (err.type === 'Error') {
|
|
||||||
message = err.message
|
|
||||||
} else {
|
|
||||||
message = L._('{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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (type === 'gpx') {
|
|
||||||
callback(toGeoJSON.gpx(toDom(c)))
|
|
||||||
} else if (type === 'georss') {
|
|
||||||
callback(GeoRSSToGeoJSON(toDom(c)))
|
|
||||||
} else if (type === 'kml') {
|
|
||||||
callback(toGeoJSON.kml(toDom(c)))
|
|
||||||
} else if (type === 'osm') {
|
|
||||||
let d
|
|
||||||
try {
|
|
||||||
d = JSON.parse(c)
|
|
||||||
} catch (e) {
|
|
||||||
d = toDom(c)
|
|
||||||
}
|
|
||||||
callback(osmtogeojson(d, { flatProperties: true }))
|
|
||||||
} else if (type === 'geojson') {
|
|
||||||
try {
|
|
||||||
const gj = JSON.parse(c)
|
|
||||||
callback(gj)
|
|
||||||
} catch (err) {
|
|
||||||
U.Alert.error(`Invalid JSON file: ${err}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// The choice of the name is not ours, because it is required by Leaflet.
|
// The choice of the name is not ours, because it is required by Leaflet.
|
||||||
// It is misleading, as the returned objects are uMap objects, and not
|
// It is misleading, as the returned objects are uMap objects, and not
|
||||||
// GeoJSON features.
|
// GeoJSON features.
|
||||||
|
@ -1136,10 +1057,12 @@ U.DataLayer = L.Evented.extend({
|
||||||
return new U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this }, id)
|
return new U.Polygon(this.map, latlngs, { geojson: geojson, datalayer: this }, id)
|
||||||
},
|
},
|
||||||
|
|
||||||
importRaw: function (raw, type) {
|
importRaw: async function (raw, format) {
|
||||||
this.addRawData(raw, type)
|
this.map.formatter
|
||||||
|
.parse(raw, format)
|
||||||
|
.then((geojson) => this.addData(geojson))
|
||||||
|
.then(() => this.zoomTo())
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
this.zoomTo()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
importFromFiles: function (files, type) {
|
importFromFiles: function (files, type) {
|
||||||
|
|
|
@ -116,7 +116,7 @@ U.MapPermissions = L.Class.extend({
|
||||||
L._('Advanced actions')
|
L._('Advanced actions')
|
||||||
)
|
)
|
||||||
const advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions)
|
const advancedButtons = L.DomUtil.create('div', 'button-bar', advancedActions)
|
||||||
const download = L.DomUtil.createButton(
|
L.DomUtil.createButton(
|
||||||
'button',
|
'button',
|
||||||
advancedButtons,
|
advancedButtons,
|
||||||
L._('Attach the map to my account'),
|
L._('Attach the map to my account'),
|
||||||
|
|
|
@ -32,7 +32,11 @@ class UmapManifestStaticFilesStorage(ManifestStaticFilesStorage):
|
||||||
'import"%(url)s"\n',
|
'import"%(url)s"\n',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
r"""(?P<matched>import\(["'](?P<url>.*?)["']\))""",
|
r"""(?P<matched>import\(["'](?P<url>.*?)["']\))\.then""",
|
||||||
|
"""import("%(url)s")""",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
r"""(?P<matched>await import\(["'](?P<url>.*?)["']\))""",
|
||||||
"""import("%(url)s")""",
|
"""import("%(url)s")""",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
<script src="{% static 'umap/vendors/minimap/Control.MiniMap.min.js' %}"
|
<script src="{% static 'umap/vendors/minimap/Control.MiniMap.min.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/vendors/csv2geojson/csv2geojson.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/csv2geojson/csv2geojson.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/togeojson/togeojson.umd.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/vendors/osmtogeojson/osmtogeojson.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/osmtogeojson/osmtogeojson.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/loading/Control.Loading.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/loading/Control.Loading.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/markercluster/leaflet.markercluster.js' %}"
|
<script src="{% static 'umap/vendors/markercluster/leaflet.markercluster.js' %}"
|
||||||
|
@ -37,9 +36,7 @@
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/vendors/measurable/Leaflet.Measurable.js' %}"
|
<script src="{% static 'umap/vendors/measurable/Leaflet.Measurable.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/vendors/togpx/togpx.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/vendors/iconlayers/iconLayers.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/iconlayers/iconLayers.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/vendors/tokml/tokml.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/vendors/locatecontrol/L.Control.Locate.min.js' %}"
|
<script src="{% static 'umap/vendors/locatecontrol/L.Control.Locate.min.js' %}"
|
||||||
defer></script>
|
defer></script>
|
||||||
<script src="{% static 'umap/vendors/colorbrewer/colorbrewer.js' %}" defer></script>
|
<script src="{% static 'umap/vendors/colorbrewer/colorbrewer.js' %}" defer></script>
|
||||||
|
@ -55,6 +52,5 @@
|
||||||
<script src="{% static 'umap/js/umap.layer.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.layer.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/umap.share.js' %}" defer></script>
|
|
||||||
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
||||||
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>
|
||||||
|
|
|
@ -218,8 +218,7 @@ def test_gpx_export(map, live_server, bootstrap, page):
|
||||||
download.save_as(path)
|
download.save_as(path)
|
||||||
assert (
|
assert (
|
||||||
path.read_text()
|
path.read_text()
|
||||||
== """<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" version="1.1" creator="togpx"><metadata/><wpt lat="52.57635" lon="-0.274658"><name>test</name><desc>name=test
|
== """<?xml version="1.0" encoding="UTF-8"?><gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="@dwayneparton/geojson-to-gpx"><wpt lat="52.57635" lon="-0.274658"><name>test</name><desc>Some description</desc></wpt><trk><name>test</name><trkseg><trkpt lat="54.476422" lon="-0.571289"/><trkpt lat="54.610255" lon="0.439453"/><trkpt lat="53.448807" lon="1.724854"/><trkpt lat="53.988395" lon="4.163818"/><trkpt lat="53.533778" lon="5.306396"/><trkpt lat="53.709714" lon="6.591797"/><trkpt lat="53.350551" lon="7.042236"/></trkseg></trk></gpx>"""
|
||||||
description=Some description</desc></wpt><trk><name>name poly</name><desc>name=name poly</desc><trkseg><trkpt lat="53.585984" lon="11.25"/><trkpt lat="52.975108" lon="10.151367"/><trkpt lat="52.167194" lon="12.689209"/><trkpt lat="53.199452" lon="14.084473"/><trkpt lat="53.618579" lon="12.634277"/><trkpt lat="53.585984" lon="11.25"/><trkpt lat="53.585984" lon="11.25"/></trkseg></trk><trk><name>test</name><desc>name=test</desc><trkseg><trkpt lat="54.476422" lon="-0.571289"/><trkpt lat="54.610255" lon="0.439453"/><trkpt lat="53.448807" lon="1.724854"/><trkpt lat="53.988395" lon="4.163818"/><trkpt lat="53.533778" lon="5.306396"/><trkpt lat="53.709714" lon="6.591797"/><trkpt lat="53.350551" lon="7.042236"/></trkseg></trk></gpx>"""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,7 +234,7 @@ def test_kml_export(map, live_server, bootstrap, page):
|
||||||
download.save_as(path)
|
download.save_as(path)
|
||||||
assert (
|
assert (
|
||||||
path.read_text()
|
path.read_text()
|
||||||
== """<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><Document><Placemark><name>name poly</name><ExtendedData><Data name="name"><value>name poly</value></Data></ExtendedData><Polygon><outerBoundaryIs><LinearRing><coordinates>11.25,53.585984 10.151367,52.975108 12.689209,52.167194 14.084473,53.199452 12.634277,53.618579 11.25,53.585984 11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark><Placemark><name>test</name><description>Some description</description><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data><Data name="description"><value>Some description</value></Data></ExtendedData><Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark><Placemark><name>test</name><ExtendedData><Data name="_umap_options"><value>[object Object]</value></Data><Data name="name"><value>test</value></Data></ExtendedData><LineString><coordinates>-0.571289,54.476422 0.439453,54.610255 1.724854,53.448807 4.163818,53.988395 5.306396,53.533778 6.591797,53.709714 7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>"""
|
== """<kml xmlns="http://www.opengis.net/kml/2.2"><Document>\n<Placemark id="gyNzM">\n<name>name poly</name><ExtendedData></ExtendedData>\n <Polygon>\n<outerBoundaryIs>\n <LinearRing><coordinates>11.25,53.585984\n10.151367,52.975108\n12.689209,52.167194\n14.084473,53.199452\n12.634277,53.618579\n11.25,53.585984\n11.25,53.585984</coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark>\n<Placemark id="QwNjg">\n<name>test</name><description>Some description</description><ExtendedData>\n <Data name="_umap_options"><value>{"color":"OliveDrab"}</value></Data></ExtendedData>\n <Point><coordinates>-0.274658,52.57635</coordinates></Point></Placemark>\n<Placemark id="YwMTM">\n<name>test</name><ExtendedData>\n <Data name="_umap_options"><value>{"fill":false,"opacity":0.6}</value></Data></ExtendedData>\n <LineString><coordinates>-0.571289,54.476422\n0.439453,54.610255\n1.724854,53.448807\n4.163818,53.988395\n5.306396,53.533778\n6.591797,53.709714\n7.042236,53.350551</coordinates></LineString></Placemark></Document></kml>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue