diff --git a/InitUiElements.ts b/InitUiElements.ts index 0dbc7eaac7..bc804a2294 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -427,6 +427,8 @@ export class InitUiElements { state.locationControl, state.selectedElement); + State.state.featurePipeline = source; + new ShowDataLayer(source.features, State.state.leafletMap, State.state.layoutToUse); const selectedFeatureHandler = new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source, State.state.osmApiFeatureSource); diff --git a/Logic/FeatureSource/GeoJsonExport.ts b/Logic/FeatureSource/GeoJsonExport.ts new file mode 100644 index 0000000000..541e103730 --- /dev/null +++ b/Logic/FeatureSource/GeoJsonExport.ts @@ -0,0 +1,39 @@ +import FeaturePipeline from "./FeaturePipeline"; +import {Utils} from "../../Utils"; + +/** + * Exports given featurePipeline as a geojson FeatureLists (downloads as a json) + * @param featurePipeline The FeaturePipeline you want to export + * @param options The options object + * @param options.metadata True if you want to include the MapComplete metadata, false otherwise + */ +export function exportAsGeoJson(featurePipeline: FeaturePipeline, options: { metadata?: boolean} = {}) { + let defaults = { + metadata: false, + } + options = Utils.setDefaults(options, defaults); + + // Select all features, ignore the freshness and other data + let featureList: JSON[] = featurePipeline ? featurePipeline.features.data.map((feature) => feature.feature) : ["I'm empty"]; + + /** + * Removes the metadata of MapComplete (all properties starting with an underscore) + * @param featureList JsonList containing features, output object + */ + function removeMetaData(featureList: JSON[]) { + for (let i=0; i < featureList.length; i++) { + let feature = featureList[i]; + for (let property in feature.properties) { + if (property[0] == "_") { + delete featureList[i]["properties"][property]; + } + } + } + } + + if (!options.metadata) removeMetaData(featureList); + + let geojson = {type: "FeatureCollection", features: featureList} + + Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), "Geodata.json"); +} diff --git a/State.ts b/State.ts index 8e4322d654..7793485aa3 100644 --- a/State.ts +++ b/State.ts @@ -19,6 +19,7 @@ import TitleHandler from "./Logic/Actors/TitleHandler"; import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; import {Relation} from "./Logic/Osm/ExtractRelations"; import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; +import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; /** * Contains the global state: a bunch of UI-event sources @@ -95,6 +96,7 @@ export default class State { public readonly featureSwitchIsDebugging: UIEventSource; public readonly featureSwitchShowAllQuestions: UIEventSource; public readonly featureSwitchApiURL: UIEventSource; + public readonly featurePipeline: FeaturePipeline; /** diff --git a/UI/BigComponents/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts index e282947096..c48b361633 100644 --- a/UI/BigComponents/LayerSelection.ts +++ b/UI/BigComponents/LayerSelection.ts @@ -7,6 +7,8 @@ import Translations from "../i18n/Translations"; import LayerConfig from "../../Customizations/JSON/LayerConfig"; import BaseUIElement from "../BaseUIElement"; import {Translation} from "../i18n/Translation"; +import {SubtleButton} from "../Base/SubtleButton"; +import {exportAsGeoJson} from "../../Logic/FeatureSource/GeoJsonExport"; /** * Shows the panel with all layers and a toggle for each of them @@ -74,6 +76,9 @@ export default class LayerSelection extends Combine { ); } + const downloadButton = new SubtleButton("./assets/svg/floppy.svg", Translations.t.general.layerSelection.downloadGeojson.Clone()) + downloadButton.onClick(() => exportAsGeoJson(State.state.featurePipeline)) + checkboxes.push(downloadButton) super(checkboxes) this.SetStyle("display:flex;flex-direction:column;") diff --git a/Utils.ts b/Utils.ts index cb88356569..f16f987af2 100644 --- a/Utils.ts +++ b/Utils.ts @@ -448,5 +448,12 @@ export class Utils { b: parseInt(hex.substr(5, 2), 16), } } + + public static setDefaults(options, defaults){ + for (let key in defaults){ + if (!(key in options)) options[key] = defaults[key]; + } + return options; + } } diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index f82b5fb71f..2d0848c432 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -594,5 +594,15 @@ "sources": [ "https://www.mapillary.com/" ] + }, + { + "authors": [ + "The Tango! Desktop Project" + ], + "path": "floppy.svg", + "license": "CC0", + "sources": [ + "https://commons.wikimedia.org/wiki/File:Media-floppy.svg" + ] } ] \ No newline at end of file diff --git a/langs/en.json b/langs/en.json index 7f327653c5..b604850f8f 100644 --- a/langs/en.json +++ b/langs/en.json @@ -147,7 +147,8 @@ "loginOnlyNeededToEdit": "if you want to edit the map", "layerSelection": { "zoomInToSeeThisLayer": "Zoom in to see this layer", - "title": "Select layers" + "title": "Select layers", + "downloadGeojson": "Download layer features as geojson" }, "weekdays": { "abbreviations": {