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; | ||||
|     } | ||||
| 
 | ||||
|     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[][] { | ||||
|         if (layerId === "*") { | ||||
|             return this.GetAllFeaturesWithin(bbox) | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import State from "../../State"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen"; | ||||
| import Translations from "../i18n/Translations"; | ||||
|  | @ -9,11 +8,39 @@ import {DownloadPanel} from "./DownloadPanel"; | |||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Svg from "../../Svg"; | ||||
| 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 { | ||||
| 
 | ||||
|     constructor(isShown: UIEventSource<boolean>) { | ||||
|         super(AllDownloads.GenTitle, AllDownloads.GeneratePanel, "downloads", isShown); | ||||
|     constructor(isShown: UIEventSource<boolean>,state: { | ||||
|         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 { | ||||
|  | @ -22,17 +49,18 @@ export default class AllDownloads extends ScrollableFullScreen { | |||
|             .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 generatePdf = () => { | ||||
|             isExporting.setData(true) | ||||
|             new ExportPDF( | ||||
|                 { | ||||
|                     freeDivId: "belowmap", | ||||
|                     background: State.state.backgroundLayer, | ||||
|                     location: State.state.locationControl, | ||||
|                     features: State.state.featurePipeline, | ||||
|                     layout: State.state.layoutToUse, | ||||
|                     background: state.backgroundLayer, | ||||
|                     location: state.locationControl, | ||||
|                     features: state.featurePipeline, | ||||
|                     layout: state.layoutToUse, | ||||
|                 }).isRunning.addCallbackAndRun(isRunning => isExporting.setData(isRunning)) | ||||
|         } | ||||
| 
 | ||||
|  | @ -57,13 +85,13 @@ export default class AllDownloads extends ScrollableFullScreen { | |||
|                 text), | ||||
|             undefined, | ||||
| 
 | ||||
|             State.state.featureSwitchExportAsPdf | ||||
|             state.featureSwitchExportAsPdf | ||||
|         ) | ||||
| 
 | ||||
|         const exportPanel = new Toggle( | ||||
|             new DownloadPanel(), | ||||
|             new DownloadPanel(state), | ||||
|             undefined, | ||||
|             State.state.featureSwitchEnableExport | ||||
|             state.featureSwitchEnableExport | ||||
|         ) | ||||
| 
 | ||||
|         return new Combine([pdf, exportPanel]).SetClass("flex flex-col"); | ||||
|  |  | |||
|  | @ -13,15 +13,16 @@ import {UIEventSource} from "../../Logic/UIEventSource"; | |||
| import SimpleMetaTagger from "../../Logic/SimpleMetaTagger"; | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | ||||
| import {BBox} from "../../Logic/BBox"; | ||||
| import FilteredLayer, {FilterState} from "../../Models/FilteredLayer"; | ||||
| 
 | ||||
| export class DownloadPanel extends Toggle { | ||||
| 
 | ||||
|     constructor() { | ||||
|         const state: { | ||||
|             featurePipeline: FeaturePipeline, | ||||
|             layoutToUse: LayoutConfig, | ||||
|             currentBounds: UIEventSource<BBox> | ||||
|         } = State.state | ||||
|     constructor(state: { | ||||
|         filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|         featurePipeline: FeaturePipeline, | ||||
|         layoutToUse: LayoutConfig, | ||||
|         currentBounds: UIEventSource<BBox> | ||||
|     }) { | ||||
| 
 | ||||
| 
 | ||||
|         const t = Translations.t.general.download | ||||
|  | @ -34,7 +35,7 @@ export class DownloadPanel extends Toggle { | |||
|         const buttonGeoJson = new SubtleButton(Svg.floppy_ui(), | ||||
|             new Combine([t.downloadGeojson.Clone().SetClass("font-bold"), | ||||
|                 t.downloadGeoJsonHelper.Clone()]).SetClass("flex flex-col")) | ||||
|             .onClick(() => { | ||||
|             .OnClickWithLoading(t.exporting,async () => { | ||||
|                 const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) | ||||
|                 Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson, null, "  "), | ||||
|                     `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( | ||||
|             [t.downloadCSV.Clone().SetClass("font-bold"), | ||||
|                 t.downloadCSVHelper.Clone()]).SetClass("flex flex-col")) | ||||
|             .onClick(() => { | ||||
|             .OnClickWithLoading(t.exporting, async () => { | ||||
|                 const geojson = DownloadPanel.getCleanGeoJson(state, metaisIncluded.data) | ||||
|                 const csv = GeoOperations.toCSV(geojson.features) | ||||
| 
 | ||||
|  | @ -72,13 +73,41 @@ export class DownloadPanel extends Toggle { | |||
| 
 | ||||
|     private static getCleanGeoJson(state: { | ||||
|         featurePipeline: FeaturePipeline, | ||||
|         currentBounds: UIEventSource<BBox> | ||||
|         currentBounds: UIEventSource<BBox>, | ||||
|         filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|     }, includeMetaData: boolean) { | ||||
| 
 | ||||
|         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 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 = { | ||||
|                     type: feature.type, | ||||
|                     geometry: feature.geometry, | ||||
|  |  | |||
|  | @ -71,7 +71,8 @@ export default class LeftControls extends Combine { | |||
| 
 | ||||
|         const toggledDownload = new Toggle( | ||||
|             new AllDownloads( | ||||
|                 guiState.downloadControlIsOpened | ||||
|                 guiState.downloadControlIsOpened, | ||||
|                 state | ||||
|             ).SetClass("block p-1 rounded-full md:floating-element-width"), | ||||
|             new MapControlButton(Svg.download_svg()) | ||||
|                 .onClick(() => guiState.downloadControlIsOpened.setData(true)), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue