2022-10-27 01:50:01 +02:00
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"
2022-09-14 12:18:51 +02:00
2022-10-27 01:50:01 +02:00
export interface PngMapCreatorOptions {
readonly divId : string
readonly width : number
readonly height : number
readonly scaling? : 1 | number
2022-09-14 12:18:51 +02:00
readonly dummyMode? : boolean
}
2022-09-12 20:14:03 +02:00
export class PngMapCreator {
2022-10-27 01:50:01 +02:00
private readonly _state : FeaturePipelineState | undefined
private readonly _options : PngMapCreatorOptions
2022-09-12 20:14:03 +02:00
2022-09-14 12:18:51 +02:00
constructor ( state : FeaturePipelineState | undefined , options : PngMapCreatorOptions ) {
2022-10-27 01:50:01 +02:00
this . _state = state
this . _options = { . . . options , scaling : options.scaling ? ? 1 }
2022-09-12 20:14:03 +02:00
}
/ * *
* Creates a minimap , waits till all needed tiles are loaded before returning
* @private
* /
private async createAndLoadMinimap ( ) : Promise < MinimapImplementation > {
2022-10-27 01:50:01 +02:00
const state = this . _state
2022-09-12 20:14:03 +02:00
const options = this . _options
2022-10-27 01:50:01 +02:00
const baselayer =
AvailableBaseLayers . layerOverview . find (
( bl ) = > bl . id === state . layoutToUse . defaultBackgroundId
) ? ? AvailableBaseLayers . osmCarto
return new Promise ( ( resolve ) = > {
2022-09-12 20:14:03 +02:00
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
2022-09-13 00:42:29 +02:00
background : new UIEventSource ( baselayer ) ,
2022-09-12 20:14:03 +02:00
allowMoving : false ,
onFullyLoaded : ( _ ) = >
window . setTimeout ( ( ) = > {
resolve ( < MinimapImplementation > minimap )
2022-10-27 01:50:01 +02:00
} , 250 ) ,
2022-09-12 20:14:03 +02:00
} )
2022-10-27 01:50:01 +02:00
const style = ` width: ${ options . width * options . scaling } mm; height: ${
options . height * options . scaling
} mm ; `
2022-09-12 20:14:03 +02:00
minimap . SetStyle ( style )
minimap . AttachTo ( options . divId )
} )
}
/ * *
* Creates a base64 - encoded PNG image
* @constructor
* /
2022-10-27 01:50:01 +02:00
public async CreatePng ( format : "image" ) : Promise < string >
public async CreatePng ( format : "blob" ) : Promise < Blob >
public async CreatePng ( format : "image" | "blob" ) : Promise < string | Blob >
2022-09-12 20:14:03 +02:00
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
2022-09-14 12:18:51 +02:00
const dummyMode = this . _options . dummyMode ? ? false
2022-09-16 18:58:42 +02:00
return new Promise < string | Blob > ( ( resolve , reject ) = > {
2022-09-12 20:14:03 +02:00
// Next: we prepare the features. Only fully contained features are shown
minimap . leafletMap . addCallbackAndRunD ( async ( leaflet ) = > {
// Ping the featurepipeline to download what is needed
2022-09-14 12:18:51 +02:00
if ( dummyMode ) {
console . warn ( "Dummy mode is active - not loading map layers" )
} else {
2022-10-27 01:50:01 +02:00
const bounds = BBox . fromLeafletBounds (
leaflet . getBounds ( ) . pad ( 0.1 ) . pad ( - state . layoutToUse . widenFactor )
)
2022-09-14 12:18:51 +02:00
state . currentBounds . setData ( bounds )
2022-10-27 01:50:01 +02:00
if ( ! state . featurePipeline . sufficientlyZoomed . data ) {
2022-09-15 14:17:39 +02:00
console . warn ( "Not sufficiently zoomed!" )
}
2022-09-12 20:14:03 +02:00
2022-09-14 12:18:51 +02:00
if ( state . featurePipeline . runningQuery . data ) {
// A query is running!
// Let's wait for it to complete
console . log ( "Waiting for the query to complete" )
2022-10-27 01:50:01 +02:00
await state . featurePipeline . runningQuery . AsPromise (
( isRunning ) = > ! isRunning
)
2022-09-14 12:18:51 +02:00
console . log ( "Query has completeted!" )
}
2022-09-12 20:14:03 +02:00
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 ,
} )
} )
2022-09-16 18:58:42 +02:00
await Utils . waitFor ( 2000 )
2022-09-14 12:18:51 +02:00
}
2022-10-27 01:50:01 +02:00
minimap
. TakeScreenshot ( format )
. then ( async ( result ) = > {
const divId = this . _options . divId
await Utils . waitFor ( 250 )
document
. getElementById ( divId )
. removeChild (
/*Will fetch the cached htmlelement:*/ minimap . ConstructElement ( )
)
return resolve ( result )
} )
. catch ( ( failreason ) = > {
console . error ( "Could no make a screenshot due to " , failreason )
reject ( failreason )
} )
2022-09-12 20:14:03 +02:00
} )
2022-09-14 12:18:51 +02:00
2022-09-12 20:14:03 +02:00
state . AddAllOverlaysToMap ( minimap . leafletMap )
} )
}
}