Formatting

This commit is contained in:
Pieter Vander Vennet 2022-10-27 01:50:41 +02:00
parent 6d822b42ca
commit 61aebc61eb
32 changed files with 664 additions and 511 deletions

View file

@ -7,7 +7,6 @@ import Svg from "../../Svg"
* The little 'translate'-icon next to every icon + some static helper functions
*/
export default class LinkToWeblate extends VariableUiElement {
constructor(context: string, availableTranslations: object) {
super(
Locale.language.map(

View file

@ -3,7 +3,7 @@ import Loc from "../../Models/Loc"
import BaseLayer from "../../Models/BaseLayer"
import { UIEventSource } from "../../Logic/UIEventSource"
import { BBox } from "../../Logic/BBox"
import {deprecate} from "util";
import { deprecate } from "util"
export interface MinimapOptions {
background?: UIEventSource<BaseLayer>
@ -27,7 +27,7 @@ export interface MinimapObj {
TakeScreenshot(format): Promise<string>
TakeScreenshot(format: "image"): Promise<string>
TakeScreenshot(format:"blob"): Promise<Blob>
TakeScreenshot(format: "blob"): Promise<Blob>
TakeScreenshot(format?: "image" | "blob"): Promise<string | Blob>
}

View file

@ -114,22 +114,22 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
* @param format: image: give a base64 encoded png image;
* @constructor
*/
public async TakeScreenshot(): Promise<string> ;
public async TakeScreenshot(format: "image"): Promise<string> ;
public async TakeScreenshot(format: "blob"): Promise<Blob> ;
public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> ;
public async TakeScreenshot(): Promise<string>
public async TakeScreenshot(format: "image"): Promise<string>
public async TakeScreenshot(format: "blob"): Promise<Blob>
public async TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>
public async TakeScreenshot(format: "image" | "blob" = "image"): Promise<string | Blob> {
console.log("Taking a screenshot...")
const screenshotter = new SimpleMapScreenshoter()
screenshotter.addTo(this.leafletMap.data)
const result = <any> await screenshotter.takeScreen((<any> format) ?? "image")
if(format === "image" && typeof result === "string"){
const result = <any>await screenshotter.takeScreen(<any>format ?? "image")
if (format === "image" && typeof result === "string") {
return result
}
if(format === "blob" && result instanceof Blob){
if (format === "blob" && result instanceof Blob) {
return result
}
throw "Something went wrong while creating the screenshot: "+result
throw "Something went wrong while creating the screenshot: " + result
}
protected InnerConstructElement(): HTMLElement {

View file

@ -1,62 +1,73 @@
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 Minimap from "../Base/Minimap";
import SearchAndGo from "./SearchAndGo";
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 {SearchablePillsSelector} from "../Input/SearchableMappingsSelector";
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 Minimap from "../Base/Minimap"
import SearchAndGo from "./SearchAndGo"
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 { SearchablePillsSelector } from "../Input/SearchableMappingsSelector"
import * as languages from "../../assets/language_translations.json"
import {Translation} from "../i18n/Translation";
import { Translation } from "../i18n/Translation"
class SelectTemplate extends Combine implements FlowStep<{ title: string, pages: string[] }> {
readonly IsValid: Store<boolean>;
readonly Value: Store<{ title: string, pages: string[] }>;
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[] }>[] = []
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})))
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,
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;
return undefined
}
const pages = []
let templateName: string = undefined;
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("."))
@ -67,40 +78,46 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
return {
templateName,
pages,
fromFile: true
fromFile: true,
}
},
_ => undefined
(_) => undefined
)
elements.push(fileMapped)
const radio = new RadioButton(elements, {selectFirstAsDefault: true})
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 => ({
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,
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)
})
return UIEventSource.FromPromiseWithErr(dloadAll)
})
const preview = new VariableUiElement(
loaded.map(pages => {
loaded.map((pages) => {
if (pages === undefined) {
return new Loading()
}
if (pages["error"] !== undefined) {
return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass("alert")
return new FixedUiElement("Loading preview failed: " + pages["err"]).SetClass(
"alert"
)
}
const svgs = pages["success"].pages
if (svgs.length === 0) {
@ -108,22 +125,16 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
}
const els: BaseUIElement[] = []
for (const pageSrc of svgs) {
const el = new Img(pageSrc, true)
.SetClass("w-96 m-2 border-black border-2")
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");
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)
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) {
@ -134,63 +145,78 @@ class SelectTemplate extends Combine implements FlowStep<{ title: string, pages:
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 }>;
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 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 = 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
const searchField = 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])
},
[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[] }>;
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 languageOptions = [
new FixedInputElement("Nederlands", "nl"),
new FixedInputElement("English", "en")
new FixedInputElement("English", "en"),
]
const langs: string[] = Array.from(Object.keys(languages["default"] ?? languages))
console.log("Available languages are:", langs)
const languageSelector = new SearchablePillsSelector(
langs.map(l => ({
langs.map((l) => ({
show: new Translation(languages[l]),
value: l,
mainTerm: languages[l]
})), {
mode: "select-many"
mainTerm: languages[l],
})),
{
mode: "select-many",
}
)
@ -202,45 +228,54 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf, langu
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) {
isPrepared.map((p) => p === undefined)
),
])
this.Value = isPrepared.map(
(isPrepped) => {
if (isPrepped === undefined) {
return undefined
}
return {svgToPdf, languages: langs}
}
return undefined;
}, [languageSelector.GetValue()])
this.IsValid = this.Value.map(v => v !== 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[] }>;
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)
const didLoadLanguages = UIEventSource.FromPromiseWithErr(svgToPdf.PrepareLanguages(languages)).map(l => l !== undefined && l["success"] !== undefined)
super(new Combine([
super(
new Combine([
new Title("Inspect translation strings"),
...languages.map(l => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l)))
...languages.map(
(l) => new Lazy(() => InspectStrings.createOverviewPanel(svgToPdf, l))
),
]),
new Loading(),
didLoadLanguages
);
this.Value = new ImmutableStore({svgToPdf, languages})
)
this.Value = new ImmutableStore({ svgToPdf, languages })
this.IsValid = didLoadLanguages
}
@ -259,64 +294,82 @@ class InspectStrings extends Toggle implements FlowStep<{ svgToPdf: SvgToPdf, la
if (translated) {
foundTranslations++
}
const linkToWeblate = new Link(spec, LinkToWeblate.hrefToWeblate(language, spec), true).SetClass("font-bold link-underline")
elements.push(new Combine([
linkToWeblate,
"&nbsp;",
translated ?? new FixedUiElement("No translation found!").SetClass("alert")
]))
const linkToWeblate = new Link(
spec,
LinkToWeblate.hrefToWeblate(language, spec),
true
).SetClass("font-bold link-underline")
elements.push(
new Combine([
linkToWeblate,
"&nbsp;",
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)}%)`,
`${foundTranslations}/${allKeys.length} of translations are found (${Math.floor(
(100 * foundTranslations) / allKeys.length
)}%)`,
"The following keys are used:",
new List(elements)
new List(elements),
]),
{closeOnClick: false, height: "15rem"})
{ 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)
)))
]);
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++)
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 { 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(
@ -338,9 +391,7 @@ export class PdfExportGui extends LeftIndex {
true
)
const leftContents: BaseUIElement[] = [
toc
].map((el) => el?.SetClass("pl-4"))
const leftContents: BaseUIElement[] = [toc].map((el) => el?.SetClass("pl-4"))
super(leftContents, flow)
}

View file

@ -26,7 +26,7 @@ import BaseLayer from "../../Models/BaseLayer"
import Loading from "../Base/Loading"
import Hash from "../../Logic/Web/Hash"
import { GlobalFilter } from "../../Logic/State/MapState"
import {WayId} from "../../Models/OsmFeature";
import { WayId } from "../../Models/OsmFeature"
/*
* The SimpleAddUI is a single panel, which can have multiple states:

View file

@ -3,10 +3,10 @@ import { UIEventSource } from "../../Logic/UIEventSource"
import { Utils } from "../../Utils"
import BaseUIElement from "../BaseUIElement"
import InputElementMap from "./InputElementMap"
import Translations from "../i18n/Translations";
import Translations from "../i18n/Translations"
export class CheckBox extends InputElementMap<number[], boolean> {
constructor(el: (BaseUIElement | string), defaultValue?: boolean) {
constructor(el: BaseUIElement | string, defaultValue?: boolean) {
super(
new CheckBoxes([Translations.W(el)]),
(x0, x1) => x0 === x1,

View file

@ -1,38 +1,40 @@
import {ReadonlyInputElement} from "./InputElement"
import { ReadonlyInputElement } from "./InputElement"
import Loc from "../../Models/Loc"
import {Store, UIEventSource} from "../../Logic/UIEventSource"
import Minimap, {MinimapObj} from "../Base/Minimap"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import Minimap, { MinimapObj } from "../Base/Minimap"
import BaseLayer from "../../Models/BaseLayer"
import Combine from "../Base/Combine"
import Svg from "../../Svg"
import {GeoOperations} from "../../Logic/GeoOperations"
import { GeoOperations } from "../../Logic/GeoOperations"
import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import {BBox} from "../../Logic/BBox"
import {FixedUiElement} from "../Base/FixedUiElement"
import { BBox } from "../../Logic/BBox"
import { FixedUiElement } from "../Base/FixedUiElement"
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
import BaseUIElement from "../BaseUIElement"
import Toggle from "./Toggle"
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";
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
import {RelationId, WayId} from "../../Models/OsmFeature";
import {Feature, LineString, Polygon} from "geojson";
import {OsmObject, OsmWay} from "../../Logic/Osm/OsmObject";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import FilteredLayer from "../../Models/FilteredLayer"
import { ElementStorage } from "../../Logic/ElementStorage"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import { RelationId, WayId } from "../../Models/OsmFeature"
import { Feature, LineString, Polygon } from "geojson"
import { OsmObject, OsmWay } from "../../Logic/Osm/OsmObject"
export default class LocationInput
extends BaseUIElement
implements ReadonlyInputElement<Loc>, MinimapObj {
implements ReadonlyInputElement<Loc>, MinimapObj
{
private static readonly matchLayer = new LayerConfig(
matchpoint,
"LocationInput.matchpoint",
true
)
public readonly snappedOnto: UIEventSource<Feature & { properties : { id : WayId} }> = new UIEventSource(undefined)
public readonly snappedOnto: UIEventSource<Feature & { properties: { id: WayId } }> =
new UIEventSource(undefined)
public readonly _matching_layer: LayerConfig
public readonly leafletMap: UIEventSource<any>
public readonly bounds
@ -43,13 +45,15 @@ export default class LocationInput
* The features to which the input should be snapped
* @private
*/
private readonly _snapTo: Store< (Feature<LineString | Polygon> & {properties: {id : WayId}})[]>
private readonly _snapTo: Store<
(Feature<LineString | Polygon> & { properties: { id: WayId } })[]
>
/**
* The features to which the input should be snapped without cleanup of relations and memberships
* Used for rendering
* @private
*/
private readonly _snapToRaw: Store< {feature: Feature}[]>
private readonly _snapToRaw: Store<{ feature: Feature }[]>
private readonly _value: Store<Loc>
private readonly _snappedPoint: Store<any>
private readonly _maxSnapDistance: number
@ -59,10 +63,10 @@ export default class LocationInput
private readonly clickLocation: UIEventSource<Loc>
private readonly _minZoom: number
private readonly _state: {
readonly filteredLayers: Store<FilteredLayer[]>;
readonly backgroundLayer: UIEventSource<BaseLayer>;
readonly layoutToUse: LayoutConfig;
readonly selectedElement: UIEventSource<any>;
readonly filteredLayers: Store<FilteredLayer[]>
readonly backgroundLayer: UIEventSource<BaseLayer>
readonly layoutToUse: LayoutConfig
readonly selectedElement: UIEventSource<any>
readonly allElements: ElementStorage
}
@ -74,27 +78,35 @@ export default class LocationInput
*
* @private
*/
private static async prepareSnapOnto(features: Feature[]): Promise<(Feature<LineString | Polygon> & {properties : {id: WayId}})[]> {
const linesAndPolygon : Feature<LineString | Polygon>[] = <any> features.filter(f => f.geometry.type !== "Point")
private static async prepareSnapOnto(
features: Feature[]
): Promise<(Feature<LineString | Polygon> & { properties: { id: WayId } })[]> {
const linesAndPolygon: Feature<LineString | Polygon>[] = <any>(
features.filter((f) => f.geometry.type !== "Point")
)
// Clean the features: multipolygons are split into their it's members
const linestrings : (Feature<LineString | Polygon> & {properties: {id: WayId}})[] = []
const linestrings: (Feature<LineString | Polygon> & { properties: { id: WayId } })[] = []
for (const feature of linesAndPolygon) {
if(feature.properties.id.startsWith("way")){
if (feature.properties.id.startsWith("way")) {
// A normal way - we continue
linestrings.push(<any> feature)
linestrings.push(<any>feature)
continue
}
// We have a multipolygon, thus: a relation
// Download the members
const relation = await OsmObject.DownloadObjectAsync(<RelationId> feature.properties.id, 60 * 60)
const members: OsmWay[] = await Promise.all(relation.members
.filter(m => m.type === "way")
.map(m => OsmObject.DownloadObjectAsync(<WayId> ("way/"+m.ref), 60 * 60)))
linestrings.push(...members.map(m => m.asGeoJson()))
const relation = await OsmObject.DownloadObjectAsync(
<RelationId>feature.properties.id,
60 * 60
)
const members: OsmWay[] = await Promise.all(
relation.members
.filter((m) => m.type === "way")
.map((m) => OsmObject.DownloadObjectAsync(<WayId>("way/" + m.ref), 60 * 60))
)
linestrings.push(...members.map((m) => m.asGeoJson()))
}
return linestrings
}
constructor(options?: {
@ -107,20 +119,32 @@ export default class LocationInput
centerLocation?: UIEventSource<Loc>
bounds?: UIEventSource<BBox>
state?: {
readonly filteredLayers: Store<FilteredLayer[]>;
readonly backgroundLayer: UIEventSource<BaseLayer>;
readonly layoutToUse: LayoutConfig;
readonly selectedElement: UIEventSource<any>;
readonly filteredLayers: Store<FilteredLayer[]>
readonly backgroundLayer: UIEventSource<BaseLayer>
readonly layoutToUse: LayoutConfig
readonly selectedElement: UIEventSource<any>
readonly allElements: ElementStorage
}
}) {
super()
this._snapToRaw = options?.snapTo?.map(feats => feats.filter(f => f.feature.geometry.type !== "Point"))
this._snapTo = options?.snapTo?.bind((features) => UIEventSource.FromPromise(LocationInput.prepareSnapOnto(features.map(f => f.feature))))?.map(f => f ?? [])
this._snapToRaw = options?.snapTo?.map((feats) =>
feats.filter((f) => f.feature.geometry.type !== "Point")
)
this._snapTo = options?.snapTo
?.bind((features) =>
UIEventSource.FromPromise(
LocationInput.prepareSnapOnto(features.map((f) => f.feature))
)
)
?.map((f) => f ?? [])
this._maxSnapDistance = options?.maxSnapDistance
this._centerLocation = options?.centerLocation ?? new UIEventSource<Loc>({
lat: 0, lon: 0, zoom: 0
})
this._centerLocation =
options?.centerLocation ??
new UIEventSource<Loc>({
lat: 0,
lon: 0,
zoom: 0,
})
this._snappedPointTags = options?.snappedPointTags
this._bounds = options?.bounds
this._minZoom = options?.minZoom
@ -152,11 +176,11 @@ export default class LocationInput
return undefined
}
// We reproject the location onto every 'snap-to-feature' and select the closest
let min = undefined
let matchedWay: Feature<LineString | Polygon> & {properties : {id : WayId}} = undefined
let matchedWay: Feature<LineString | Polygon> & { properties: { id: WayId } } =
undefined
for (const feature of self._snapTo.data ?? []) {
try {
const nearestPointOnLine = GeoOperations.nearestPoint(feature, [
@ -191,18 +215,16 @@ export default class LocationInput
return {
type: "Feature",
properties: options?.snappedPointTags ?? min.properties,
geometry: {type: "Point", coordinates: [loc.lon, loc.lat]},
geometry: { type: "Point", coordinates: [loc.lon, loc.lat] },
}
}
}
min.properties = options?.snappedPointTags ?? min.properties
if(matchedWay.properties.id.startsWith("relation/")){
if (matchedWay.properties.id.startsWith("relation/")) {
// We matched a relation instead of a way
console.log("Snapping onto a relation. The relation is", matchedWay)
}
self.snappedOnto.setData(<any> matchedWay)
self.snappedOnto.setData(<any>matchedWay)
return min
},
[this._snapTo]
@ -217,7 +239,10 @@ export default class LocationInput
}
})
}
this.mapBackground = options?.mapBackground ?? this._state?.backgroundLayer ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
this.mapBackground =
options?.mapBackground ??
this._state?.backgroundLayer ??
new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
this.SetClass("block h-full")
this.clickLocation = new UIEventSource<Loc>(undefined)
@ -249,7 +274,7 @@ export default class LocationInput
try {
const self = this
const hasMoved = new UIEventSource(false)
const startLocation = {...this._centerLocation.data}
const startLocation = { ...this._centerLocation.data }
this._centerLocation.addCallbackD((newLocation) => {
const f = 100000
console.log(newLocation.lon, startLocation.lon)
@ -279,7 +304,7 @@ export default class LocationInput
if (loc === undefined) {
return []
}
return [{feature: loc}]
return [{ feature: loc }]
})
console.log("Constructing the match layer", matchPoint)
@ -335,9 +360,9 @@ export default class LocationInput
}
}
TakeScreenshot(format: "image"): Promise<string>;
TakeScreenshot(format: "blob"): Promise<Blob>;
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>;
TakeScreenshot(format: "image"): Promise<string>
TakeScreenshot(format: "blob"): Promise<Blob>
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob>
TakeScreenshot(format: "image" | "blob"): Promise<string | Blob> {
return this.map.TakeScreenshot(format)
}

View file

@ -18,7 +18,7 @@ import Title from "../Base/Title"
import { GlobalFilter } from "../../Logic/State/MapState"
import { VariableUiElement } from "../Base/VariableUIElement"
import { Tag } from "../../Logic/Tags/Tag"
import {WayId} from "../../Models/OsmFeature";
import { WayId } from "../../Models/OsmFeature"
export default class ConfirmLocationOfPoint extends Combine {
constructor(
@ -76,7 +76,7 @@ export default class ConfirmLocationOfPoint extends Combine {
snappedPointTags: tags,
maxSnapDistance: preset.preciseInput.maxSnapDistance,
bounds: mapBounds,
state: <any> state
state: <any>state,
})
preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
preciseInput

View file

@ -22,7 +22,7 @@ import Title from "../Base/Title"
import { SubstitutedTranslation } from "../SubstitutedTranslation"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import TagRenderingQuestion from "./TagRenderingQuestion"
import {OsmId} from "../../Models/OsmFeature";
import { OsmId } from "../../Models/OsmFeature"
export default class DeleteWizard extends Toggle {
/**

View file

@ -248,29 +248,27 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
)
editElements.push(
Toggle.If(state.featureSwitchIsDebugging,
() => {
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
{ render: "{all_tags()}" },
""
)
const config_download: TagRenderingConfig = new TagRenderingConfig(
{ render: "{export_as_geojson()}" },
""
)
const config_id: TagRenderingConfig = new TagRenderingConfig(
{ render: "{open_in_iD()}" },
""
)
return new Combine([
new TagRenderingAnswer(tags, config_all_tags, state),
new TagRenderingAnswer(tags, config_download, state),
new TagRenderingAnswer(tags, config_id, state),
"This is layer " + layerConfig.id,
])
}
Toggle.If(state.featureSwitchIsDebugging, () => {
const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
{ render: "{all_tags()}" },
""
)
const config_download: TagRenderingConfig = new TagRenderingConfig(
{ render: "{export_as_geojson()}" },
""
)
const config_id: TagRenderingConfig = new TagRenderingConfig(
{ render: "{open_in_iD()}" },
""
)
return new Combine([
new TagRenderingAnswer(tags, config_all_tags, state),
new TagRenderingAnswer(tags, config_download, state),
new TagRenderingAnswer(tags, config_id, state),
"This is layer " + layerConfig.id,
])
})
)
return new Combine(editElements).SetClass("flex flex-col")

View file

@ -145,7 +145,7 @@ export default class MoveWizard extends Toggle {
minZoom: reason.minZoom,
centerLocation: loc,
mapBackground: new UIEventSource<BaseLayer>(preferredBackground), // We detach the layer
state: <any> state
state: <any>state,
})
if (reason.lockBounds) {

View file

@ -51,18 +51,18 @@ export default class TagApplyButton implements AutoAction {
* // Should handle escaped ";"
* TagApplyButton.parseTagSpec("key=value;key0=value0\\;value1") // => [["key","value"],["key0","value0;value1"]]
*/
private static parseTagSpec(spec: string): [string, string][]{
const tgsSpec : [string, string][] = []
private static parseTagSpec(spec: string): [string, string][] {
const tgsSpec: [string, string][] = []
while(spec.length > 0){
while (spec.length > 0) {
const [part] = spec.match(/((\\;)|[^;])*/)
spec = spec.substring(part.length + 1) // +1 to remove the pending ';' as well
const kv = part.split("=").map((s) => s.trim().replace("\\;",";"))
const kv = part.split("=").map((s) => s.trim().replace("\\;", ";"))
if (kv.length == 2) {
tgsSpec.push(<[string, string]> kv)
}else if (kv.length < 2) {
tgsSpec.push(<[string, string]>kv)
} else if (kv.length < 2) {
throw "Invalid key spec: no '=' found in " + spec
}else{
} else {
throw "Invalid key spec: multiple '=' found in " + spec
}
}
@ -83,7 +83,7 @@ export default class TagApplyButton implements AutoAction {
spec = tagSource.data[spec.replace("$", "")]
}
const tgsSpec = TagApplyButton.parseTagSpec(spec)
const tgsSpec = TagApplyButton.parseTagSpec(spec)
return tagSource.map((tags) => {
const newTags: Tag[] = []

View file

@ -304,7 +304,7 @@ export default class TagRenderingQuestion extends Combine {
const patchedMapping = <Mapping>{
...mapping,
iconClass: mapping.iconClass ?? `small-height`,
icon: mapping.icon ?? (addIcons ? "./assets/svg/none.svg" : undefined)
icon: mapping.icon ?? (addIcons ? "./assets/svg/none.svg" : undefined),
}
const fancy = TagRenderingQuestion.GenerateMappingContent(
patchedMapping,

View file

@ -10,12 +10,15 @@ import * as clusterstyle from "../../assets/layers/cluster_style/cluster_style.j
export default class ShowTileInfo {
public static readonly styling = new LayerConfig(clusterstyle, "ShowTileInfo", true)
constructor(options: {
source: FeatureSource & Tiled
leafletMap: UIEventSource<any>
layer?: LayerConfig
doShowLayer?: UIEventSource<boolean>
}, state) {
constructor(
options: {
source: FeatureSource & Tiled
leafletMap: UIEventSource<any>
layer?: LayerConfig
doShowLayer?: UIEventSource<boolean>
},
state
) {
const source = options.source
const metaFeature: Store<{ feature; freshness: Date }[]> = source.features.map(
(features) => {
@ -55,7 +58,7 @@ export default class ShowTileInfo {
features: new StaticFeatureSource(metaFeature),
leafletMap: options.leafletMap,
doShowLayer: options.doShowLayer,
state
state,
})
}
}

View file

@ -41,7 +41,10 @@ export default class Translations {
* translation.textFor("nl") // => "Nederlands"
*
*/
static T(t: string | undefined | null | Translation | TypedTranslation<object>, context = undefined): TypedTranslation<object> {
static T(
t: string | undefined | null | Translation | TypedTranslation<object>,
context = undefined
): TypedTranslation<object> {
if (t === undefined || t === null) {
return undefined
}