forked from MapComplete/MapComplete
Fix: decent PDF-export
This commit is contained in:
parent
905f796baa
commit
de20b00b8f
15 changed files with 619 additions and 1396 deletions
|
@ -1,378 +0,0 @@
|
|||
import Combine from "../Base/Combine"
|
||||
import {FlowPanelFactory, FlowStep} from "../ImportFlow/FlowStep"
|
||||
import {ImmutableStore, Store, UIEventSource} from "../../Logic/UIEventSource"
|
||||
import {InputElement} from "../Input/InputElement"
|
||||
import {SvgToPdf, SvgToPdfOptions} from "../../Utils/svgToPdf"
|
||||
import {FixedInputElement} from "../Input/FixedInputElement"
|
||||
import {FixedUiElement} from "../Base/FixedUiElement"
|
||||
import FileSelectorButton from "../Input/FileSelectorButton"
|
||||
import InputElementMap from "../Input/InputElementMap"
|
||||
import {RadioButton} from "../Input/RadioButton"
|
||||
import {Utils} from "../../Utils"
|
||||
import {VariableUiElement} from "../Base/VariableUIElement"
|
||||
import Loading from "../Base/Loading"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import Img from "../Base/Img"
|
||||
import Title from "../Base/Title"
|
||||
import {CheckBox} from "../Input/Checkboxes"
|
||||
import Toggle from "../Input/Toggle"
|
||||
import List from "../Base/List"
|
||||
import LeftIndex from "../Base/LeftIndex"
|
||||
import Constants from "../../Models/Constants"
|
||||
import Toggleable from "../Base/Toggleable"
|
||||
import Lazy from "../Base/Lazy"
|
||||
import LinkToWeblate from "../Base/LinkToWeblate"
|
||||
import Link from "../Base/Link"
|
||||
import {AllLanguagesSelector} from "../Popup/AllLanguagesSelector"
|
||||
|
||||
class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> {
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ title: string; pages: string[] }>
|
||||
|
||||
constructor() {
|
||||
const elements: InputElement<{ templateName: string; pages: string[] }>[] = []
|
||||
for (const templateName in SvgToPdf.templates) {
|
||||
const template = SvgToPdf.templates[templateName]
|
||||
elements.push(
|
||||
new FixedInputElement(
|
||||
new Combine([
|
||||
new FixedUiElement(templateName).SetClass("font-bold pr-2"),
|
||||
template.description,
|
||||
]),
|
||||
new UIEventSource({ templateName, pages: template.pages })
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const file = new FileSelectorButton(
|
||||
new FixedUiElement("Select an svg image which acts as template"),
|
||||
{
|
||||
acceptType: "image/svg+xml",
|
||||
allowMultiple: true,
|
||||
}
|
||||
)
|
||||
const fileMapped = new InputElementMap<
|
||||
FileList,
|
||||
{ templateName: string; pages: string[]; fromFile: true }
|
||||
>(
|
||||
file,
|
||||
(x0, x1) => x0 === x1,
|
||||
(filelist) => {
|
||||
if (filelist === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const pages = []
|
||||
let templateName: string = undefined
|
||||
for (const file of Array.from(filelist)) {
|
||||
if (templateName == undefined) {
|
||||
templateName = file.name.substring(file.name.lastIndexOf("/") + 1)
|
||||
templateName = templateName.substring(0, templateName.lastIndexOf("."))
|
||||
}
|
||||
pages.push(file.text())
|
||||
}
|
||||
|
||||
return {
|
||||
templateName,
|
||||
pages,
|
||||
fromFile: true,
|
||||
}
|
||||
},
|
||||
(_) => undefined
|
||||
)
|
||||
elements.push(fileMapped)
|
||||
const radio = new RadioButton(elements, { selectFirstAsDefault: true })
|
||||
|
||||
const loaded: Store<{ success: { title: string; pages: string[] } } | { error: any }> =
|
||||
radio.GetValue().bind((template) => {
|
||||
if (template === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (template["fromFile"]) {
|
||||
return UIEventSource.FromPromiseWithErr(
|
||||
Promise.all(template.pages).then((pages) => ({
|
||||
title: template.templateName,
|
||||
pages,
|
||||
}))
|
||||
)
|
||||
}
|
||||
const urls = template.pages.map((p) => SelectTemplate.ToUrl(p))
|
||||
const dloadAll: Promise<{ title: string; pages: string[] }> = Promise.all(
|
||||
urls.map((url) => Utils.download(url))
|
||||
).then((pages) => ({
|
||||
pages,
|
||||
title: template.templateName,
|
||||
}))
|
||||
|
||||
return UIEventSource.FromPromiseWithErr(dloadAll)
|
||||
})
|
||||
const preview = new VariableUiElement(
|
||||
loaded.map((pages) => {
|
||||
if (pages === undefined) {
|
||||
return new Loading()
|
||||
}
|
||||
if (pages["error"] !== undefined) {
|
||||
return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass(
|
||||
"alert"
|
||||
)
|
||||
}
|
||||
const svgs = pages["success"].pages
|
||||
if (svgs.length === 0) {
|
||||
return new FixedUiElement("No pages loaded...").SetClass("alert")
|
||||
}
|
||||
const els: BaseUIElement[] = []
|
||||
for (const pageSrc of svgs) {
|
||||
const el = new Img(pageSrc, true).SetClass("w-96 m-2 border-black border-2")
|
||||
els.push(el)
|
||||
}
|
||||
return new Combine(els).SetClass("flex border border-subtle rounded-xl")
|
||||
})
|
||||
)
|
||||
|
||||
super([new Title("Select template"), radio, new Title("Preview"), preview])
|
||||
this.Value = loaded.map((l) => (l === undefined ? undefined : l["success"]))
|
||||
this.IsValid = this.Value.map((v) => v !== undefined)
|
||||
}
|
||||
|
||||
public static ToUrl(spec: string) {
|
||||
if (spec.startsWith("http")) {
|
||||
return spec
|
||||
}
|
||||
let path = window.location.pathname
|
||||
path = path.substring(0, path.lastIndexOf("/"))
|
||||
return window.location.protocol + "//" + window.location.host + path + "/" + spec
|
||||
}
|
||||
}
|
||||
|
||||
class SelectPdfOptions
|
||||
extends Combine
|
||||
implements FlowStep<{ title: string; pages: string[]; options: SvgToPdfOptions }>
|
||||
{
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ title: string; pages: string[]; options: SvgToPdfOptions }>
|
||||
|
||||
constructor(title: string, pages: string[], getFreeDiv: () => string) {
|
||||
const dummy = new CheckBox("Don't add data to the map (to quickly preview the PDF)", false)
|
||||
const overrideMapLocation = new CheckBox(
|
||||
"Override map location: use a selected location instead of the location set in the template",
|
||||
false
|
||||
)
|
||||
const locationInput = Minimap.createMiniMap().SetClass("block w-full")
|
||||
const searchField = undefined // new SearchAndGo({ leafletMap: locationInput.leafletMap })
|
||||
const selectLocation = new Combine([
|
||||
new Toggle(
|
||||
new Combine([new Title("Select override location"), searchField]).SetClass("flex"),
|
||||
undefined,
|
||||
overrideMapLocation.GetValue()
|
||||
),
|
||||
new Toggle(
|
||||
locationInput.SetStyle("height: 20rem"),
|
||||
undefined,
|
||||
overrideMapLocation.GetValue()
|
||||
).SetStyle("height: 20rem"),
|
||||
])
|
||||
.SetClass("block")
|
||||
.SetStyle("height: 25rem")
|
||||
super([new Title("Select options"), dummy, overrideMapLocation, selectLocation])
|
||||
this.Value = dummy.GetValue().map(
|
||||
(disableMaps) => {
|
||||
return {
|
||||
pages,
|
||||
title,
|
||||
options: <SvgToPdfOptions>{
|
||||
disableMaps,
|
||||
getFreeDiv,
|
||||
overrideLocation: overrideMapLocation.GetValue().data
|
||||
? locationInput.location.data
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
[overrideMapLocation.GetValue(), locationInput.location]
|
||||
)
|
||||
this.IsValid = new ImmutableStore(true)
|
||||
}
|
||||
}
|
||||
|
||||
class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }> {
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>
|
||||
|
||||
constructor(title: string, pages: string[], options: SvgToPdfOptions) {
|
||||
const svgToPdf = new SvgToPdf(title, pages, options)
|
||||
const languageSelector = new AllLanguagesSelector()
|
||||
const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare())
|
||||
|
||||
super([
|
||||
new Title("Select languages..."),
|
||||
languageSelector,
|
||||
new Toggle(
|
||||
new Loading("Preparing maps..."),
|
||||
undefined,
|
||||
isPrepared.map((p) => p === undefined)
|
||||
),
|
||||
])
|
||||
this.Value = isPrepared.map(
|
||||
(isPrepped) => {
|
||||
if (isPrepped === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (isPrepped["success"] !== undefined) {
|
||||
const svgToPdf = isPrepped["success"]
|
||||
const langs = languageSelector.GetValue().data
|
||||
console.log("Languages are", langs)
|
||||
if (langs.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return { svgToPdf, languages: langs }
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
[languageSelector.GetValue()]
|
||||
)
|
||||
this.IsValid = this.Value.map((v) => v !== undefined)
|
||||
}
|
||||
}
|
||||
|
||||
class InspectStrings
|
||||
extends Toggle
|
||||
implements FlowStep<{ svgToPdf: SvgToPdf; languages: string[] }>
|
||||
{
|
||||
readonly IsValid: Store<boolean>
|
||||
readonly Value: Store<{ svgToPdf: SvgToPdf; languages: string[] }>
|
||||
|
||||
constructor(svgToPdf: SvgToPdf, languages: string[]) {
|
||||
const didLoadLanguages = UIEventSource.FromPromiseWithErr(
|
||||
svgToPdf.PrepareLanguages(languages)
|
||||
).map((l) => l !== undefined && l["success"] !== undefined)
|
||||
|
||||
super(
|
||||
new Combine([
|
||||
new Title("Inspect translation strings"),
|
||||
...languages.map(
|
||||
(l) => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l))
|
||||
),
|
||||
]),
|
||||
new Loading(),
|
||||
didLoadLanguages
|
||||
)
|
||||
this.Value = new ImmutableStore({ svgToPdf, languages })
|
||||
this.IsValid = didLoadLanguages
|
||||
}
|
||||
|
||||
private static createOverviewPanel(svgToPdf: SvgToPdf, language: string): BaseUIElement {
|
||||
const elements: BaseUIElement[] = []
|
||||
let foundTranslations = 0
|
||||
const allKeys = Array.from(svgToPdf.translationKeys())
|
||||
for (const translationKey of allKeys) {
|
||||
let spec = translationKey
|
||||
if (translationKey.startsWith("layer.")) {
|
||||
spec = "layers:" + translationKey.substring(6)
|
||||
} else {
|
||||
spec = "core:" + translationKey
|
||||
}
|
||||
const translated = svgToPdf.getTranslation("$" + translationKey, language, true)
|
||||
if (translated) {
|
||||
foundTranslations++
|
||||
}
|
||||
const linkToWeblate = new Link(
|
||||
spec,
|
||||
LinkToWeblate.hrefToWeblate(language, spec),
|
||||
true
|
||||
).SetClass("font-bold link-underline")
|
||||
elements.push(
|
||||
new Combine([
|
||||
linkToWeblate,
|
||||
" ",
|
||||
translated ?? new FixedUiElement("No translation found!").SetClass("alert"),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
return new Toggleable(
|
||||
new Title("Translations for " + language),
|
||||
new Combine([
|
||||
`${foundTranslations}/${allKeys.length} of translations are found (${Math.floor(
|
||||
(100 * foundTranslations) / allKeys.length
|
||||
)}%)`,
|
||||
"The following keys are used:",
|
||||
new List(elements),
|
||||
]),
|
||||
{ closeOnClick: false, height: "15rem" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SavePdf extends Combine {
|
||||
constructor(svgToPdf: SvgToPdf, languages: string[]) {
|
||||
super([
|
||||
new Title("Generating your pdfs..."),
|
||||
new List(
|
||||
languages.map(
|
||||
(lng) =>
|
||||
new Toggle(
|
||||
lng + " is done!",
|
||||
new Loading("Creating pdf for " + lng),
|
||||
UIEventSource.FromPromiseWithErr(
|
||||
svgToPdf.ConvertSvg(lng).then(() => true)
|
||||
).map((x) => x !== undefined && x["success"] === true)
|
||||
)
|
||||
)
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export class PdfExportGui extends LeftIndex {
|
||||
constructor(freeDivId: string) {
|
||||
let i = 0
|
||||
const createDiv = (): string => {
|
||||
const div = document.createElement("div")
|
||||
div.id = "freediv-" + i++
|
||||
document.getElementById(freeDivId).append(div)
|
||||
return div.id
|
||||
}
|
||||
|
||||
Constants.defaultOverpassUrls.splice(0, 1)
|
||||
const { flow, furthestStep, titles } = FlowPanelFactory.start(
|
||||
new Title("Select template"),
|
||||
new SelectTemplate()
|
||||
)
|
||||
.then(
|
||||
new Title("Select options"),
|
||||
({ title, pages }) => new SelectPdfOptions(title, pages, createDiv)
|
||||
)
|
||||
.then(
|
||||
"Generate maps...",
|
||||
({ title, pages, options }) => new PreparePdf(title, pages, options)
|
||||
)
|
||||
.then(
|
||||
"Inspect translations",
|
||||
({ svgToPdf, languages }) => new InspectStrings(svgToPdf, languages)
|
||||
)
|
||||
.finish("Generating...", ({ svgToPdf, languages }) => new SavePdf(svgToPdf, languages))
|
||||
|
||||
const toc = new List(
|
||||
titles.map(
|
||||
(title, i) =>
|
||||
new VariableUiElement(
|
||||
furthestStep.map((currentStep) => {
|
||||
if (i > currentStep) {
|
||||
return new Combine([title]).SetClass("subtle")
|
||||
}
|
||||
if (i == currentStep) {
|
||||
return new Combine([title]).SetClass("font-bold")
|
||||
}
|
||||
if (i < currentStep) {
|
||||
return title
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
true
|
||||
)
|
||||
|
||||
const leftContents: BaseUIElement[] = [toc].map((el) => el?.SetClass("pl-4"))
|
||||
|
||||
super(leftContents, flow)
|
||||
}
|
||||
}
|
|
@ -10,12 +10,13 @@
|
|||
import DownloadHelper from "./DownloadHelper";
|
||||
import {Utils} from "../../Utils";
|
||||
import type {PriviligedLayerType} from "../../Models/Constants";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
export let extension: string
|
||||
export let mimetype: string
|
||||
export let construct: (geojsonCleaned: FeatureCollection) => (Blob | string) | Promise<void>
|
||||
export let construct: (geojsonCleaned: FeatureCollection, title: string) => (Blob | string) | Promise<void>
|
||||
export let mainText: Translation
|
||||
export let helperText: Translation
|
||||
export let metaIsIncluded: boolean
|
||||
|
@ -26,51 +27,63 @@
|
|||
let isExporting = false
|
||||
let isError = false
|
||||
|
||||
let status: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
|
||||
async function clicked() {
|
||||
isExporting = true
|
||||
const gpsLayer = state.layerState.filteredLayers.get(
|
||||
<PriviligedLayerType>"gps_location"
|
||||
)
|
||||
state.lastClickObject.features.setData([])
|
||||
|
||||
|
||||
const gpsIsDisplayed = gpsLayer.isDisplayed.data
|
||||
try {
|
||||
gpsLayer.isDisplayed.setData(false)
|
||||
const geojson: FeatureCollection = downloadHelper.getCleanGeoJson(metaIsIncluded)
|
||||
const name = state.layout.id
|
||||
|
||||
const promise = construct(geojson)
|
||||
const title = `MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.${extension}`
|
||||
const promise = construct(geojson, title)
|
||||
let data: Blob | string
|
||||
if (typeof promise === "string") {
|
||||
data = promise
|
||||
} else if (typeof promise["then"] === "function") {
|
||||
data = await <Promise<Blob | string>> promise
|
||||
data = await <Promise<Blob | string>>promise
|
||||
} else {
|
||||
data = <Blob>promise
|
||||
}
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
console.log("Got data", data)
|
||||
Utils.offerContentsAsDownloadableFile(
|
||||
data,
|
||||
`MapComplete_${name}_export_${new Date().toISOString().substr(0, 19)}.${extension}`,
|
||||
title,
|
||||
{
|
||||
mimetype,
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
isError = true
|
||||
console.error(e)
|
||||
} finally {
|
||||
isExporting = false
|
||||
gpsLayer.isDisplayed.setData(gpsIsDisplayed)
|
||||
}
|
||||
gpsLayer.isDisplayed.setData(gpsIsDisplayed)
|
||||
|
||||
isExporting = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if isError}
|
||||
<Tr cls="alert" t={Translations.t.error}/>
|
||||
<Tr cls="alert" t={Translations.t.general.error}/>
|
||||
{:else if isExporting}
|
||||
<Loading>
|
||||
<Tr t={t.exporting}/>
|
||||
{#if $status}
|
||||
{$status}
|
||||
{:else}
|
||||
<Tr t={t.exporting}/>
|
||||
{/if}
|
||||
</Loading>
|
||||
{:else}
|
||||
<button class="flex w-full" on:click={clicked}>
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
<script lang="ts">
|
||||
|
||||
import type {SpecialVisualizationState} from "../SpecialVisualization";
|
||||
import Loading from "../Base/Loading.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import DownloadHelper from "./DownloadHelper";
|
||||
import DownloadButton from "./DownloadButton.svelte";
|
||||
import {GeoOperations} from "../../Logic/GeoOperations";
|
||||
import {SvgToPdf} from "../../Utils/svgToPdf";
|
||||
import Locale from "../i18n/Locale";
|
||||
import ThemeViewState from "../../Models/ThemeViewState";
|
||||
import {Utils} from "../../Utils";
|
||||
import Constants from "../../Models/Constants";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import {AvailableRasterLayers} from "../../Models/RasterLayers";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let state: ThemeViewState
|
||||
let isLoading = state.dataIsLoading
|
||||
|
||||
const t = Translations.t.general.download
|
||||
|
@ -30,6 +37,30 @@
|
|||
})
|
||||
}
|
||||
|
||||
async function constructPdf(_, title: string, status: UIEventSource<string>) {
|
||||
const templateUrls = SvgToPdf.templates["current_view_a3"].pages
|
||||
const templates: string[] = await Promise.all(templateUrls.map(url => Utils.download(url)))
|
||||
console.log("Templates are", templates)
|
||||
const bg = state.mapProperties.rasterLayer.data ?? AvailableRasterLayers.maplibre
|
||||
const creator = new SvgToPdf(title, templates, {
|
||||
state,
|
||||
freeComponentId: "belowmap",
|
||||
textSubstitutions: <Record<string, string>> {
|
||||
"layout.title": state.layout.title,
|
||||
title: state.layout.title,
|
||||
layoutImg: state.layout.icon,
|
||||
version: Constants.vNumber,
|
||||
date: new Date().toISOString().substring(0,16),
|
||||
background: new Translation(bg.properties.name).txt
|
||||
}
|
||||
})
|
||||
|
||||
const unsub = creator.status.addCallbackAndRunD(s => status?.setData(s))
|
||||
await creator.ExportPdf(Locale.language.data)
|
||||
unsub()
|
||||
return undefined
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -85,7 +116,7 @@
|
|||
extension="pdf"
|
||||
mainText={t.downloadAsPdf}
|
||||
helperText={t.downloadAsPdfHelper}
|
||||
construct={_ => state.mapProperties.exportAsPng(4)}
|
||||
construct={constructPdf}
|
||||
/>
|
||||
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
return url
|
||||
}
|
||||
|
||||
private static setDpi(drawOn: HTMLCanvasElement, ctx: CanvasRenderingContext2D, dpiFactor: number) {
|
||||
public static setDpi(drawOn: HTMLCanvasElement, ctx: CanvasRenderingContext2D, dpiFactor: number) {
|
||||
drawOn.style.width = drawOn.style.width || drawOn.width + "px"
|
||||
drawOn.style.height = drawOn.style.height || drawOn.height + "px"
|
||||
|
||||
|
@ -223,23 +223,16 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
console.log("Canvas size:", drawOn.width, drawOn.height)
|
||||
const ctx = drawOn.getContext("2d")
|
||||
// Set up CSS size.
|
||||
MapLibreAdaptor.setDpi(drawOn, ctx, dpiFactor)
|
||||
MapLibreAdaptor.setDpi(drawOn, ctx, dpiFactor / map.getPixelRatio())
|
||||
|
||||
await this.exportBackgroundOnCanvas(drawOn, ctx, dpiFactor)
|
||||
await this.exportBackgroundOnCanvas(ctx)
|
||||
|
||||
drawOn.toBlob(blob => {
|
||||
Utils.offerContentsAsDownloadableFile(blob, "bg.png")
|
||||
})
|
||||
console.log("Getting markers")
|
||||
// MapLibreAdaptor.setDpi(drawOn, ctx, 1)
|
||||
const markers = await this.drawMarkers(dpiFactor)
|
||||
console.log("Drawing markers (" + markers.width + "*" + markers.height + ") onto drawOn (" + drawOn.width + "*" + drawOn.height + ")")
|
||||
ctx.scale(1/dpiFactor,1/dpiFactor )
|
||||
ctx.drawImage(markers, 0, 0, drawOn.width, drawOn.height)
|
||||
ctx.scale(dpiFactor, dpiFactor)
|
||||
markers.toBlob(blob => {
|
||||
Utils.offerContentsAsDownloadableFile(blob, "markers.json")
|
||||
})
|
||||
this._maplibreMap.data?.resize()
|
||||
return await new Promise<Blob>(resolve => drawOn.toBlob(blob => resolve(blob)))
|
||||
}
|
||||
|
@ -248,7 +241,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
|||
* Exports the background map and lines to PNG.
|
||||
* Markers are _not_ rendered
|
||||
*/
|
||||
private async exportBackgroundOnCanvas(drawOn: HTMLCanvasElement, ctx: CanvasRenderingContext2D, dpiFactor: number = 1): Promise<void> {
|
||||
private async exportBackgroundOnCanvas(ctx: CanvasRenderingContext2D): Promise<void> {
|
||||
const map = this._maplibreMap.data
|
||||
// We draw the maplibre-map onto the canvas. This does not export markers
|
||||
// Inspiration by https://github.com/mapbox/mapbox-gl-js/issues/2766
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { Map } from "@onsvisual/svelte-maps";
|
||||
import type { Map as MaplibreMap } from "maplibre-gl";
|
||||
import type { Writable } from "svelte/store";
|
||||
import {AvailableRasterLayers} from "../../Models/RasterLayers";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -24,7 +25,7 @@
|
|||
$map.resize();
|
||||
});
|
||||
});
|
||||
const styleUrl = "https://api.maptiler.com/maps/15cc8f61-0353-4be6-b8da-13daea5f7432/style.json?key=GvoVAJgu46I5rZapJuAy";
|
||||
const styleUrl = AvailableRasterLayers.maplibre.properties.url;
|
||||
</script>
|
||||
<main>
|
||||
<Map bind:center={center}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue