2021-07-28 02:51:07 +02:00
import jsPDF from "jspdf" ;
import { UIEventSource } from "../Logic/UIEventSource" ;
2021-11-03 00:44:53 +01:00
import Minimap , { MinimapObj } from "./Base/Minimap" ;
2021-07-28 02:51:07 +02:00
import Loc from "../Models/Loc" ;
import BaseLayer from "../Models/BaseLayer" ;
import { FixedUiElement } from "./Base/FixedUiElement" ;
import Translations from "./i18n/Translations" ;
2021-07-29 01:57:45 +02:00
import State from "../State" ;
import Constants from "../Models/Constants" ;
2021-08-07 23:11:34 +02:00
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" ;
2021-09-21 02:10:42 +02:00
import FeaturePipeline from "../Logic/FeatureSource/FeaturePipeline" ;
import ShowDataLayer from "./ShowDataLayer/ShowDataLayer" ;
2021-09-28 17:30:48 +02:00
import { BBox } from "../Logic/BBox" ;
2021-11-07 16:34:51 +01:00
2021-09-21 02:10:42 +02:00
/ * *
* Creates screenshoter to take png screenshot
* Creates jspdf and downloads it
* - landscape pdf
*
* To add new layout :
* - add new possible layout name in constructor
* - add new layout in "PDFLayout"
* - > in there are more instructions
* /
2021-07-28 02:51:07 +02:00
export default class ExportPDF {
// dimensions of the map in milimeter
2021-09-09 00:05:51 +02:00
public isRunning = new UIEventSource ( true )
2021-07-28 02:51:07 +02:00
// A4: 297 * 210mm
private readonly mapW = 297 ;
private readonly mapH = 210 ;
private readonly scaling = 2
private readonly freeDivId : string ;
2021-10-03 01:38:57 +02:00
private readonly _layout : LayoutConfig ;
2021-07-28 15:14:13 +02:00
private _screenhotTaken = false ;
2021-07-28 02:51:07 +02:00
constructor (
options : {
freeDivId : string ,
location : UIEventSource < Loc > ,
background? : UIEventSource < BaseLayer >
2021-09-21 02:10:42 +02:00
features : FeaturePipeline ,
2021-10-03 01:38:57 +02:00
layout : LayoutConfig
2021-07-28 02:51:07 +02:00
}
) {
this . freeDivId = options . freeDivId ;
this . _layout = options . layout ;
const self = this ;
// We create a minimap at the given location and attach it to the given 'hidden' element
2021-07-28 12:36:39 +02:00
const l = options . location . data ;
const loc = {
2021-07-28 15:14:13 +02:00
lat : l.lat ,
2021-07-28 12:36:39 +02:00
lon : l.lon ,
zoom : l.zoom + 1
}
2021-07-28 15:14:13 +02:00
2021-09-21 02:10:42 +02:00
const minimap = Minimap . createMiniMap ( {
2021-07-28 12:36:39 +02:00
location : new UIEventSource < Loc > ( loc ) , // 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
2021-07-28 02:51:07 +02:00
background : options.background ,
2021-07-28 12:36:39 +02:00
allowMoving : false ,
2021-11-03 00:44:53 +01:00
onFullyLoaded : _ = > window . setTimeout ( ( ) = > {
2021-07-28 15:14:13 +02:00
if ( self . _screenhotTaken ) {
return ;
}
try {
2021-11-03 00:44:53 +01:00
self . CreatePdf ( minimap )
2021-07-28 15:14:13 +02:00
. then ( ( ) = > self . cleanup ( ) )
. catch ( ( ) = > self . cleanup ( ) )
} catch ( e ) {
2021-07-28 02:51:07 +02:00
console . error ( e )
self . cleanup ( )
}
} , 500 )
} )
minimap . SetStyle ( ` width: ${ this . mapW * this . scaling } mm; height: ${ this . mapH * this . scaling } mm; ` )
minimap . AttachTo ( options . freeDivId )
// Next: we prepare the features. Only fully contained features are shown
2021-11-07 16:34:51 +01:00
minimap . leafletMap . addCallbackAndRunD ( leaflet = > {
2021-07-28 02:51:07 +02:00
const bounds = BBox . fromLeafletBounds ( leaflet . getBounds ( ) . pad ( 0.2 ) )
2021-09-21 02:10:42 +02:00
options . features . GetTilesPerLayerWithin ( bounds , tile = > {
2021-11-07 16:34:51 +01:00
if ( tile . layer . layerDef . minzoom > l . zoom ) {
2021-10-14 17:54:43 +02:00
return
}
2022-01-31 11:01:05 +01:00
if ( tile . layer . layerDef . id . startsWith ( "note_import" ) ) {
// Don't export notes to import
return ;
}
2021-09-21 02:10:42 +02:00
new ShowDataLayer (
{
features : tile ,
leafletMap : minimap.leafletMap ,
layerToShow : tile.layer.layerDef ,
2022-01-19 20:34:04 +01:00
popup : undefined ,
doShowLayer : tile.layer.isDisplayed ,
state : undefined
2021-09-21 02:10:42 +02:00
}
)
} )
2021-11-07 16:34:51 +01:00
2021-09-21 02:10:42 +02:00
} )
2021-07-28 02:51:07 +02:00
2021-10-15 05:20:02 +02:00
State . state . AddAllOverlaysToMap ( minimap . leafletMap )
2021-07-28 02:51:07 +02:00
}
2021-07-28 15:14:13 +02:00
private cleanup() {
2021-09-09 23:24:21 +02:00
new FixedUiElement ( "Screenshot taken!" ) . AttachTo ( this . freeDivId )
2021-07-28 15:14:13 +02:00
this . _screenhotTaken = true ;
2021-07-28 02:51:07 +02:00
}
2021-11-03 00:44:53 +01:00
private async CreatePdf ( minimap : MinimapObj ) {
2021-11-07 16:34:51 +01:00
2021-10-03 01:38:57 +02:00
console . log ( "PDF creation started" )
2021-07-28 02:51:07 +02:00
const t = Translations . t . general . pdf ;
2021-10-03 01:38:57 +02:00
const layout = this . _layout
2021-07-28 02:51:07 +02:00
let doc = new jsPDF ( 'landscape' ) ;
2021-11-03 00:44:53 +01:00
const image = await minimap . TakeScreenshot ( )
2021-07-28 02:51:07 +02:00
// @ts-ignore
doc . addImage ( image , 'PNG' , 0 , 0 , this . mapW , this . mapH ) ;
doc . setDrawColor ( 255 , 255 , 255 )
doc . setFillColor ( 255 , 255 , 255 )
2021-07-29 01:57:45 +02:00
doc . roundedRect ( 12 , 10 , 145 , 25 , 5 , 5 , 'FD' )
2021-07-28 02:51:07 +02:00
doc . setFontSize ( 20 )
2021-07-29 01:57:45 +02:00
doc . textWithLink ( layout . title . txt , 40 , 18.5 , {
maxWidth : 125 ,
url : window.location.href
2021-07-28 02:51:07 +02:00
} )
doc . setFontSize ( 10 )
2021-07-29 01:57:45 +02:00
doc . text ( t . generatedWith . txt , 40 , 23 , {
maxWidth : 125
2021-07-28 02:51:07 +02:00
} )
2021-08-22 20:10:19 +02:00
const backgroundLayer : BaseLayer = State . state . backgroundLayer . data
2021-07-29 01:57:45 +02:00
const attribution = new FixedUiElement ( backgroundLayer . layer ( ) . getAttribution ( ) ? ? backgroundLayer . name ) . ConstructElement ( ) . innerText
doc . textWithLink ( t . attr . txt , 40 , 26.5 , {
maxWidth : 125 ,
url : "https://www.openstreetmap.org/copyright"
} )
doc . text ( t . attrBackground . Subs ( {
background : attribution
} ) . txt , 40 , 30 )
2021-08-22 20:10:19 +02:00
let date = new Date ( ) . toISOString ( ) . substr ( 0 , 16 )
2021-07-29 01:57:45 +02:00
doc . setFontSize ( 7 )
doc . text ( t . versionInfo . Subs ( {
version : Constants.vNumber ,
date : date
} ) . txt , 40 , 34 , {
maxWidth : 125
} )
2021-08-22 20:10:19 +02:00
2021-07-28 02:51:07 +02:00
// Add the logo of the layout
let img = document . createElement ( 'img' ) ;
const imgSource = layout . icon
2021-07-29 01:57:45 +02:00
const imgType = imgSource . substr ( imgSource . lastIndexOf ( "." ) + 1 ) ;
2021-07-28 02:51:07 +02:00
img . src = imgSource
2021-07-29 01:57:45 +02:00
if ( imgType . toLowerCase ( ) === "svg" ) {
new FixedUiElement ( "" ) . AttachTo ( this . freeDivId )
// This is an svg image, we use the canvas to convert it to a png
const canvas = document . createElement ( 'canvas' )
const ctx = canvas . getContext ( '2d' ) ;
canvas . width = 500
canvas . height = 500
img . style . width = "100%"
img . style . height = "100%"
ctx . drawImage ( img , 0 , 0 , 500 , 500 ) ;
const base64img = canvas . toDataURL ( "image/png" )
doc . addImage ( base64img , 'png' , 15 , 12 , 20 , 20 ) ;
} else {
try {
doc . addImage ( img , imgType , 15 , 12 , 20 , 20 ) ;
} catch ( e ) {
console . error ( e )
}
2021-07-28 02:51:07 +02:00
}
2021-07-29 01:57:45 +02:00
doc . save ( ` MapComplete_ ${ layout . title . txt } _ ${ date } .pdf ` ) ;
2021-07-28 02:51:07 +02:00
2021-08-22 20:10:19 +02:00
this . isRunning . setData ( false )
2021-07-28 02:51:07 +02:00
}
}