Exporting CSV/Geojson now respects filters + refactoring away State.state

This commit is contained in:
Pieter Vander Vennet 2022-02-16 01:34:28 +01:00
parent 5696b80a5e
commit f0675a026b
4 changed files with 97 additions and 23 deletions

View file

@ -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)

View file

@ -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");

View file

@ -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,

View file

@ -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)),