diff --git a/index.php b/index.php index 4cc57487..a9ca721c 100644 --- a/index.php +++ b/index.php @@ -65,6 +65,7 @@ html_export_var(array(
OpenStreet Browser
'>
+
diff --git a/lang/ast.json b/lang/ast.json index 6792c3f9..14e3629d 100644 --- a/lang/ast.json +++ b/lang/ast.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/ca.json b/lang/ca.json index 92fbcbad..09abd388 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/cs.json b/lang/cs.json index 6a00b20d..2a7bdff8 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/da.json b/lang/da.json index 246c041a..1a01e3e8 100644 --- a/lang/da.json +++ b/lang/da.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/de.json b/lang/de.json index 36a8c64e..76e55319 100644 --- a/lang/de.json +++ b/lang/de.json @@ -3,12 +3,13 @@ "category-info-tooltip": "Info & Legende", "closed": "geschlossen", "default": "Standard", - "download:geojson": "Als GeoJSON runterladen", "edit": "bearbeiten", "error": { "message": "Fehler", "!=1": "Fehler" }, + "export:GeoJSON": "Als GeoJSON runterladen", + "export:OSMXML": null, "facilities": "Einrichtungen", "header:attributes": "Attribute", "header:export": "Export", diff --git a/lang/el.json b/lang/el.json index 92acb3fc..82412698 100644 --- a/lang/el.json +++ b/lang/el.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/en.json b/lang/en.json index 62a959c0..1791c258 100644 --- a/lang/en.json +++ b/lang/en.json @@ -3,12 +3,15 @@ "category-info-tooltip": "Info & Map key", "closed": "closed", "default": "default", - "download:geojson": "Download as GeoJSON", "edit": "edit", "error": { "message": "Error", "!=1": "Errors" }, + "export-all": "Export all visible map features", + "export-prepare": "Prepare download", + "export:GeoJSON": "Download as GeoJSON", + "export:OSMXML": "Download as OSMXML", "facilities": "Facilities", "header:attributes": "Attributes", "header:export": "Export", diff --git a/lang/es.json b/lang/es.json index 62837550..4f34b83a 100644 --- a/lang/es.json +++ b/lang/es.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/et.json b/lang/et.json index 9e346130..2cfc0df7 100644 --- a/lang/et.json +++ b/lang/et.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/fr.json b/lang/fr.json index 2e9720c6..afd65d26 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -3,9 +3,10 @@ "category-info-tooltip": "Info & Légende", "closed": "Fermé", "default": "Par défaut", - "download:geojson": "Télécharger comme GeoJSON", "edit": "éditer", "error": "Erreur", + "export:GeoJSON": "Télécharger comme GeoJSON", + "export:OSMXML": null, "facilities": "Aménagements", "header:attributes": "Attributs", "header:export": "Export", diff --git a/lang/hu.json b/lang/hu.json index 49fe6e02..c8a12808 100644 --- a/lang/hu.json +++ b/lang/hu.json @@ -3,9 +3,10 @@ "category-info-tooltip": "Információk és jelmagyarázat", "closed": "Lezárva", "default": "Alapértelmezett", - "download:geojson": "Letöltés GeoJSON formátumban", "edit": "Szerkesztés", "error": "Hiba", + "export:GeoJSON": "Letöltés GeoJSON formátumban", + "export:OSMXML": null, "facilities": "Létesítmények", "header:attributes": "Tulajdonságok", "header:export": "Exportálás", diff --git a/lang/it.json b/lang/it.json index caa1f358..f100e93c 100644 --- a/lang/it.json +++ b/lang/it.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/ja.json b/lang/ja.json index d1d9466f..9685ac1b 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/nl.json b/lang/nl.json index 0c555511..ef7ccbee 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/pl.json b/lang/pl.json index 2dbff807..bf0c7120 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/pt-br.json b/lang/pt-br.json index 008e8b05..607fc336 100644 --- a/lang/pt-br.json +++ b/lang/pt-br.json @@ -3,12 +3,13 @@ "category-info-tooltip": "Info & Legenda", "closed": "fechado", "default": "padrão", - "download:geojson": "Baixar como GeoJSON", "edit": "editar", "error": { "message": "Erro", "!=1": "Erros" }, + "export:GeoJSON": "Baixar como GeoJSON", + "export:OSMXML": null, "facilities": "Instalações", "header:attributes": "Atributos", "header:export": "Exportar", diff --git a/lang/pt.json b/lang/pt.json index 42293725..372bc6f0 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -3,12 +3,13 @@ "category-info-tooltip": "Info & Legenda", "closed": "fechado", "default": "padrão", - "download:geojson": "Descarregar como GeoJSON", "edit": "editar", "error": { "message": "Erro", "!=1": "Erros" }, + "export:GeoJSON": "Descarregar como GeoJSON", + "export:OSMXML": null, "facilities": "Instalações", "header:attributes": "Atributos", "header:export": "Exportar", diff --git a/lang/ro.json b/lang/ro.json index 393b5e82..46d1fe29 100644 --- a/lang/ro.json +++ b/lang/ro.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/ru.json b/lang/ru.json index 1c8389ad..fc7c04a8 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/sr.json b/lang/sr.json index b53e0904..d07af30e 100644 --- a/lang/sr.json +++ b/lang/sr.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/template.json b/lang/template.json index 09c4e48c..16e3970e 100644 --- a/lang/template.json +++ b/lang/template.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lang/uk.json b/lang/uk.json index 11018c7e..8f514591 100644 --- a/lang/uk.json +++ b/lang/uk.json @@ -3,9 +3,10 @@ "category-info-tooltip": "", "closed": "", "default": "", - "download:geojson": "", "edit": "", "error": "", + "export:GeoJSON": "", + "export:OSMXML": null, "facilities": "", "header:attributes": "", "header:export": "", diff --git a/lib/modulekit/form b/lib/modulekit/form index 8a2d3015..5d39b2f6 160000 --- a/lib/modulekit/form +++ b/lib/modulekit/form @@ -1 +1 @@ -Subproject commit 8a2d3015ec181b14441037a79e7db3869453b135 +Subproject commit 5d39b2f61b7eda9a635414acaeb3c4e0dc524490 diff --git a/package.json b/package.json index 39329b25..5d6c1690 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "babel-core": "^6.26.0", "babel-preset-env": "^1.6.1", "color-interpolate": "^1.0.2", + "file-saver": "^2.0.0", "i18next-client": "^1.11.4", "ip-location": "^1.0.1", "json-multiline-strings": "^0.1.0", diff --git a/src/CategoryBase.js b/src/CategoryBase.js index 7b7a489f..261767b2 100644 --- a/src/CategoryBase.js +++ b/src/CategoryBase.js @@ -204,4 +204,8 @@ CategoryBase.prototype.notifyChildLoadEnd = function (category) { } } +CategoryBase.prototype.allMapFeatures = function (callback) { + callback(null, []) +} + module.exports = CategoryBase diff --git a/src/CategoryIndex.js b/src/CategoryIndex.js index dc2d0828..0a44e85c 100644 --- a/src/CategoryIndex.js +++ b/src/CategoryIndex.js @@ -106,5 +106,24 @@ CategoryIndex.prototype.toggleCategory = function (id) { }.bind(this)) } +CategoryIndex.prototype.allMapFeatures = function (callback) { + let result = [] + + async.each(this.childrenCategories, + (category, done) => category.allMapFeatures( + (err, data) => { + if (err) { + return done(err) + } + + result = result.concat(data) + + global.setTimeout(done, 0) + } + ), + (err) => callback(err, result) + ) +} + OpenStreetBrowserLoader.registerType('index', CategoryIndex) module.exports = CategoryIndex diff --git a/src/CategoryOverpass.js b/src/CategoryOverpass.js index 2b8fd58f..35e79922 100644 --- a/src/CategoryOverpass.js +++ b/src/CategoryOverpass.js @@ -491,5 +491,13 @@ CategoryOverpass.prototype.renderTemplate = function (object, templateId, callba }) } +CategoryOverpass.prototype.allMapFeatures = function (callback) { + if (!this.isOpen) { + return callback(null, []) + } + + callback(null, Object.values(this.layer.mainlayer.visibleFeatures)) +} + OpenStreetBrowserLoader.registerType('overpass', CategoryOverpass) module.exports = CategoryOverpass diff --git a/src/ExportGeoJSON.js b/src/ExportGeoJSON.js new file mode 100644 index 00000000..4a9434f5 --- /dev/null +++ b/src/ExportGeoJSON.js @@ -0,0 +1,34 @@ +class ExportGeoJSON { + constructor (conf) { + this.conf = conf + } + + each (ob, callback) { + ob.object.exportGeoJSON(this.conf, callback) + } + + finishOne (object) { + return { + content: JSON.stringify(object, null, ' '), + fileType: 'application/json', + extension: 'geojson' + } + } + + finish (list) { + if (list.length) { + list = { + type: 'FeatureCollection', + features: list + } + } + + return { + content: JSON.stringify(list, null, ' '), + fileType: 'application/json', + extension: 'geojson' + } + } +} + +module.exports = ExportGeoJSON diff --git a/src/ExportOSMJSON.js b/src/ExportOSMJSON.js new file mode 100644 index 00000000..1929c478 --- /dev/null +++ b/src/ExportOSMJSON.js @@ -0,0 +1,24 @@ +class ExportOSMXML { + constructor (conf) { + this.conf = conf + this.elements = {} + } + + each (ob, callback) { + ob.object.exportOSMJSON(this.conf, this.elements, callback) + } + + finish (list) { + return { + content: JSON.stringify({ + version: '0.6', + generator: 'OpenStreetBrowser', + elements: Object.values(this.elements) + }, null, ' '), + fileType: 'application/json', + extension: 'osm.json' + } + } +} + +module.exports = ExportOSMXML diff --git a/src/ExportOSMXML.js b/src/ExportOSMXML.js new file mode 100644 index 00000000..9858b6e0 --- /dev/null +++ b/src/ExportOSMXML.js @@ -0,0 +1,20 @@ +class ExportOSMXML { + constructor (conf) { + this.conf = conf + this.parentNode = document.createElement('osm') + } + + each (ob, callback) { + ob.object.exportOSMXML(this.conf, this.parentNode, callback) + } + + finish (list) { + return { + content: '' + this.parentNode.innerHTML + '', + fileType: 'application/xml', + extension: 'osm.xml' + } + } +} + +module.exports = ExportOSMXML diff --git a/src/chunkSplit.js b/src/chunkSplit.js new file mode 100644 index 00000000..c850b879 --- /dev/null +++ b/src/chunkSplit.js @@ -0,0 +1,9 @@ +module.exports = function chunkSplit (data, size=1000) { + let result = [] + + for (let i = 0; i < data.length; i += size) { + result.push(data.slice(i, i + size)) + } + + return result +} diff --git a/src/exportAll.js b/src/exportAll.js new file mode 100644 index 00000000..4cf06bca --- /dev/null +++ b/src/exportAll.js @@ -0,0 +1,148 @@ +const tabs = require('modulekit-tabs') +const async = require('async') +const FileSaver = require('file-saver') + +const chunkSplit = require('./chunkSplit') + +const types = { + GeoJSON: require('./ExportGeoJSON'), + OSMXML: require('./ExportOSMXML'), + OSMJSON: require('./ExportOSMJSON') +} + +let tab +let formExport + +function prepareDownload (callback) { + let conf = formExport.get_data() + + global.baseCategory.allMapFeatures((err, data) => { + if (err) { + return callback(err) + } + + createDownload(conf, data, callback) + }) +} + +function createDownload (conf, data, callback) { + let type = types[conf.type] + let exportFun = new type(conf) + + let chunks = chunkSplit(data, 1000) + let parentNode + + async.mapLimit( + chunks, + 1, + (chunk, done) => { + async.map(chunk, + (ob, done) => exportFun.each(ob, done), + (err, result) => { + global.setTimeout(() => done(err, result), 0) + } + ) + }, + (err, data) => { + if (err) { + return callback(err) + } + + data = data.reduce((all, chunk) => all.concat(chunk)) + + let result = exportFun.finish(data) + + var blob = new Blob([ result.content ], { type: result.fileType + ';charset=utf-8' }) + FileSaver.saveAs(blob, 'openstreetbrowser.' + result.extension) + + callback() + } + ) +} + +function formDef () { + let values = {} + Object.keys(types).forEach(type => + values[type] = lang('export:' + type) + ) + + return { + type: { + name: 'Type', + type: 'radio', + values, + default: Object.keys(types)[0] + } + } +} + +register_hook('init', function () { + tab = new tabs.Tab({ + id: 'export' + }) + global.tabs.add(tab) + + tab.header.innerHTML = '' + tab.header.title = lang('export-all') + tab.content.innerHTML = '

' + lang('export-all') + '

' + + formExport = new form('export', formDef()) + + let domForm = document.createElement('form') + tab.content.appendChild(domForm) + formExport.show(domForm) + + let submit = document.createElement('input') + submit.type = 'submit' + submit.value = lang('export-prepare') + submit.onclick = () => { + let progressIndicator = document.createElement('div') + progressIndicator.innerHTML = ' ' + lang('loading') + tab.content.appendChild(progressIndicator) + submit.style.display = 'none' + + prepareDownload((err) => { + if (err) { + alert(err) + } + + submit.style.display = 'block' + tab.content.removeChild(progressIndicator) + tab.unselect() + }) + } + tab.content.appendChild(submit) + + tab.on('select', () => { + formExport.resize() + }) +}) + +module.exports = (data, div) => { + let formExport = new form('exportOne', formDef()) + + let domForm = document.createElement('form') + div.appendChild(domForm) + formExport.show(domForm) + + let submit = document.createElement('input') + submit.type = 'submit' + submit.value = lang('export-prepare') + submit.onclick = () => { + let progressIndicator = document.createElement('div') + progressIndicator.innerHTML = ' ' + lang('loading') + div.appendChild(progressIndicator) + submit.style.display = 'none' + + let conf = formExport.get_data() + createDownload(conf, [ data ], (err) => { + if (err) { + alert(err) + } + + submit.style.display = 'block' + div.removeChild(progressIndicator) + }) + } + div.appendChild(submit) +} diff --git a/src/index.js b/src/index.js index 0dab30bf..ebf7bff7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ /* globals map:true, overpassFrontend:true, currentPath:true, options:true, baseCategory:true, overpassUrl:true showDetails */ var LeafletGeoSearch = require('leaflet-geosearch') +const tabs = require('modulekit-tabs') var OverpassFrontend = require('overpass-frontend') var OpenStreetBrowserLoader = require('./OpenStreetBrowserLoader') @@ -17,6 +18,7 @@ global.baseCategory = null global.overpassUrl = null global.overpassFrontend = null global.currentPath = null +global.tabs = null var lastPopupClose = 0 // Optional modules @@ -31,6 +33,7 @@ require('./categories') require('./wikipedia') require('./image') require('./addCategories') +let exportAll = require('./exportAll') window.onload = function () { var initState = config.defaultView @@ -42,6 +45,8 @@ window.onload = function () { options = {} } + global.tabs = new tabs.Tabs(document.getElementById('globalTabs')) + call_hooks('init') call_hooks_callback('init_callback', initState, onload2.bind(this, initState)) @@ -163,6 +168,10 @@ function onload2 (initState) { call_hooks('initFinish') } +global.allMapFeatures = function (callback) { + global.baseCategory.allMapFeatures(callback) +} + window.setPath = function (path, state) { currentPath = path @@ -285,17 +294,9 @@ window.showDetails = function (data, category) { h.innerHTML = lang('header:export') dom.appendChild(h) - div = document.createElement('ul') + div = document.createElement('div') dom.appendChild(div) - - li = document.createElement('li') - div.appendChild(li) - - a = document.createElement('a') - a.download = data.id + '.json' - a.href = 'data:application/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(data.object.GeoJSON(), null, ' ')) - a.innerHTML = lang('download:geojson') - li.appendChild(a) + exportAll(data, div) h = document.createElement('h3') h.innerHTML = lang('header:attributes') diff --git a/style.css b/style.css index ddc416e2..ee0a8534 100644 --- a/style.css +++ b/style.css @@ -77,6 +77,11 @@ a:active { font-size: 16px; } +#sidebar > #globalTabs { + padding-left: 10px; + padding-right: 10px; +} + #sidebar > #content { flex: 1; flex-shrink: 0;