forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			115 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			115 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import FeaturePipelineState from "../Logic/State/FeaturePipelineState";
 | |
| import MinimapImplementation from "../UI/Base/MinimapImplementation";
 | |
| import {UIEventSource} from "../Logic/UIEventSource";
 | |
| import Loc from "../Models/Loc";
 | |
| import ShowDataLayer from "../UI/ShowDataLayer/ShowDataLayer";
 | |
| import {BBox} from "../Logic/BBox";
 | |
| import Minimap from "../UI/Base/Minimap";
 | |
| import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers";
 | |
| import {Utils} from "../Utils";
 | |
| 
 | |
| export interface PngMapCreatorOptions{
 | |
|     readonly divId: string; readonly width: number; readonly height: number; readonly scaling?: 1 | number,
 | |
|     readonly dummyMode?: boolean
 | |
| }
 | |
| 
 | |
| export class PngMapCreator {
 | |
|     private readonly _state: FeaturePipelineState | undefined;
 | |
|     private readonly _options: PngMapCreatorOptions;
 | |
| 
 | |
|     constructor(state: FeaturePipelineState | undefined, options: PngMapCreatorOptions) {
 | |
|         this._state = state;
 | |
|         this._options = {...options, scaling: options.scaling ?? 1};
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a minimap, waits till all needed tiles are loaded before returning
 | |
|      * @private
 | |
|      */
 | |
|     private async createAndLoadMinimap(): Promise<MinimapImplementation> {
 | |
|         const state = this._state;
 | |
|         const options = this._options
 | |
|         const baselayer = AvailableBaseLayers.layerOverview.find(bl => bl.id === state.layoutToUse.defaultBackgroundId) ?? AvailableBaseLayers.osmCarto
 | |
|         return new Promise(resolve => {
 | |
|             const minimap = Minimap.createMiniMap({
 | |
|                 location: new UIEventSource<Loc>(state.locationControl.data), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
 | |
|                 background: new UIEventSource(baselayer),
 | |
|                 allowMoving: false,
 | |
|                 onFullyLoaded: (_) =>
 | |
|                     window.setTimeout(() => {
 | |
|                         resolve(<MinimapImplementation>minimap)
 | |
|                     }, 250)
 | |
|             })
 | |
|             const style = `width: ${options.width * options.scaling}mm; height: ${options.height * options.scaling}mm;`
 | |
|             minimap.SetStyle(style)
 | |
|             minimap.AttachTo(options.divId)
 | |
|         })
 | |
| 
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Creates a base64-encoded PNG image
 | |
|      * @constructor
 | |
|      */
 | |
|     public async CreatePng(format: "image" ): Promise<string > ;
 | |
|     public async CreatePng(format: "blob"): Promise<Blob> ;
 | |
|     public async CreatePng(format: "image" | "blob"): Promise<string | Blob>;
 | |
|     public async CreatePng(format: "image" | "blob"): Promise<string | Blob> {
 | |
| 
 | |
|         // Lets first init the minimap and wait for all background tiles to load
 | |
|         const minimap = await this.createAndLoadMinimap()
 | |
|         const state = this._state
 | |
|         const dummyMode = this._options.dummyMode ?? false
 | |
|         return new Promise<string | Blob>((resolve, reject) => {
 | |
|             // Next: we prepare the features. Only fully contained features are shown
 | |
|             minimap.leafletMap.addCallbackAndRunD(async (leaflet) => {
 | |
|                 // Ping the featurepipeline to download what is needed
 | |
|                 if (dummyMode) {
 | |
|                     console.warn("Dummy mode is active - not loading map layers")
 | |
|                 } else {
 | |
|                     const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.1).pad(-state.layoutToUse.widenFactor))
 | |
|                     state.currentBounds.setData(bounds)
 | |
|                     if(!state.featurePipeline.sufficientlyZoomed.data){
 | |
|                         console.warn("Not sufficiently zoomed!")
 | |
|                     }
 | |
| 
 | |
|                     if (state.featurePipeline.runningQuery.data) {
 | |
|                         // A query is running!
 | |
|                         // Let's wait for it to complete
 | |
|                         console.log("Waiting for the query to complete")
 | |
|                         await state.featurePipeline.runningQuery.AsPromise(isRunning => !isRunning)
 | |
|                         console.log("Query has completeted!")
 | |
|                     }
 | |
| 
 | |
|                     state.featurePipeline.GetTilesPerLayerWithin(bounds, (tile) => {
 | |
|                         if (tile.layer.layerDef.id.startsWith("note_import")) {
 | |
|                             // Don't export notes to import
 | |
|                             return
 | |
|                         }
 | |
|                         new ShowDataLayer({
 | |
|                             features: tile,
 | |
|                             leafletMap: minimap.leafletMap,
 | |
|                             layerToShow: tile.layer.layerDef,
 | |
|                             doShowLayer: tile.layer.isDisplayed,
 | |
|                             state: undefined,
 | |
|                         })
 | |
|                     })
 | |
|                     await Utils.waitFor(2000)
 | |
|                 }
 | |
|                 minimap.TakeScreenshot(format).then(result => {
 | |
|                     const divId = this._options.divId
 | |
|                     window.setTimeout(() => {
 | |
|                     document.getElementById(divId).removeChild(/*Will fetch the cached htmlelement:*/minimap.ConstructElement())
 | |
| 
 | |
|                     }, 500)
 | |
|                     return resolve(result);
 | |
|                 }).catch(failreason => {
 | |
|                     console.error("Could no make a screenshot due to ",failreason)
 | |
|                     reject(failreason)
 | |
|                 })
 | |
|             })
 | |
| 
 | |
|             state.AddAllOverlaysToMap(minimap.leafletMap)
 | |
|         })
 | |
|     }
 | |
| }
 |