forked from MapComplete/MapComplete
Exporting CSV/Geojson now respects filters + refactoring away State.state
This commit is contained in:
parent
5696b80a5e
commit
f0675a026b
4 changed files with 97 additions and 23 deletions
|
@ -345,6 +345,22 @@ export default class FeaturePipeline {
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GetAllFeaturesAndMetaWithin(bbox: BBox, layerIdWhitelist?: Set<string>): {features: any[], layer: string}[] {
|
||||||
|
const self = this
|
||||||
|
const tiles :{features: any[], layer: string}[]= []
|
||||||
|
Array.from(this.perLayerHierarchy.keys())
|
||||||
|
.forEach(key => {
|
||||||
|
if(layerIdWhitelist !== undefined && !layerIdWhitelist.has(key)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return tiles.push({
|
||||||
|
layer: key,
|
||||||
|
features: [].concat(...self.GetFeaturesWithin(key, bbox))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
return tiles;
|
||||||
|
}
|
||||||
|
|
||||||
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
|
public GetFeaturesWithin(layerId: string, bbox: BBox): any[][] {
|
||||||
if (layerId === "*") {
|
if (layerId === "*") {
|
||||||
return this.GetAllFeaturesWithin(bbox)
|
return this.GetAllFeaturesWithin(bbox)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import State from "../../State";
|
|
||||||
import Combine from "../Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
||||||
import Translations from "../i18n/Translations";
|
import Translations from "../i18n/Translations";
|
||||||
|
@ -9,11 +8,39 @@ import {DownloadPanel} from "./DownloadPanel";
|
||||||
import {SubtleButton} from "../Base/SubtleButton";
|
import {SubtleButton} from "../Base/SubtleButton";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import ExportPDF from "../ExportPDF";
|
import ExportPDF from "../ExportPDF";
|
||||||
|
import FilteredLayer from "../../Models/FilteredLayer";
|
||||||
|
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline";
|
||||||
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
|
import {BBox} from "../../Logic/BBox";
|
||||||
|
import BaseLayer from "../../Models/BaseLayer";
|
||||||
|
import Loc from "../../Models/Loc";
|
||||||
|
|
||||||
|
interface DownloadState {
|
||||||
|
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
|
featurePipeline: FeaturePipeline,
|
||||||
|
layoutToUse: LayoutConfig,
|
||||||
|
currentBounds: UIEventSource<BBox>,
|
||||||
|
backgroundLayer:UIEventSource<BaseLayer>,
|
||||||
|
locationControl: UIEventSource<Loc>,
|
||||||
|
featureSwitchExportAsPdf: UIEventSource<boolean>,
|
||||||
|
featureSwitchEnableExport: UIEventSource<boolean>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class AllDownloads extends ScrollableFullScreen {
|
export default class AllDownloads extends ScrollableFullScreen {
|
||||||
|
|
||||||
constructor(isShown: UIEventSource<boolean>) {
|
constructor(isShown: UIEventSource<boolean>,state: {
|
||||||
super(AllDownloads.GenTitle, AllDownloads.GeneratePanel, "downloads", isShown);
|
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
|
featurePipeline: FeaturePipeline,
|
||||||
|
layoutToUse: LayoutConfig,
|
||||||
|
currentBounds: UIEventSource<BBox>,
|
||||||
|
backgroundLayer:UIEventSource<BaseLayer>,
|
||||||
|
locationControl: UIEventSource<Loc>,
|
||||||
|
featureSwitchExportAsPdf: UIEventSource<boolean>,
|
||||||
|
featureSwitchEnableExport: UIEventSource<boolean>,
|
||||||
|
}) {
|
||||||
|
super(AllDownloads.GenTitle, () => AllDownloads.GeneratePanel(state), "downloads", isShown);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GenTitle(): BaseUIElement {
|
private static GenTitle(): BaseUIElement {
|
||||||
|
@ -22,17 +49,18 @@ export default class AllDownloads extends ScrollableFullScreen {
|
||||||
.SetClass("text-2xl break-words font-bold p-2");
|
.SetClass("text-2xl break-words font-bold p-2");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GeneratePanel(): BaseUIElement {
|
private static GeneratePanel(state: DownloadState): BaseUIElement {
|
||||||
|
|
||||||
const isExporting = new UIEventSource(false, "Pdf-is-exporting")
|
const isExporting = new UIEventSource(false, "Pdf-is-exporting")
|
||||||
const generatePdf = () => {
|
const generatePdf = () => {
|
||||||
isExporting.setData(true)
|
isExporting.setData(true)
|
||||||
new ExportPDF(
|
new ExportPDF(
|
||||||
{
|
{
|
||||||
freeDivId: "belowmap",
|
freeDivId: "belowmap",
|
||||||
background: State.state.backgroundLayer,
|
background: state.backgroundLayer,
|
||||||
location: State.state.locationControl,
|
location: state.locationControl,
|
||||||
features: State.state.featurePipeline,
|
features: state.featurePipeline,
|
||||||
layout: State.state.layoutToUse,
|
layout: state.layoutToUse,
|
||||||
}).isRunning.addCallbackAndRun(isRunning => isExporting.setData(isRunning))
|
}).isRunning.addCallbackAndRun(isRunning => isExporting.setData(isRunning))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,13 +85,13 @@ export default class AllDownloads extends ScrollableFullScreen {
|
||||||
text),
|
text),
|
||||||
undefined,
|
undefined,
|
||||||
|
|
||||||
State.state.featureSwitchExportAsPdf
|
state.featureSwitchExportAsPdf
|
||||||
)
|
)
|
||||||
|
|
||||||
const exportPanel = new Toggle(
|
const exportPanel = new Toggle(
|
||||||
new DownloadPanel(),
|
new DownloadPanel(state),
|
||||||
undefined,
|
undefined,
|
||||||
State.state.featureSwitchEnableExport
|
state.featureSwitchEnableExport
|
||||||
)
|
)
|
||||||
|
|
||||||
return new Combine([pdf, exportPanel]).SetClass("flex flex-col");
|
return new Combine([pdf, exportPanel]).SetClass("flex flex-col");
|
||||||
|
|
|
@ -13,15 +13,16 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import SimpleMetaTagger from "../../Logic/SimpleMetaTagger";
|
import SimpleMetaTagger from "../../Logic/SimpleMetaTagger";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
import {BBox} from "../../Logic/BBox";
|
import {BBox} from "../../Logic/BBox";
|
||||||
|
import FilteredLayer, {FilterState} from "../../Models/FilteredLayer";
|
||||||
|
|
||||||
export class DownloadPanel extends Toggle {
|
export class DownloadPanel extends Toggle {
|
||||||
|
|
||||||
constructor() {
|
constructor(state: {
|
||||||
const state: {
|
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
featurePipeline: FeaturePipeline,
|
featurePipeline: FeaturePipeline,
|
||||||
layoutToUse: LayoutConfig,
|
layoutToUse: LayoutConfig,
|
||||||
currentBounds: UIEventSource<BBox>
|
currentBounds: UIEventSource<BBox>
|
||||||
} = State.state
|
}) {
|
||||||
|
|
||||||
|
|
||||||
const t = Translations.t.general.download
|
const t = Translations.t.general.download
|
||||||
|
@ -34,7 +35,7 @@ export class DownloadPanel extends Toggle {
|
||||||
const buttonGeoJson = new SubtleButton(Svg.floppy_ui(),
|
const buttonGeoJson = new SubtleButton(Svg.floppy_ui(),
|
||||||
new Combine([t.downloadGeojson.Clone().SetClass("font-bold"),
|
new Combine([t.downloadGeojson.Clone().SetClass("font-bold"),
|
||||||
t.downloadGeoJsonHelper.Clone()]).SetClass("flex flex-col"))
|
t.downloadGeoJsonHelper.Clone()]).SetClass("flex flex-col"))
|
||||||
.onClick(() => {
|
.OnClickWithLoading(t.exporting,async () => {
|
||||||
const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data)
|
const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data)
|
||||||
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "),
|
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, " "),
|
||||||
`MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.geojson`, {
|
`MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.geojson`, {
|
||||||
|
@ -46,7 +47,7 @@ export class DownloadPanel extends Toggle {
|
||||||
const buttonCSV = new SubtleButton(Svg.floppy_ui(), new Combine(
|
const buttonCSV = new SubtleButton(Svg.floppy_ui(), new Combine(
|
||||||
[t.downloadCSV.Clone().SetClass("font-bold"),
|
[t.downloadCSV.Clone().SetClass("font-bold"),
|
||||||
t.downloadCSVHelper.Clone()]).SetClass("flex flex-col"))
|
t.downloadCSVHelper.Clone()]).SetClass("flex flex-col"))
|
||||||
.onClick(() => {
|
.OnClickWithLoading(t.exporting, async () => {
|
||||||
const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data)
|
const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data)
|
||||||
const csv = GeoOperations.toCSV(geojson.features)
|
const csv = GeoOperations.toCSV(geojson.features)
|
||||||
|
|
||||||
|
@ -72,13 +73,41 @@ export class DownloadPanel extends Toggle {
|
||||||
|
|
||||||
private static getCleanGeoJson(state: {
|
private static getCleanGeoJson(state: {
|
||||||
featurePipeline: FeaturePipeline,
|
featurePipeline: FeaturePipeline,
|
||||||
currentBounds: UIEventSource<BBox>
|
currentBounds: UIEventSource<BBox>,
|
||||||
|
filteredLayers: UIEventSource<FilteredLayer[]>
|
||||||
}, includeMetaData: boolean) {
|
}, includeMetaData: boolean) {
|
||||||
|
|
||||||
const resultFeatures = []
|
const resultFeatures = []
|
||||||
const featureList = state.featurePipeline.GetAllFeaturesWithin(state.currentBounds.data);
|
const neededLayers = state.filteredLayers.data.map(l => l.layerDef.id)
|
||||||
|
const bbox = state.currentBounds.data
|
||||||
|
const featureList = state.featurePipeline.GetAllFeaturesAndMetaWithin(bbox, new Set(neededLayers));
|
||||||
for (const tile of featureList) {
|
for (const tile of featureList) {
|
||||||
for (const feature of tile) {
|
const layer = state.filteredLayers.data.find(fl => fl.layerDef.id === tile.layer)
|
||||||
|
const filters = layer.appliedFilters.data
|
||||||
|
for (const feature of tile.features) {
|
||||||
|
|
||||||
|
if(!bbox.overlapsWith(BBox.get(feature))){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (filters !== undefined) {
|
||||||
|
let featureDoesMatchAllFilters = true;
|
||||||
|
for (let key of Array.from(filters.keys())) {
|
||||||
|
const filter: FilterState = filters.get(key)
|
||||||
|
if(filter?.currentFilter === undefined){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!filter.currentFilter.matchesProperties(feature.properties)) {
|
||||||
|
featureDoesMatchAllFilters = false;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!featureDoesMatchAllFilters){
|
||||||
|
continue; // the outer loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cleaned = {
|
const cleaned = {
|
||||||
type: feature.type,
|
type: feature.type,
|
||||||
geometry: feature.geometry,
|
geometry: feature.geometry,
|
||||||
|
|
|
@ -71,7 +71,8 @@ export default class LeftControls extends Combine {
|
||||||
|
|
||||||
const toggledDownload = new Toggle(
|
const toggledDownload = new Toggle(
|
||||||
new AllDownloads(
|
new AllDownloads(
|
||||||
guiState.downloadControlIsOpened
|
guiState.downloadControlIsOpened,
|
||||||
|
state
|
||||||
).SetClass("block p-1 rounded-full md:floating-element-width"),
|
).SetClass("block p-1 rounded-full md:floating-element-width"),
|
||||||
new MapControlButton(Svg.download_svg())
|
new MapControlButton(Svg.download_svg())
|
||||||
.onClick(() => guiState.downloadControlIsOpened.setData(true)),
|
.onClick(() => guiState.downloadControlIsOpened.setData(true)),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue