forked from MapComplete/MapComplete
More work on the flyers
This commit is contained in:
parent
921132b478
commit
27ccce70c0
16 changed files with 687 additions and 308 deletions
|
@ -22,7 +22,7 @@ export default class LayoutConfig {
|
||||||
public readonly startLat: number
|
public readonly startLat: number
|
||||||
public readonly startLon: number
|
public readonly startLon: number
|
||||||
public widenFactor: number
|
public widenFactor: number
|
||||||
public readonly defaultBackgroundId?: string
|
public defaultBackgroundId?: string
|
||||||
public layers: LayerConfig[]
|
public layers: LayerConfig[]
|
||||||
public tileLayerSources: TilesourceConfig[]
|
public tileLayerSources: TilesourceConfig[]
|
||||||
public readonly clustering?: {
|
public readonly clustering?: {
|
||||||
|
@ -46,7 +46,7 @@ export default class LayoutConfig {
|
||||||
public readonly customCss?: string
|
public readonly customCss?: string
|
||||||
|
|
||||||
public readonly overpassUrl: string[]
|
public readonly overpassUrl: string[]
|
||||||
public readonly overpassTimeout: number
|
public overpassTimeout: number
|
||||||
public readonly overpassMaxZoom: number
|
public readonly overpassMaxZoom: number
|
||||||
public readonly osmApiTileSize: number
|
public readonly osmApiTileSize: number
|
||||||
public readonly official: boolean
|
public readonly official: boolean
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Minimap, { MinimapObj } from "../Base/Minimap"
|
||||||
import BaseLayer from "../../Models/BaseLayer"
|
import BaseLayer from "../../Models/BaseLayer"
|
||||||
import Combine from "../Base/Combine"
|
import Combine from "../Base/Combine"
|
||||||
import Svg from "../../Svg"
|
import Svg from "../../Svg"
|
||||||
import State from "../../State"
|
|
||||||
import {GeoOperations} from "../../Logic/GeoOperations"
|
import {GeoOperations} from "../../Logic/GeoOperations"
|
||||||
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
|
||||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||||
|
@ -16,11 +15,13 @@ import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement"
|
||||||
import Toggle from "./Toggle"
|
import Toggle from "./Toggle"
|
||||||
import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json"
|
import * as matchpoint from "../../assets/layers/matchpoint/matchpoint.json"
|
||||||
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
|
import FilteredLayer from "../../Models/FilteredLayer";
|
||||||
|
import {ElementStorage} from "../../Logic/ElementStorage";
|
||||||
|
|
||||||
export default class LocationInput
|
export default class LocationInput
|
||||||
extends BaseUIElement
|
extends BaseUIElement
|
||||||
implements ReadonlyInputElement<Loc>, MinimapObj
|
implements ReadonlyInputElement<Loc>, MinimapObj {
|
||||||
{
|
|
||||||
private static readonly matchLayer = new LayerConfig(
|
private static readonly matchLayer = new LayerConfig(
|
||||||
matchpoint,
|
matchpoint,
|
||||||
"LocationInput.matchpoint",
|
"LocationInput.matchpoint",
|
||||||
|
@ -47,6 +48,13 @@ export default class LocationInput
|
||||||
private readonly map: BaseUIElement & MinimapObj
|
private readonly map: BaseUIElement & MinimapObj
|
||||||
private readonly clickLocation: UIEventSource<Loc>
|
private readonly clickLocation: UIEventSource<Loc>
|
||||||
private readonly _minZoom: number
|
private readonly _minZoom: number
|
||||||
|
private readonly _state: {
|
||||||
|
readonly filteredLayers: Store<FilteredLayer[]>;
|
||||||
|
readonly backgroundLayer: UIEventSource<BaseLayer>;
|
||||||
|
readonly layoutToUse: LayoutConfig;
|
||||||
|
readonly selectedElement: UIEventSource<any>;
|
||||||
|
readonly allElements: ElementStorage
|
||||||
|
}
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: {
|
||||||
minZoom?: number
|
minZoom?: number
|
||||||
|
@ -57,6 +65,13 @@ export default class LocationInput
|
||||||
requiresSnapping?: boolean
|
requiresSnapping?: boolean
|
||||||
centerLocation: UIEventSource<Loc>
|
centerLocation: UIEventSource<Loc>
|
||||||
bounds?: UIEventSource<BBox>
|
bounds?: UIEventSource<BBox>
|
||||||
|
state: {
|
||||||
|
readonly filteredLayers: Store<FilteredLayer[]>;
|
||||||
|
readonly backgroundLayer: UIEventSource<BaseLayer>;
|
||||||
|
readonly layoutToUse: LayoutConfig;
|
||||||
|
readonly selectedElement: UIEventSource<any>;
|
||||||
|
readonly allElements: ElementStorage
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
super()
|
super()
|
||||||
this._snapTo = options.snapTo?.map((features) =>
|
this._snapTo = options.snapTo?.map((features) =>
|
||||||
|
@ -67,13 +82,14 @@ export default class LocationInput
|
||||||
this._snappedPointTags = options.snappedPointTags
|
this._snappedPointTags = options.snappedPointTags
|
||||||
this._bounds = options.bounds
|
this._bounds = options.bounds
|
||||||
this._minZoom = options.minZoom
|
this._minZoom = options.minZoom
|
||||||
|
this._state = options.state
|
||||||
if (this._snapTo === undefined) {
|
if (this._snapTo === undefined) {
|
||||||
this._value = this._centerLocation
|
this._value = this._centerLocation
|
||||||
} else {
|
} else {
|
||||||
const self = this
|
const self = this
|
||||||
|
|
||||||
if (self._snappedPointTags !== undefined) {
|
if (self._snappedPointTags !== undefined) {
|
||||||
const layout = State.state.layoutToUse
|
const layout = this._state.layoutToUse
|
||||||
|
|
||||||
let matchingLayer = LocationInput.matchLayer
|
let matchingLayer = LocationInput.matchLayer
|
||||||
for (const layer of layout.layers) {
|
for (const layer of layout.layers) {
|
||||||
|
@ -149,14 +165,14 @@ export default class LocationInput
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.mapBackground = options.mapBackground ?? State.state?.backgroundLayer
|
this.mapBackground = options.mapBackground ?? this._state?.backgroundLayer
|
||||||
this.SetClass("block h-full")
|
this.SetClass("block h-full")
|
||||||
|
|
||||||
this.clickLocation = new UIEventSource<Loc>(undefined)
|
this.clickLocation = new UIEventSource<Loc>(undefined)
|
||||||
this.map = Minimap.createMiniMap({
|
this.map = Minimap.createMiniMap({
|
||||||
location: this._centerLocation,
|
location: this._centerLocation,
|
||||||
background: this.mapBackground,
|
background: this.mapBackground,
|
||||||
attribution: this.mapBackground !== State.state?.backgroundLayer,
|
attribution: this.mapBackground !== this._state?.backgroundLayer,
|
||||||
lastClickLocation: this.clickLocation,
|
lastClickLocation: this.clickLocation,
|
||||||
bounds: this._bounds,
|
bounds: this._bounds,
|
||||||
addLayerControl: true,
|
addLayerControl: true,
|
||||||
|
@ -204,7 +220,7 @@ export default class LocationInput
|
||||||
features: StaticFeatureSource.fromDateless(this._snapTo),
|
features: StaticFeatureSource.fromDateless(this._snapTo),
|
||||||
zoomToFeatures: false,
|
zoomToFeatures: false,
|
||||||
leafletMap: this.map.leafletMap,
|
leafletMap: this.map.leafletMap,
|
||||||
layers: State.state.filteredLayers,
|
layers: this._state.filteredLayers,
|
||||||
})
|
})
|
||||||
// Show the central point
|
// Show the central point
|
||||||
const matchPoint = this._snappedPoint.map((loc) => {
|
const matchPoint = this._snappedPoint.map((loc) => {
|
||||||
|
@ -220,8 +236,8 @@ export default class LocationInput
|
||||||
zoomToFeatures: false,
|
zoomToFeatures: false,
|
||||||
leafletMap: this.map.leafletMap,
|
leafletMap: this.map.leafletMap,
|
||||||
layerToShow: this._matching_layer,
|
layerToShow: this._matching_layer,
|
||||||
state: State.state,
|
state: this._state,
|
||||||
selectedElement: State.state.selectedElement,
|
selectedElement: this._state.selectedElement,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.mapBackground.map(
|
this.mapBackground.map(
|
||||||
|
@ -267,7 +283,10 @@ export default class LocationInput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TakeScreenshot(): Promise<string> {
|
TakeScreenshot(format: "image"): Promise<string>;
|
||||||
return this.map.TakeScreenshot()
|
TakeScreenshot(format: "blob"): Promise<Blob>;
|
||||||
|
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>;
|
||||||
|
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> {
|
||||||
|
return this.map.TakeScreenshot(format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ export default class ConfirmLocationOfPoint extends Combine {
|
||||||
snappedPointTags: tags,
|
snappedPointTags: tags,
|
||||||
maxSnapDistance: preset.preciseInput.maxSnapDistance,
|
maxSnapDistance: preset.preciseInput.maxSnapDistance,
|
||||||
bounds: mapBounds,
|
bounds: mapBounds,
|
||||||
|
state: <any> state
|
||||||
})
|
})
|
||||||
preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
|
preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
|
||||||
preciseInput
|
preciseInput
|
||||||
|
|
|
@ -145,6 +145,7 @@ export default class MoveWizard extends Toggle {
|
||||||
minZoom: reason.minZoom,
|
minZoom: reason.minZoom,
|
||||||
centerLocation: loc,
|
centerLocation: loc,
|
||||||
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
|
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
|
||||||
|
state: <any> state
|
||||||
})
|
})
|
||||||
|
|
||||||
if (reason.lockBounds) {
|
if (reason.lockBounds) {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeature
|
||||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||||
import { Tiles } from "../../Models/TileRange"
|
import { Tiles } from "../../Models/TileRange"
|
||||||
import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.json"
|
import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.json"
|
||||||
import State from "../../State"
|
|
||||||
|
|
||||||
export default class ShowTileInfo {
|
export default class ShowTileInfo {
|
||||||
public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true)
|
public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true)
|
||||||
|
@ -16,7 +15,7 @@ export default class ShowTileInfo {
|
||||||
leafletMap: UIEventSource<any>
|
leafletMap: UIEventSource<any>
|
||||||
layer?: LayerConfig
|
layer?: LayerConfig
|
||||||
doShowLayer?: UIEventSource<boolean>
|
doShowLayer?: UIEventSource<boolean>
|
||||||
}) {
|
}, state) {
|
||||||
const source = options.source
|
const source = options.source
|
||||||
const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map(
|
const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map(
|
||||||
(features) => {
|
(features) => {
|
||||||
|
@ -56,7 +55,7 @@ export default class ShowTileInfo {
|
||||||
features: new StaticFeatureSource(metaFeature),
|
features: new StaticFeatureSource(metaFeature),
|
||||||
leafletMap: options.leafletMap,
|
leafletMap: options.leafletMap,
|
||||||
doShowLayer: options.doShowLayer,
|
doShowLayer: options.doShowLayer,
|
||||||
state: State.state,
|
state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
Utils.ts
2
Utils.ts
|
@ -823,7 +823,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
||||||
} else if (xhr.status === 509 || xhr.status === 429) {
|
} else if (xhr.status === 509 || xhr.status === 429) {
|
||||||
reject("rate limited")
|
reject("rate limited")
|
||||||
} else {
|
} else {
|
||||||
reject(xhr.statusText)
|
reject("Could not download "+url+" due to "+xhr.statusText)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xhr.open("GET", url)
|
xhr.open("GET", url)
|
||||||
|
|
|
@ -6,20 +6,19 @@ import ShowDataLayer from "../UI/ShowDataLayer/ShowDataLayer";
|
||||||
import {BBox} from "../Logic/BBox";
|
import {BBox} from "../Logic/BBox";
|
||||||
import Minimap from "../UI/Base/Minimap";
|
import Minimap from "../UI/Base/Minimap";
|
||||||
import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers";
|
import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers";
|
||||||
import AvailableBaseLayersImplementation from "../Logic/Actors/AvailableBaseLayersImplementation";
|
import {Utils} from "../Utils";
|
||||||
|
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
|
|
||||||
|
export interface PngMapCreatorOptions{
|
||||||
|
readonly divId: string; readonly width: number; readonly height: number; readonly scaling?: 1 | number,
|
||||||
|
readonly dummyMode?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export class PngMapCreator {
|
export class PngMapCreator {
|
||||||
private readonly _state: FeaturePipelineState;
|
private readonly _state: FeaturePipelineState | undefined;
|
||||||
private readonly _options: {
|
private readonly _options: PngMapCreatorOptions;
|
||||||
readonly divId: string; readonly width: number; readonly height: number; readonly scaling?: 1 | number
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(state: FeaturePipelineState, options: {
|
constructor(state: FeaturePipelineState | undefined, options: PngMapCreatorOptions) {
|
||||||
readonly divId: string
|
|
||||||
readonly width: number,
|
|
||||||
readonly height: number,
|
|
||||||
readonly scaling?: 1 | number
|
|
||||||
}) {
|
|
||||||
this._state = state;
|
this._state = state;
|
||||||
this._options = {...options, scaling: options.scaling ?? 1};
|
this._options = {...options, scaling: options.scaling ?? 1};
|
||||||
}
|
}
|
||||||
|
@ -62,13 +61,19 @@ export class PngMapCreator {
|
||||||
// Lets first init the minimap and wait for all background tiles to load
|
// Lets first init the minimap and wait for all background tiles to load
|
||||||
const minimap = await this.createAndLoadMinimap()
|
const minimap = await this.createAndLoadMinimap()
|
||||||
const state = this._state
|
const state = this._state
|
||||||
|
const freediv = this._options.divId
|
||||||
|
const dummyMode = this._options.dummyMode ?? false
|
||||||
|
console.log("Dummy mode is", dummyMode)
|
||||||
return new Promise<string | Blob>(resolve => {
|
return new Promise<string | Blob>(resolve => {
|
||||||
// Next: we prepare the features. Only fully contained features are shown
|
// Next: we prepare the features. Only fully contained features are shown
|
||||||
minimap.leafletMap.addCallbackAndRunD(async (leaflet) => {
|
minimap.leafletMap.addCallbackAndRunD(async (leaflet) => {
|
||||||
const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.1).pad(-state.layoutToUse.widenFactor))
|
|
||||||
// Ping the featurepipeline to download what is needed
|
// 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)
|
state.currentBounds.setData(bounds)
|
||||||
|
|
||||||
if (state.featurePipeline.runningQuery.data) {
|
if (state.featurePipeline.runningQuery.data) {
|
||||||
// A query is running!
|
// A query is running!
|
||||||
// Let's wait for it to complete
|
// Let's wait for it to complete
|
||||||
|
@ -77,9 +82,6 @@ export class PngMapCreator {
|
||||||
console.log("Query has completeted!")
|
console.log("Query has completeted!")
|
||||||
}
|
}
|
||||||
|
|
||||||
window.setTimeout(() => {
|
|
||||||
|
|
||||||
|
|
||||||
state.featurePipeline.GetTilesPerLayerWithin(bounds, (tile) => {
|
state.featurePipeline.GetTilesPerLayerWithin(bounds, (tile) => {
|
||||||
|
|
||||||
if (tile.layer.layerDef.minzoom > state.locationControl.data.zoom) {
|
if (tile.layer.layerDef.minzoom > state.locationControl.data.zoom) {
|
||||||
|
@ -97,9 +99,14 @@ export class PngMapCreator {
|
||||||
state: undefined,
|
state: undefined,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
minimap.TakeScreenshot(format).then(result => resolve(result))
|
await Utils.waitFor(2500)
|
||||||
}, 2500)
|
}
|
||||||
|
minimap.TakeScreenshot(format).then(result => {
|
||||||
|
new FixedUiElement("Done!").AttachTo(freediv)
|
||||||
|
return resolve(result);
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
state.AddAllOverlaysToMap(minimap.leafletMap)
|
state.AddAllOverlaysToMap(minimap.leafletMap)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,11 @@ import {Translation, TypedTranslation} from "../UI/i18n/Translation";
|
||||||
import FeaturePipelineState from "../Logic/State/FeaturePipelineState";
|
import FeaturePipelineState from "../Logic/State/FeaturePipelineState";
|
||||||
import {PngMapCreator} from "./pngMapCreator";
|
import {PngMapCreator} from "./pngMapCreator";
|
||||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||||
|
import {Store, UIEventSource} from "../Logic/UIEventSource";
|
||||||
|
import "../assets/templates/Ubuntu-M-normal.js"
|
||||||
|
import "../assets/templates/Ubuntu-L-normal.js"
|
||||||
|
import "../assets/templates/UbuntuMono-B-bold.js"
|
||||||
|
import {parseSVG, makeAbsolute} from 'svg-path-parser';
|
||||||
|
|
||||||
class SvgToPdfInternals {
|
class SvgToPdfInternals {
|
||||||
private readonly doc: jsPDF;
|
private readonly doc: jsPDF;
|
||||||
|
@ -179,7 +184,7 @@ class SvgToPdfInternals {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extractTranslation(text: string) {
|
private extractTranslation(text: string) {
|
||||||
const pathPart = text.match(/\$(([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+)(.*)/)
|
const pathPart = text.match(/\$(([_a-zA-Z0-9]+\.)+[_a-zA-Z0-9]+)(.*)/)
|
||||||
if (pathPart === null) {
|
if (pathPart === null) {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
@ -315,6 +320,38 @@ class SvgToPdfInternals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private drawPath(element: SVGPathElement): void {
|
||||||
|
const path = element.getAttribute("d")
|
||||||
|
const parsed: { code: string, x: number, y: number, x2?, y2?, x1?, y1? }[] = parseSVG(path)
|
||||||
|
makeAbsolute(parsed)
|
||||||
|
|
||||||
|
for (const c of parsed) {
|
||||||
|
if (c.code === "C" || c.code === "c") {
|
||||||
|
const command = {op: "c", c: [c.x1, c.y1, c.x2, c.y2, c.x, c.y]}
|
||||||
|
this.doc.path([command])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
this.doc.path([{op: c.code.toLowerCase(), c: [c.x, c.y]}])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const css = SvgToPdfInternals.css(element)
|
||||||
|
this.doc.setDrawColor(css["color"])
|
||||||
|
this.doc.setFillColor(css["fill"])
|
||||||
|
if (css["stroke-width"]) {
|
||||||
|
this.doc.setLineWidth(Number(css["stroke-width"]))
|
||||||
|
}
|
||||||
|
if (css["stroke-linejoin"] !== undefined) {
|
||||||
|
this.doc.setLineJoin(css["stroke-linejoin"])
|
||||||
|
}
|
||||||
|
if (css["fill-rule"] === "evenodd") {
|
||||||
|
this.doc.fillEvenOdd()
|
||||||
|
} else {
|
||||||
|
this.doc.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public handleElement(element: SVGSVGElement | Element): void {
|
public handleElement(element: SVGSVGElement | Element): void {
|
||||||
const isTransformed = this.setTransform(element)
|
const isTransformed = this.setTransform(element)
|
||||||
if (element.tagName === "tspan") {
|
if (element.tagName === "tspan") {
|
||||||
|
@ -331,6 +368,10 @@ class SvgToPdfInternals {
|
||||||
this.drawImage(element)
|
this.drawImage(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (element.tagName === "path") {
|
||||||
|
this.drawPath(<any>element)
|
||||||
|
}
|
||||||
|
|
||||||
if (element.tagName === "g" || element.tagName === "text") {
|
if (element.tagName === "g" || element.tagName === "text") {
|
||||||
|
|
||||||
for (let child of Array.from(element.children)) {
|
for (let child of Array.from(element.children)) {
|
||||||
|
@ -371,6 +412,13 @@ class SvgToPdfInternals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SvgToPdfOptions {
|
||||||
|
getFreeDiv: () => string,
|
||||||
|
disableMaps?: false | true
|
||||||
|
textSubstitutions?: Record<string, string>, beforePage?: (i: number) => void
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class SvgToPdf {
|
export class SvgToPdf {
|
||||||
|
|
||||||
private images: Record<string, HTMLImageElement> = {}
|
private images: Record<string, HTMLImageElement> = {}
|
||||||
|
@ -379,18 +427,20 @@ export class SvgToPdf {
|
||||||
private readonly _textSubstitutions: Record<string, string>;
|
private readonly _textSubstitutions: Record<string, string>;
|
||||||
private readonly _beforePage: ((i: number) => void) | undefined;
|
private readonly _beforePage: ((i: number) => void) | undefined;
|
||||||
public readonly _usedTranslations: Set<string> = new Set<string>()
|
public readonly _usedTranslations: Set<string> = new Set<string>()
|
||||||
private readonly _freeDivId: string | undefined;
|
private readonly _freeDivId: () => string;
|
||||||
|
private readonly _currentState = new UIEventSource<string>("Initing")
|
||||||
|
public readonly currentState: Store<string>
|
||||||
|
private readonly _disableMaps: boolean ;
|
||||||
|
|
||||||
constructor(pages: string[], options?: {
|
constructor(pages: string[], options?:SvgToPdfOptions) {
|
||||||
freeDivId?: string,
|
this.currentState = this._currentState
|
||||||
textSubstitutions?: Record<string, string>, beforePage?: (i: number) => void
|
|
||||||
}) {
|
|
||||||
this._textSubstitutions = options?.textSubstitutions ?? {};
|
this._textSubstitutions = options?.textSubstitutions ?? {};
|
||||||
this._beforePage = options?.beforePage;
|
this._beforePage = options?.beforePage;
|
||||||
this._freeDivId = options?.freeDivId
|
this._freeDivId = options?.getFreeDiv
|
||||||
|
this._disableMaps = options.disableMaps ?? false
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
const xmlDoc = parser.parseFromString(page, "text/xml");
|
const xmlDoc = parser.parseFromString(page, "image/svg+xml");
|
||||||
const svgRoot = xmlDoc.getElementsByTagName("svg")[0];
|
const svgRoot = xmlDoc.getElementsByTagName("svg")[0];
|
||||||
this._svgRoots.push(svgRoot)
|
this._svgRoots.push(svgRoot)
|
||||||
}
|
}
|
||||||
|
@ -423,6 +473,7 @@ export class SvgToPdf {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.images[xlink] = img
|
this.images[xlink] = img
|
||||||
|
this.setState("Preparing: loading image " + Object.keys(this.images).length + ": " + img.src.substring(0, 30))
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
img.onload = _ => {
|
img.onload = _ => {
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -466,19 +517,11 @@ export class SvgToPdf {
|
||||||
|
|
||||||
private _isPrepared = false;
|
private _isPrepared = false;
|
||||||
|
|
||||||
public async Prepare() {
|
private setState(message: string) {
|
||||||
if (this._isPrepared) {
|
this._currentState.setData(message)
|
||||||
return
|
|
||||||
}
|
|
||||||
this._isPrepared = true;
|
|
||||||
const mapSpecs: SVGTSpanElement[] = []
|
|
||||||
for (const svgRoot of this._svgRoots) {
|
|
||||||
for (let child of Array.from(svgRoot.children)) {
|
|
||||||
await this.prepareElement(<any>child, mapSpecs)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const mapSpec of mapSpecs) {
|
private async prepareMap(mapSpec: SVGTSpanElement,): Promise<void> {
|
||||||
// Upper left point of the tspan
|
// Upper left point of the tspan
|
||||||
const {x, y} = SvgToPdfInternals.GetActualXY(mapSpec)
|
const {x, y} = SvgToPdfInternals.GetActualXY(mapSpec)
|
||||||
|
|
||||||
|
@ -493,6 +536,8 @@ export class SvgToPdf {
|
||||||
throw "Invalid mapspec:" + spec
|
throw "Invalid mapspec:" + spec
|
||||||
}
|
}
|
||||||
const params = SvgToPdfInternals.parseCss(match[1], ",")
|
const params = SvgToPdfInternals.parseCss(match[1], ",")
|
||||||
|
const ctx = `Preparing map (theme ${params["theme"]})`
|
||||||
|
this.setState(ctx + "...")
|
||||||
|
|
||||||
let smallestRect: SVGRectElement = undefined
|
let smallestRect: SVGRectElement = undefined
|
||||||
let smallestSurface: number = undefined;
|
let smallestSurface: number = undefined;
|
||||||
|
@ -533,12 +578,11 @@ export class SvgToPdf {
|
||||||
throw "Theme not found:" + params["theme"] + ". Use theme: to define which theme to use. "
|
throw "Theme not found:" + params["theme"] + ". Use theme: to define which theme to use. "
|
||||||
}
|
}
|
||||||
layout.widenFactor = 0
|
layout.widenFactor = 0
|
||||||
|
layout.overpassTimeout = 180
|
||||||
|
layout.defaultBackgroundId = params["background"] ?? layout.defaultBackgroundId
|
||||||
const zoom = Number(params["zoom"] ?? params["z"] ?? 14);
|
const zoom = Number(params["zoom"] ?? params["z"] ?? 14);
|
||||||
for (const l of layout.layers) {
|
|
||||||
l.minzoom = zoom
|
|
||||||
}
|
|
||||||
const state = new FeaturePipelineState(layout)
|
const state = new FeaturePipelineState(layout)
|
||||||
state.backgroundLayer.addCallbackAndRunD(l => console.log("baselayer is", l.id))
|
|
||||||
state.locationControl.setData({
|
state.locationControl.setData({
|
||||||
zoom,
|
zoom,
|
||||||
lat: Number(params["lat"] ?? 51.05016),
|
lat: Number(params["lat"] ?? 51.05016),
|
||||||
|
@ -557,35 +601,68 @@ export class SvgToPdf {
|
||||||
for (const paramsKey in params) {
|
for (const paramsKey in params) {
|
||||||
if (paramsKey.startsWith("layer-")) {
|
if (paramsKey.startsWith("layer-")) {
|
||||||
const layerName = paramsKey.substring("layer-".length)
|
const layerName = paramsKey.substring("layer-".length)
|
||||||
const isDisplayed = params[paramsKey].toLowerCase().trim() === "true";
|
const key = params[paramsKey].toLowerCase().trim()
|
||||||
console.log("Setting display status of ", layerName, "to", isDisplayed)
|
const isDisplayed = key === "true" || key === "force";
|
||||||
state.filteredLayers.data.find(l => l.layerDef.id === layerName).isDisplayed.setData(
|
const layer = state.filteredLayers.data.find(l => l.layerDef.id === layerName)
|
||||||
|
layer.isDisplayed.setData(
|
||||||
isDisplayed
|
isDisplayed
|
||||||
)
|
)
|
||||||
|
if (key === "force") {
|
||||||
|
layer.layerDef.minzoom = zoom
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState(ctx + ": loading map data...")
|
||||||
const pngCreator = new PngMapCreator(
|
const pngCreator = new PngMapCreator(
|
||||||
state,
|
state,
|
||||||
{
|
{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
scaling: Number(params["scaling"] ?? 1.5),
|
scaling: Number(params["scaling"] ?? 1.5),
|
||||||
divId: this._freeDivId
|
divId: this._freeDivId(),
|
||||||
|
dummyMode : this._disableMaps
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
this.setState(ctx + ": rendering png")
|
||||||
const png = await pngCreator.CreatePng("image")
|
const png = await pngCreator.CreatePng("image")
|
||||||
|
|
||||||
svgImage.setAttribute('xlink:href', png)
|
svgImage.setAttribute('xlink:href', png)
|
||||||
smallestRect.parentElement.insertBefore(svgImage, smallestRect)
|
smallestRect.parentElement.insertBefore(svgImage, smallestRect)
|
||||||
await this.prepareElement(svgImage, [])
|
await this.prepareElement(svgImage, [])
|
||||||
smallestRect.setAttribute("style", "fill:#ff00ff00;fill-opacity:0;stroke:#000000;stroke-width:0.202542;stroke-linecap:round;stroke-opacity:1")
|
|
||||||
|
|
||||||
|
const smallestRectCss = SvgToPdfInternals.parseCss(smallestRect.getAttribute("style"))
|
||||||
|
smallestRectCss["fill-opacity"] = "0"
|
||||||
|
smallestRect.setAttribute("style", Object.keys(smallestRectCss).map(k => k + ":" + smallestRectCss[k]).join(";"))
|
||||||
|
|
||||||
|
|
||||||
textElement.parentElement.removeChild(textElement)
|
textElement.parentElement.removeChild(textElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Prepare() {
|
||||||
|
if (this._isPrepared) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._isPrepared = true;
|
||||||
|
this.setState("Preparing...")
|
||||||
|
const mapSpecs: SVGTSpanElement[] = []
|
||||||
|
for (const svgRoot of this._svgRoots) {
|
||||||
|
for (let child of Array.from(svgRoot.children)) {
|
||||||
|
await this.prepareElement(<any>child, mapSpecs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const self = this;
|
||||||
|
await Promise.all(mapSpecs.map(ms => self.prepareMap(ms)))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ConvertSvg(saveAs: string): Promise<void> {
|
public async ConvertSvg(saveAs: string): Promise<void> {
|
||||||
await this.Prepare()
|
await this.Prepare()
|
||||||
|
const ctx = "Rendering PDF"
|
||||||
|
this.setState(ctx + "...")
|
||||||
const firstPage = this._svgRoots[0]
|
const firstPage = this._svgRoots[0]
|
||||||
const width = SvgToPdfInternals.attrNumber(firstPage, "width")
|
const width = SvgToPdfInternals.attrNumber(firstPage, "width")
|
||||||
const height = SvgToPdfInternals.attrNumber(firstPage, "height")
|
const height = SvgToPdfInternals.attrNumber(firstPage, "height")
|
||||||
|
@ -598,6 +675,7 @@ export class SvgToPdf {
|
||||||
doc.advancedAPI(advancedApi => {
|
doc.advancedAPI(advancedApi => {
|
||||||
const internal = new SvgToPdfInternals(advancedApi, this._textSubstitutions, this.images, this.rects);
|
const internal = new SvgToPdfInternals(advancedApi, this._textSubstitutions, this.images, this.rects);
|
||||||
for (let i = 0; i < this._svgRoots.length; i++) {
|
for (let i = 0; i < this._svgRoots.length; i++) {
|
||||||
|
this.setState(ctx + ": page " + i + "/" + this._svgRoots.length)
|
||||||
beforePage(i)
|
beforePage(i)
|
||||||
const svgRoot = svgRoots[i];
|
const svgRoot = svgRoots[i];
|
||||||
for (let child of Array.from(svgRoot.children)) {
|
for (let child of Array.from(svgRoot.children)) {
|
||||||
|
@ -608,7 +686,9 @@ export class SvgToPdf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
this.setState("Serving PDF...")
|
||||||
await doc.save(saveAs);
|
await doc.save(saveAs);
|
||||||
|
this.setState("Done")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"render": "{export_as_geojson()}"
|
"render": "{export_as_geojson()}"
|
||||||
},
|
},
|
||||||
"wikipedia": {
|
"wikipedia": {
|
||||||
"description": "Shows a wikipedia box with the corresponding wikipedia article",
|
"description": "Shows a wikipedia box with the corresponding wikipedia article; the wikidata-item link can be changed by a contributor",
|
||||||
"render": "{wikipedia():max-height:25rem}",
|
"render": "{wikipedia():max-height:25rem}",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What is the corresponding Wikidata entity?",
|
"en": "What is the corresponding Wikidata entity?",
|
||||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 45 KiB |
|
@ -26,15 +26,15 @@
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
showguides="true"
|
showguides="true"
|
||||||
inkscape:guide-bbox="true"
|
inkscape:guide-bbox="true"
|
||||||
inkscape:zoom="1.2400294"
|
inkscape:zoom="1.2563786"
|
||||||
inkscape:cx="712.48308"
|
inkscape:cx="450.10316"
|
||||||
inkscape:cy="537.08402"
|
inkscape:cy="393.98951"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1007"
|
inkscape:window-height="1007"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="layer2"
|
inkscape:current-layer="layer1"
|
||||||
inkscape:snap-global="false">
|
inkscape:snap-global="false">
|
||||||
<sodipodi:guide
|
<sodipodi:guide
|
||||||
position="98.990966,200.95191"
|
position="98.990966,200.95191"
|
||||||
|
@ -230,9 +230,9 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect45500);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect45500);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="594.75243"
|
y="594.75243"
|
||||||
id="tspan37960"><tspan
|
id="tspan39535"><tspan
|
||||||
style="font-size:20px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:20px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37958">$flyer.frontParagraph</tspan></tspan></text>
|
id="tspan39533">$flyer.frontParagraph</tspan></tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
transform="scale(0.26458333)"
|
transform="scale(0.26458333)"
|
||||||
|
@ -256,9 +256,9 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect61742);fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect61742);fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
||||||
x="11.105469"
|
x="11.105469"
|
||||||
y="116.27391"
|
y="116.27391"
|
||||||
id="tspan37964"><tspan
|
id="tspan39539"><tspan
|
||||||
style="font-weight:bold;font-size:26.6667px;-inkscape-font-specification:'sans-serif, Bold'"
|
style="font-weight:bold;font-size:26.6667px;-inkscape-font-specification:'sans-serif, Bold'"
|
||||||
id="tspan37962">$flyer.tagline</tspan></tspan></text>
|
id="tspan39537">$flyer.tagline</tspan></tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
transform="scale(0.26458333)"
|
transform="scale(0.26458333)"
|
||||||
|
@ -271,7 +271,7 @@
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect135032);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect135032);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
||||||
x="8.7285156"
|
x="8.7285156"
|
||||||
y="34.819131"
|
y="34.819131"
|
||||||
id="tspan37966">$flyer.title</tspan></text>
|
id="tspan39541">$flyer.title</tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,97.529993,-16.582379)"
|
transform="matrix(0.26458333,0,0,0.26458333,97.529993,-16.582379)"
|
||||||
|
@ -279,9 +279,9 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect61742-2);display:inline;fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect61742-2);display:inline;fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
||||||
x="11.105469"
|
x="11.105469"
|
||||||
y="116.27391"
|
y="116.27391"
|
||||||
id="tspan37970"><tspan
|
id="tspan39545"><tspan
|
||||||
style="font-weight:bold;font-size:32px;-inkscape-font-specification:'sans-serif, Bold';fill:#000000"
|
style="font-weight:bold;font-size:32px;-inkscape-font-specification:'sans-serif, Bold';fill:#000000"
|
||||||
id="tspan37968">$flyer.whatIsOsm</tspan></tspan></text>
|
id="tspan39543">$flyer.whatIsOsm</tspan></tspan></text>
|
||||||
<image
|
<image
|
||||||
width="55.363865"
|
width="55.363865"
|
||||||
height="21.384291"
|
height="21.384291"
|
||||||
|
@ -298,9 +298,9 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect20620);fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect20620);fill:#d4f1f4;fill-opacity:1;stroke:none"><tspan
|
||||||
x="11.105469"
|
x="11.105469"
|
||||||
y="116.27391"
|
y="116.27391"
|
||||||
id="tspan37974"><tspan
|
id="tspan39549"><tspan
|
||||||
style="font-weight:bold;font-size:26.6667px;-inkscape-font-specification:'sans-serif, Bold'"
|
style="font-weight:bold;font-size:26.6667px;-inkscape-font-specification:'sans-serif, Bold'"
|
||||||
id="tspan37972">$flyer.mapcomplete.title</tspan></tspan></text>
|
id="tspan39547">$flyer.mapcomplete.title</tspan></tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,93.634029,-58.617677)"
|
transform="matrix(0.26458333,0,0,0.26458333,93.634029,-58.617677)"
|
||||||
|
@ -308,63 +308,69 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect21926);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect21926);fill:#000000;fill-opacity:1;stroke:none"><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="594.75243"
|
y="594.75243"
|
||||||
id="tspan37978"><tspan
|
id="tspan39553"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37976">$flyer.mapcomplete.intro
|
id="tspan39551">$flyer.mapcomplete.intro
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="624.75243"
|
y="624.75243"
|
||||||
id="tspan37982"><tspan
|
id="tspan39557"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37980">
|
id="tspan39555">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="654.75243"
|
y="654.75243"
|
||||||
id="tspan37986"><tspan
|
id="tspan39561"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37984">$list(flyer.mapcomplete.li)
|
id="tspan39559">$list(flyer.mapcomplete.li)
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="684.75243"
|
y="684.75243"
|
||||||
id="tspan37990"><tspan
|
id="tspan39565"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37988">
|
id="tspan39563">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="714.75243"
|
y="714.75243"
|
||||||
id="tspan37994"><tspan
|
id="tspan39569"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37992">
|
id="tspan39567">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="744.75243"
|
y="744.75243"
|
||||||
id="tspan37998"><tspan
|
id="tspan39573"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan37996">
|
id="tspan39571">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="774.75243"
|
y="774.75243"
|
||||||
id="tspan38002"><tspan
|
id="tspan39577"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38000">
|
id="tspan39575">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="804.75243"
|
y="804.75243"
|
||||||
id="tspan38006"><tspan
|
id="tspan39581"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38004">
|
id="tspan39579">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="834.75243"
|
y="834.75243"
|
||||||
id="tspan38010"><tspan
|
id="tspan39585"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38008">
|
id="tspan39583">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="864.75243"
|
y="864.75243"
|
||||||
id="tspan38014"><tspan
|
id="tspan39589"><tspan
|
||||||
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38012">$flyer.mapcomplete.customize</tspan></tspan></text>
|
id="tspan39587">
|
||||||
|
</tspan></tspan><tspan
|
||||||
|
x="27.585938"
|
||||||
|
y="894.75243"
|
||||||
|
id="tspan39593"><tspan
|
||||||
|
style="font-size:17.3333px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
|
id="tspan39591">$flyer.mapcomplete.customize</tspan></tspan></text>
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
@ -423,17 +429,17 @@
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect169564);display:inline;fill:#000000;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:0.75;font-family:sans-serif;white-space:pre;shape-inside:url(#rect169564);display:inline;fill:#000000;fill-opacity:1;stroke:none"><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="594.75243"
|
y="594.75243"
|
||||||
id="tspan38020"><tspan
|
id="tspan39599"><tspan
|
||||||
style="font-weight:bold;font-size:24px;-inkscape-font-specification:'sans-serif, Bold'"
|
style="font-weight:bold;font-size:24px;-inkscape-font-specification:'sans-serif, Bold'"
|
||||||
id="tspan38016">$flyer.callToAction</tspan><tspan
|
id="tspan39595">$flyer.callToAction</tspan><tspan
|
||||||
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38018">
|
id="tspan39597">
|
||||||
</tspan></tspan><tspan
|
</tspan></tspan><tspan
|
||||||
x="27.585938"
|
x="27.585938"
|
||||||
y="624.75243"
|
y="624.75243"
|
||||||
id="tspan38024"><tspan
|
id="tspan39603"><tspan
|
||||||
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38022"> </tspan></tspan></text>
|
id="tspan39601"> </tspan></tspan></text>
|
||||||
<image
|
<image
|
||||||
width="73.774147"
|
width="73.774147"
|
||||||
height="103.17501"
|
height="103.17501"
|
||||||
|
@ -11777,8 +11783,8 @@ eTmE1QAAAABJRU5ErkJggg==
|
||||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect171375);display:inline;fill:#000000;fill-opacity:1;stroke:none"><tspan
|
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect171375);display:inline;fill:#000000;fill-opacity:1;stroke:none"><tspan
|
||||||
x="381.5332"
|
x="381.5332"
|
||||||
y="126.50438"
|
y="126.50438"
|
||||||
id="tspan38028"><tspan
|
id="tspan39607"><tspan
|
||||||
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
style="font-size:18.6667px;-inkscape-font-specification:'sans-serif, Normal'"
|
||||||
id="tspan38026">$flyer.osm</tspan></tspan></text>
|
id="tspan39605">$flyer.osm</tspan></tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
@ -40,13 +40,12 @@
|
||||||
"reload": "Reload the data"
|
"reload": "Reload the data"
|
||||||
},
|
},
|
||||||
"flyer": {
|
"flyer": {
|
||||||
|
"aerial": "This map uses a different background, namely aerial imagery by Agentschap Informatie Vlaanderen",
|
||||||
"callToAction": "Test it on mapcomplete.osm.be",
|
"callToAction": "Test it on mapcomplete.osm.be",
|
||||||
|
"examples": "There are many thematic maps available of which a few are printed here, namely the maps with AED's, artwork, cyclestreets, benches and cyclepumps.\n\nThere are many more thematic maps online: about healthcare, indoor navigation, wheelchair accessibility, waste facilities, public bookcases, pedestrian crossings with a rainbow-painting,... Discover them all on mapcomplete.osm.be ",
|
||||||
"frontParagraph": "MapComplete is an easy to use web application to collect geodata in OpenStreetMap, enabling collecting and managing relevant data in an open, crowdsourced and reusable way.\n\nNew categories and attributes can be added upon request.",
|
"frontParagraph": "MapComplete is an easy to use web application to collect geodata in OpenStreetMap, enabling collecting and managing relevant data in an open, crowdsourced and reusable way.\n\nNew categories and attributes can be added upon request.",
|
||||||
"license": {
|
|
||||||
"text": "The webversion is free to use, both for viewing and adding data.\nAdding data requires a free account on OpenStreetMap.org.\n\n MapComplete can tailored to your needs, with new map layers, new functionalities or styled to your organisation styleguide. We also have experience with starting campaigns to crowdsource geodata.\nContact pietervdvn@posteo.net for a quote.\n\nMapComplete is fully Open Source (GPL-licenses).\n\nData on OpenStreetMap is under the ODbL-license, which means all data can be reused for all purposes, as long as attribution is given and all (improvements to) the data are republished under the same license.\nSee osm.org/copyright for more details",
|
|
||||||
"title": "License and pricing"
|
|
||||||
},
|
|
||||||
"mapcomplete": {
|
"mapcomplete": {
|
||||||
|
"customize": "MapComplete can tailored to your needs, with new map layers, new functionalities or styled to your organisation styleguide. We also have experience with starting campaigns to crowdsource geodata.\nContact pietervdvn@posteo.net for a quote.",
|
||||||
"intro": "MapComplete is a website which has {mapCount} interactive maps. Every single map allows to add or update information.",
|
"intro": "MapComplete is a website which has {mapCount} interactive maps. Every single map allows to add or update information.",
|
||||||
"li0": "Communicate where POI are",
|
"li0": "Communicate where POI are",
|
||||||
"li1": "Add new points and update information on existing points",
|
"li1": "Add new points and update information on existing points",
|
||||||
|
@ -56,11 +55,13 @@
|
||||||
"li5": "See aerial imagery and map backgrounds",
|
"li5": "See aerial imagery and map backgrounds",
|
||||||
"li6": "Can be placed in other websites as iFrame",
|
"li6": "Can be placed in other websites as iFrame",
|
||||||
"li7": "Embedded within the OpenStreetMap-ecosystem, which has many tools available",
|
"li7": "Embedded within the OpenStreetMap-ecosystem, which has many tools available",
|
||||||
|
"li8": "Fully open source (GPL) and free to use",
|
||||||
"title": "What is MapComplete?"
|
"title": "What is MapComplete?"
|
||||||
},
|
},
|
||||||
"osm": "OpenStreetMap is an online map which can be edited and reused by anyone for any purpose - just like Wikipedia.\n\nIt is the biggest geospatial database in the world and is reused by thousands of applications and websites.",
|
"osm": "OpenStreetMap is an online map which can be edited and reused by anyone for any purpose as long as attribution is given and the data is kept open.\n\nIt is the biggest geospatial database in the world and is reused by thousands of applications and websites.",
|
||||||
"tagline": "Collect geodata easily with OpenStreetMap",
|
"tagline": "Collect geodata easily with OpenStreetMap",
|
||||||
"title": "MapComplete.osm.be",
|
"title": "MapComplete.osm.be",
|
||||||
|
"toerisme_vlaanderen": "For joint project with Toerism Flanders, 'Pin your point' was created. Over 160 contributors added a few thousand benches and picnictables and spotted 100 charging station for bicycles.",
|
||||||
"whatIsOsm": "What is OpenStreetMap?"
|
"whatIsOsm": "What is OpenStreetMap?"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
|
|
|
@ -40,13 +40,12 @@
|
||||||
"reload": "Herlaad de data"
|
"reload": "Herlaad de data"
|
||||||
},
|
},
|
||||||
"flyer": {
|
"flyer": {
|
||||||
|
"aerial": "Deze kaart gebruikt luchtfoto's van het Agentschap Informatie Vlaanderen als achtergrond.\nOok het GRB is beschikbaar als achtergrondlaag.",
|
||||||
"callToAction": "Probeer het uit op mapcomplete.osm.be",
|
"callToAction": "Probeer het uit op mapcomplete.osm.be",
|
||||||
|
"examples": "Er zijn vele thematische kaarten beschikbaar. Enkele voorbeelden zijn hier geprint, zoals de kaart met AEDs, kunstwerken, fietsstraten, banken en fietspompen.\n\nOnline zijn er nog kaarten met diverse thema's, zoals gezondheidszorg, binnenruimtes, rolstoeltoegankelijkheid, afvalcontainers, boekenruilkasten, regenboog-zebrapaden,... Ontdek ze allemaal mapcomplete.osm.be ",
|
||||||
"frontParagraph": "MapComplete is een web-applicatie om OpenStreetMap-data te tonen en aan te passen op basis van thematische kaarten. Het maakt het mogelijk om open geodata te crowdsourcen en te managen op een makkelijke manier.\n\nNieuwe categorie<69>n en attributen kunnen op vraag worden toegevoegd.",
|
"frontParagraph": "MapComplete is een web-applicatie om OpenStreetMap-data te tonen en aan te passen op basis van thematische kaarten. Het maakt het mogelijk om open geodata te crowdsourcen en te managen op een makkelijke manier.\n\nNieuwe categorie<69>n en attributen kunnen op vraag worden toegevoegd.",
|
||||||
"license": {
|
|
||||||
"text": "De webversie is gratis te gebruiken, zowel voor het bekijken als voor het toevoegen van data.\nVoor het toevoegen van data is een gratis account op OpenStreetMap.org vereist.\n\nWil je een versie op maat? Wil je een versie in jullie huisstijl?\nWil je een nieuwe kaartlaag of functionaliteit? Wil je een crowdsourcing-campagne opzetten?\nNeem contact op met pietervdvn@posteo.net voor een offerte.\n\nMapComplete is volledig OpenSource (GPL-licentie).\n\nData op OpenStreetMap valt onder de ODbL-licentie. Data mag herbruikt worden voor alle doeleinden, mits bronvermelding en het openhouden van (verbeteringen aan) de data.\nZie osm.org/copyright voor alle details.",
|
|
||||||
"title": "Licentie and kostprijs"
|
|
||||||
},
|
|
||||||
"mapcomplete": {
|
"mapcomplete": {
|
||||||
|
"customize": "Wil je een versie op maat? Wil je een versie in jullie huisstijl?\nWil je een nieuwe kaartlaag of functionaliteit? Wil je een crowdsourcing-campagne opzetten?\nNeem contact op met pietervdvn@posteo.net voor een offerte.",
|
||||||
"intro": "MapComplete is een website met {mapCount} interactieve kaarten. Op iedere kaart kunnen gebruikers data zien en updaten.",
|
"intro": "MapComplete is een website met {mapCount} interactieve kaarten. Op iedere kaart kunnen gebruikers data zien en updaten.",
|
||||||
"li0": "Communiceer waar interessepunten zijn",
|
"li0": "Communiceer waar interessepunten zijn",
|
||||||
"li1": "Voeg nieuwe punten toe en update informatie van reeds bestaande punten",
|
"li1": "Voeg nieuwe punten toe en update informatie van reeds bestaande punten",
|
||||||
|
@ -56,11 +55,13 @@
|
||||||
"li5": "Wissel tussen kaart- en luchtfoto's als achtergrond",
|
"li5": "Wissel tussen kaart- en luchtfoto's als achtergrond",
|
||||||
"li6": "Eenvoudig te embedden in een website als iFrame",
|
"li6": "Eenvoudig te embedden in een website als iFrame",
|
||||||
"li7": "Deel van het OpenStreetMap-ecosysteem waarbinnen honderden andere tools bestaan",
|
"li7": "Deel van het OpenStreetMap-ecosysteem waarbinnen honderden andere tools bestaan",
|
||||||
|
"li8": "Volledig Open-Source (GPL) en gratis te gebruiken",
|
||||||
"title": "Wat is MapComplete?"
|
"title": "Wat is MapComplete?"
|
||||||
},
|
},
|
||||||
"osm": "OpenStreetMap is een online kaart die door iedereen aangepast en herbruikt mag worden - net zoals Wikipedia.\n\nHet is de grootste geodatabank ter wereld en wordt herbruikt door miljoenen websites en applicaties.",
|
"osm": "OpenStreetMap is een online kaart die door iedereen aangepast en herbruikt mag worden - mits bronvermelding en het openhouden van de data.\n\nHet is de grootste geodatabank ter wereld en wordt herbruikt door miljoenen websites en applicaties.",
|
||||||
"tagline": "Verzamel geodata eenvoudig met OpenStreetMap",
|
"tagline": "Eenvoudig geodata verzamelen met OpenStreetMap",
|
||||||
"title": "MapComplete.osm.be",
|
"title": "MapComplete.osm.be",
|
||||||
|
"toerisme_vlaanderen": "In samenwerking met Toerisme Vlaanderen werd 'Pin Je Punt' gecre<72>erd. Op enkele maanden tijd werden duizenden zitbanken en picnictafels en meer dan honderd oplaadpunten voor elektrische fietsen toegevoegd aan de kaart door meer dan 160 bijdragers.",
|
||||||
"whatIsOsm": "Wat is OpenStreetMap?"
|
"whatIsOsm": "Wat is OpenStreetMap?"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -39,6 +39,7 @@
|
||||||
"osmtogeojson": "^3.0.0-beta.4",
|
"osmtogeojson": "^3.0.0-beta.4",
|
||||||
"papaparse": "^5.3.1",
|
"papaparse": "^5.3.1",
|
||||||
"prompt-sync": "^4.2.0",
|
"prompt-sync": "^4.2.0",
|
||||||
|
"svg-path-parser": "^1.1.0",
|
||||||
"tailwindcss": "^3.1.8",
|
"tailwindcss": "^3.1.8",
|
||||||
"togpx": "^0.5.4",
|
"togpx": "^0.5.4",
|
||||||
"wikibase-sdk": "^7.14.0",
|
"wikibase-sdk": "^7.14.0",
|
||||||
|
@ -14430,6 +14431,11 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg-path-parser": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A=="
|
||||||
|
},
|
||||||
"node_modules/svg-pathdata": {
|
"node_modules/svg-pathdata": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz",
|
||||||
|
@ -28146,6 +28152,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||||
},
|
},
|
||||||
|
"svg-path-parser": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A=="
|
||||||
|
},
|
||||||
"svg-pathdata": {
|
"svg-pathdata": {
|
||||||
"version": "5.0.5",
|
"version": "5.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-5.0.5.tgz",
|
||||||
|
|
|
@ -99,6 +99,7 @@
|
||||||
"osmtogeojson": "^3.0.0-beta.4",
|
"osmtogeojson": "^3.0.0-beta.4",
|
||||||
"papaparse": "^5.3.1",
|
"papaparse": "^5.3.1",
|
||||||
"prompt-sync": "^4.2.0",
|
"prompt-sync": "^4.2.0",
|
||||||
|
"svg-path-parser": "^1.1.0",
|
||||||
"tailwindcss": "^3.1.8",
|
"tailwindcss": "^3.1.8",
|
||||||
"togpx": "^0.5.4",
|
"togpx": "^0.5.4",
|
||||||
"wikibase-sdk": "^7.14.0",
|
"wikibase-sdk": "^7.14.0",
|
||||||
|
|
49
test.ts
49
test.ts
|
@ -1,36 +1,55 @@
|
||||||
import "./assets/templates/Ubuntu-M-normal.js"
|
|
||||||
import "./assets/templates/Ubuntu-L-normal.js"
|
|
||||||
import "./assets/templates/UbuntuMono-B-bold.js"
|
|
||||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
|
||||||
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
||||||
import {Utils} from "./Utils";
|
|
||||||
import FeaturePipelineState from "./Logic/State/FeaturePipelineState";
|
|
||||||
import Locale from "./UI/i18n/Locale";
|
|
||||||
import {SvgToPdf} from "./Utils/svgToPdf";
|
|
||||||
|
|
||||||
MinimapImplementation.initialize()
|
MinimapImplementation.initialize()
|
||||||
|
|
||||||
|
import {Utils} from "./Utils";
|
||||||
|
import {SvgToPdf, SvgToPdfOptions} from "./Utils/svgToPdf";
|
||||||
|
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
||||||
|
import Locale from "./UI/i18n/Locale";
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
function createElement(): string {
|
||||||
|
const div = document.createElement("div")
|
||||||
|
div.id = "freediv-" + (i++)
|
||||||
|
document.getElementById("extradiv").append(div)
|
||||||
|
return div.id
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
|
||||||
const svg = await Utils.download(window.location.protocol + "//" + window.location.host + "/assets/templates/MapComplete-flyer.svg")
|
const svg = await Utils.download(window.location.protocol + "//" + window.location.host + "/assets/templates/MapComplete-flyer.svg")
|
||||||
const svgBack = await Utils.download(window.location.protocol + "//" + window.location.host + "/assets/templates/MapComplete-flyer.back.svg")
|
const svgBack = await Utils.download(window.location.protocol + "//" + window.location.host + "/assets/templates/MapComplete-flyer.back.svg")
|
||||||
Locale.language.setData("en")
|
|
||||||
/*
|
const options = <SvgToPdfOptions>{
|
||||||
await new SvgToPdf([svg], {
|
getFreeDiv: createElement,
|
||||||
state,
|
|
||||||
textSubstitutions: {
|
textSubstitutions: {
|
||||||
mapCount: "" + Array.from(AllKnownLayouts.allKnownLayouts.values()).filter(th => !th.hideFromOverview).length
|
mapCount: "" + Array.from(AllKnownLayouts.allKnownLayouts.values()).filter(th => !th.hideFromOverview).length
|
||||||
|
},
|
||||||
|
disableMaps: false
|
||||||
}
|
}
|
||||||
}).ConvertSvg("flyer_en.pdf")
|
Locale.language.setData("nl")
|
||||||
|
const back = new SvgToPdf([svgBack], options)
|
||||||
|
const front = await new SvgToPdf([svg], options)
|
||||||
|
await back.ConvertSvg("Flyer-back-nl.pdf")
|
||||||
|
await front.ConvertSvg("Flyer-front-nl.pdf")
|
||||||
|
Locale.language.setData("en")
|
||||||
|
await back.ConvertSvg("Flyer-back-en.pdf")
|
||||||
|
await front.ConvertSvg("Flyer-front-en.pdf")
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
const svg = await Utils.download(window.location.protocol + "//" + window.location.host + "/assets/templates/MapComplete-flyer.svg")
|
||||||
|
|
||||||
//*/
|
|
||||||
|
|
||||||
Locale.language.setData("en")
|
Locale.language.setData("en")
|
||||||
const svgToPdf = new SvgToPdf([svgBack], {
|
const svgToPdf = new SvgToPdf([svgBack], {
|
||||||
freeDivId: "extradiv",
|
getFreeDiv: createElement,
|
||||||
textSubstitutions: {
|
textSubstitutions: {
|
||||||
mapCount: "" + Array.from(AllKnownLayouts.allKnownLayouts.values()).filter(th => !th.hideFromOverview).length
|
mapCount: "" + Array.from(AllKnownLayouts.allKnownLayouts.values()).filter(th => !th.hideFromOverview).length
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
new VariableUiElement(svgToPdf.currentState).AttachTo("maindiv")
|
||||||
await svgToPdf.Prepare()
|
await svgToPdf.Prepare()
|
||||||
console.log("Used translations", svgToPdf._usedTranslations)
|
console.log("Used translations", svgToPdf._usedTranslations)
|
||||||
await svgToPdf.ConvertSvg("flyer_nl.pdf")
|
await svgToPdf.ConvertSvg("flyer_nl.pdf")
|
||||||
|
|
Loading…
Reference in a new issue