forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
aa50d33b81
53 changed files with 1094 additions and 411 deletions
|
@ -187,7 +187,7 @@ export default class LayerConfig {
|
||||||
|
|
||||||
const keys = Array.from(SharedTagRenderings.SharedTagRendering.keys())
|
const keys = Array.from(SharedTagRenderings.SharedTagRendering.keys())
|
||||||
|
|
||||||
throw `Predefined tagRendering ${renderingJson} not found in ${context}.\n Try one of ${(keys.join(", "))}`;
|
throw `Predefined tagRendering ${renderingJson} not found in ${context}.\n Try one of ${(keys.join(", "))}\n If you intent to output this text literally, use {\"render\": <your text>} instead"}`;
|
||||||
}
|
}
|
||||||
return new TagRenderingConfig(renderingJson, self.source.osmTags, `${context}.tagrendering[${i}]`);
|
return new TagRenderingConfig(renderingJson, self.source.osmTags, `${context}.tagrendering[${i}]`);
|
||||||
});
|
});
|
||||||
|
@ -343,7 +343,7 @@ export default class LayerConfig {
|
||||||
|
|
||||||
const iconW = num(iconSize[0]);
|
const iconW = num(iconSize[0]);
|
||||||
let iconH = num(iconSize[1]);
|
let iconH = num(iconSize[1]);
|
||||||
const mode = iconSize[2] ?? "center"
|
const mode = iconSize[2]?.trim()?.toLowerCase() ?? "center"
|
||||||
|
|
||||||
let anchorW = iconW / 2;
|
let anchorW = iconW / 2;
|
||||||
let anchorH = iconH / 2;
|
let anchorH = iconH / 2;
|
||||||
|
@ -365,12 +365,6 @@ export default class LayerConfig {
|
||||||
const self = this;
|
const self = this;
|
||||||
const mappedHtml = tags.map(tgs => {
|
const mappedHtml = tags.map(tgs => {
|
||||||
function genHtmlFromString(sourcePart: string): BaseUIElement {
|
function genHtmlFromString(sourcePart: string): BaseUIElement {
|
||||||
if (sourcePart.indexOf("html:") == 0) {
|
|
||||||
// We use § as a replacement for ;
|
|
||||||
const html = sourcePart.substring("html:".length)
|
|
||||||
const inner = new FixedUiElement(SubstitutingTag.substituteString(html, tgs)).SetClass("block w-min text-center")
|
|
||||||
return new Combine([inner]).SetClass("flex flex-col items-center");
|
|
||||||
}
|
|
||||||
|
|
||||||
const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
|
const style = `width:100%;height:100%;transform: rotate( ${rotation} );display:block;position: absolute; top: 0; left: 0`;
|
||||||
let html: BaseUIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
|
let html: BaseUIElement = new FixedUiElement(`<img src="${sourcePart}" style="${style}" />`);
|
||||||
|
@ -432,7 +426,7 @@ export default class LayerConfig {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const label = self.label?.GetRenderValue(tgs)?.Subs(tgs)
|
const label = self.label?.GetRenderValue(tgs)?.Subs(tgs)
|
||||||
?.SetClass("block w-min text-center")
|
?.SetClass("block text-center")
|
||||||
?.SetStyle("margin-top: " + (iconH + 2) + "px")
|
?.SetStyle("margin-top: " + (iconH + 2) + "px")
|
||||||
if (label !== undefined) {
|
if (label !== undefined) {
|
||||||
htmlParts.push(new Combine([label]).SetClass("flex flex-col items-center"))
|
htmlParts.push(new Combine([label]).SetClass("flex flex-col items-center"))
|
||||||
|
|
|
@ -424,13 +424,13 @@ def clean_input(contents):
|
||||||
yield row
|
yield row
|
||||||
|
|
||||||
|
|
||||||
def contributor_count(stats):
|
def contributor_count(stats, index=1, item = "contributor"):
|
||||||
seen_contributors = set()
|
seen_contributors = set()
|
||||||
for line in stats:
|
for line in stats:
|
||||||
contributor = line[1]
|
contributor = line[index]
|
||||||
if(contributor in seen_contributors):
|
if(contributor in seen_contributors):
|
||||||
continue
|
continue
|
||||||
print("New contributor " + str(len(seen_contributors) + 1) + ": "+contributor)
|
print("New " + item + " " + str(len(seen_contributors) + 1) + ": "+contributor)
|
||||||
seen_contributors.add(contributor)
|
seen_contributors.add(contributor)
|
||||||
print(line)
|
print(line)
|
||||||
|
|
||||||
|
@ -440,10 +440,10 @@ def main():
|
||||||
stats = list(clean_input(csv.reader(csvfile, delimiter=',', quotechar='"')))
|
stats = list(clean_input(csv.reader(csvfile, delimiter=',', quotechar='"')))
|
||||||
print("Found " + str(len(stats)) + " changesets")
|
print("Found " + str(len(stats)) + " changesets")
|
||||||
|
|
||||||
contributor_count(stats)
|
contributor_count(stats, 3, "theme")
|
||||||
create_graphs(stats)
|
# create_graphs(stats)
|
||||||
create_per_theme_graphs(stats, 15)
|
# create_per_theme_graphs(stats, 15)
|
||||||
create_per_contributor_graphs(stats, 25)
|
# create_per_contributor_graphs(stats, 25)
|
||||||
print("All done!")
|
print("All done!")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -352,6 +352,11 @@ export class InitUiElements {
|
||||||
|
|
||||||
State.state.backgroundLayer = State.state.backgroundLayerId
|
State.state.backgroundLayer = State.state.backgroundLayerId
|
||||||
.map((selectedId: string) => {
|
.map((selectedId: string) => {
|
||||||
|
if(selectedId === undefined){
|
||||||
|
return AvailableBaseLayers.osmCarto
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const available = State.state.availableBackgroundLayers.data;
|
const available = State.state.availableBackgroundLayers.data;
|
||||||
for (const layer of available) {
|
for (const layer of available) {
|
||||||
if (layer.id === selectedId) {
|
if (layer.id === selectedId) {
|
||||||
|
|
|
@ -14,18 +14,19 @@ import {Utils} from "../../Utils";
|
||||||
export default class AvailableBaseLayers {
|
export default class AvailableBaseLayers {
|
||||||
|
|
||||||
|
|
||||||
public static osmCarto: BaseLayer =
|
public static osmCarto: BaseLayer =
|
||||||
{
|
{
|
||||||
id: "osm",
|
id: "osm",
|
||||||
name: "OpenStreetMap",
|
name: "OpenStreetMap",
|
||||||
layer: AvailableBaseLayers.CreateBackgroundLayer("osm", "OpenStreetMap",
|
layer: () => AvailableBaseLayers.CreateBackgroundLayer("osm", "OpenStreetMap",
|
||||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "OpenStreetMap", "https://openStreetMap.org/copyright",
|
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "OpenStreetMap", "https://openStreetMap.org/copyright",
|
||||||
19,
|
19,
|
||||||
false, false),
|
false, false),
|
||||||
feature: null,
|
feature: null,
|
||||||
max_zoom: 19,
|
max_zoom: 19,
|
||||||
min_zoom: 0
|
min_zoom: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex());
|
public static layerOverview = AvailableBaseLayers.LoadRasterIndex().concat(AvailableBaseLayers.LoadProviderIndex());
|
||||||
|
@ -123,7 +124,7 @@ export default class AvailableBaseLayers {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const leafletLayer = AvailableBaseLayers.CreateBackgroundLayer(
|
const leafletLayer: () => TileLayer = () => AvailableBaseLayers.CreateBackgroundLayer(
|
||||||
props.id,
|
props.id,
|
||||||
props.name,
|
props.name,
|
||||||
props.url,
|
props.url,
|
||||||
|
@ -150,10 +151,10 @@ export default class AvailableBaseLayers {
|
||||||
private static LoadProviderIndex(): BaseLayer[] {
|
private static LoadProviderIndex(): BaseLayer[] {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
X; // Import X to make sure the namespace is not optimized away
|
X; // Import X to make sure the namespace is not optimized away
|
||||||
function l(id: string, name: string) {
|
function l(id: string, name: string) : BaseLayer{
|
||||||
try {
|
try {
|
||||||
const layer: any = L.tileLayer.provider(id, undefined);
|
const layer: any = () => L.tileLayer.provider(id, undefined);
|
||||||
return {
|
const baseLayer : BaseLayer = {
|
||||||
feature: null,
|
feature: null,
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -161,6 +162,7 @@ export default class AvailableBaseLayers {
|
||||||
min_zoom: layer.minzoom,
|
min_zoom: layer.minzoom,
|
||||||
max_zoom: layer.maxzoom
|
max_zoom: layer.maxzoom
|
||||||
}
|
}
|
||||||
|
return baseLayer
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Could not find provided layer", name, e);
|
console.error("Could not find provided layer", name, e);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {ImagesInCategory, Wikidata, Wikimedia} from "../Web/Wikimedia";
|
import {ImagesInCategory, Wikidata, Wikimedia} from "../ImageProviders/Wikimedia";
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
9
Logic/ImageProviders/AllImageProviders.ts
Normal file
9
Logic/ImageProviders/AllImageProviders.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import {Mapillary} from "./Mapillary";
|
||||||
|
import {Wikimedia} from "./Wikimedia";
|
||||||
|
import {Imgur} from "./Imgur";
|
||||||
|
|
||||||
|
export default class AllImageProviders{
|
||||||
|
|
||||||
|
public static ImageAttributionSource = [Imgur.singleton, Mapillary.singleton, Wikimedia.singleton]
|
||||||
|
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import BaseUIElement from "../../UI/BaseUIElement";
|
||||||
|
|
||||||
export default abstract class ImageAttributionSource {
|
export default abstract class ImageAttributionSource {
|
||||||
|
|
||||||
|
|
||||||
private _cache = new Map<string, UIEventSource<LicenseInfo>>()
|
private _cache = new Map<string, UIEventSource<LicenseInfo>>()
|
||||||
|
|
||||||
GetAttributionFor(url: string): UIEventSource<LicenseInfo> {
|
GetAttributionFor(url: string): UIEventSource<LicenseInfo> {
|
||||||
|
@ -22,6 +21,7 @@ export default abstract class ImageAttributionSource {
|
||||||
|
|
||||||
public abstract SourceIcon(backlinkSource?: string) : BaseUIElement;
|
public abstract SourceIcon(backlinkSource?: string) : BaseUIElement;
|
||||||
protected abstract DownloadAttribution(url: string): UIEventSource<LicenseInfo>;
|
protected abstract DownloadAttribution(url: string): UIEventSource<LicenseInfo>;
|
||||||
|
/*Converts a value to a URL. Can return null if not applicable*/
|
||||||
public PrepareUrl(value: string): string{
|
public PrepareUrl(value: string): string{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import BaseUIElement from "../../UI/BaseUIElement";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import {UIEventSource} from "../UIEventSource";
|
import {UIEventSource} from "../UIEventSource";
|
||||||
import Link from "../../UI/Base/Link";
|
import Link from "../../UI/Base/Link";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module provides endpoints for wikipedia/wikimedia and others
|
* This module provides endpoints for wikipedia/wikimedia and others
|
||||||
|
@ -138,21 +139,28 @@ export class Wikimedia extends ImageAttributionSource {
|
||||||
"api.php?action=query&prop=imageinfo&iiprop=extmetadata&" +
|
"api.php?action=query&prop=imageinfo&iiprop=extmetadata&" +
|
||||||
"titles=" + filename +
|
"titles=" + filename +
|
||||||
"&format=json&origin=*";
|
"&format=json&origin=*";
|
||||||
console.log("Getting attribution at ", url)
|
Utils.downloadJson(url).then(
|
||||||
$.getJSON(url, function (data) {
|
data =>{
|
||||||
const licenseInfo = new LicenseInfo();
|
const licenseInfo = new LicenseInfo();
|
||||||
const license = data.query.pages[-1].imageinfo[0].extmetadata;
|
const license = (data.query.pages[-1].imageinfo ?? [])[0]?.extmetadata;
|
||||||
|
if(license === undefined){
|
||||||
|
console.error("This file has no usable metedata or license attached... Please fix the license info file yourself!")
|
||||||
|
source.setData(null)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
licenseInfo.artist = license.Artist?.value;
|
licenseInfo.artist = license.Artist?.value;
|
||||||
licenseInfo.license = license.License?.value;
|
licenseInfo.license = license.License?.value;
|
||||||
licenseInfo.copyrighted = license.Copyrighted?.value;
|
licenseInfo.copyrighted = license.Copyrighted?.value;
|
||||||
licenseInfo.attributionRequired = license.AttributionRequired?.value;
|
licenseInfo.attributionRequired = license.AttributionRequired?.value;
|
||||||
licenseInfo.usageTerms = license.UsageTerms?.value;
|
licenseInfo.usageTerms = license.UsageTerms?.value;
|
||||||
licenseInfo.licenseShortName = license.LicenseShortName?.value;
|
licenseInfo.licenseShortName = license.LicenseShortName?.value;
|
||||||
licenseInfo.credit = license.Credit?.value;
|
licenseInfo.credit = license.Credit?.value;
|
||||||
licenseInfo.description = license.ImageDescription?.value;
|
licenseInfo.description = license.ImageDescription?.value;
|
||||||
source.setData(licenseInfo);
|
source.setData(licenseInfo);
|
||||||
});
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
|
|
||||||
}
|
}
|
|
@ -97,7 +97,7 @@ export class OsmPreferences {
|
||||||
|
|
||||||
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
key = prefix + key;
|
key = prefix + key;
|
||||||
key = key.replace(/[:\\\/"' {}.%_]/g, '')
|
key = key.replace(/[:\\\/"' {}.%]/g, '')
|
||||||
if (key.length >= 255) {
|
if (key.length >= 255) {
|
||||||
throw "Preferences: key length to big";
|
throw "Preferences: key length to big";
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {TileLayer} from "leaflet";
|
||||||
export default interface BaseLayer {
|
export default interface BaseLayer {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
layer: TileLayer,
|
layer: () => TileLayer,
|
||||||
max_zoom: number,
|
max_zoom: number,
|
||||||
min_zoom: number;
|
min_zoom: number;
|
||||||
feature: any,
|
feature: any,
|
||||||
|
|
19
Svg.ts
19
Svg.ts
|
@ -104,11 +104,26 @@ export default class Svg {
|
||||||
public static direction_svg() { return new Img(Svg.direction, true);}
|
public static direction_svg() { return new Img(Svg.direction, true);}
|
||||||
public static direction_ui() { return new FixedUiElement(Svg.direction_img);}
|
public static direction_ui() { return new FixedUiElement(Svg.direction_img);}
|
||||||
|
|
||||||
public static direction_gradient = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" id=\"svg8\" version=\"1.1\" viewBox=\"0 0 100 100\" height=\"100\" width=\"100\"> <metadata id=\"metadata8\"> <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> </cc:Work> </rdf:RDF> </metadata> <defs id=\"defs6\"> <linearGradient id=\"linearGradient820\"> <stop style=\"stop-color:#000000;stop-opacity:1;\" offset=\"0\" id=\"innercolor\" /> <stop style=\"stop-color:#000000;stop-opacity:0\" offset=\"1\" id=\"outercolor\" /> </linearGradient> <radialGradient xlink:href=\"#linearGradient820\" id=\"radialGradient828\" cx=\"49.787739\" cy=\"53.828533\" fx=\"49.787739\" fy=\"53.828533\" r=\"28.883806\" gradientTransform=\"matrix(1.5439431,-0.01852438,0.02075364,1.7297431,-27.986574,-42.187244)\" gradientUnits=\"userSpaceOnUse\" /> </defs> <path id=\"path821\" d=\"M 50,50 21.042889,9.3993342 C 36.191421,-2.001434 60.726552,-3.6768009 78.8105,9.1490935 Z\" style=\"fill:url(#radialGradient828);fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" /> </svg> "
|
public static direction_gradient = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"860.50732pt\" height=\"860.50732pt\" viewBox=\"0 0 860.50732 860.50732\" preserveAspectRatio=\"xMidYMid meet\" id=\"svg14\" sodipodi:docname=\"direction_gradient.svg\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\"> <defs id=\"defs18\"> <linearGradient inkscape:collect=\"always\" id=\"linearGradient832\"> <stop style=\"stop-color:#000000;stop-opacity:1;\" offset=\"0\" id=\"stop828\" /> <stop style=\"stop-color:#000000;stop-opacity:0;\" offset=\"1\" id=\"stop830\" /> </linearGradient> <radialGradient inkscape:collect=\"always\" xlink:href=\"#linearGradient832\" id=\"radialGradient838\" cx=\"430.25363\" cy=\"519.61188\" fx=\"430.25363\" fy=\"519.61188\" r=\"305.54589\" gradientTransform=\"matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)\" gradientUnits=\"userSpaceOnUse\" /> </defs> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" id=\"namedview16\" showgrid=\"false\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:zoom=\"0.70710678\" inkscape:cx=\"279.00239\" inkscape:cy=\"856.75313\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg14\"> <sodipodi:guide position=\"430.25363,862.49682\" orientation=\"1,0\" id=\"guide832\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"-42.427977,430.25368\" orientation=\"0,1\" id=\"guide834\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"398.27788,720.18823\" orientation=\"0,1\" id=\"guide840\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <path style=\"fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" d=\"M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z\" id=\"path836\" inkscape:connector-curvature=\"0\" sodipodi:nodetypes=\"ccccc\" /> </svg> "
|
||||||
public static direction_gradient_img = Img.AsImageElement(Svg.direction_gradient)
|
public static direction_gradient_img = Img.AsImageElement(Svg.direction_gradient)
|
||||||
public static direction_gradient_svg() { return new Img(Svg.direction_gradient, true);}
|
public static direction_gradient_svg() { return new Img(Svg.direction_gradient, true);}
|
||||||
public static direction_gradient_ui() { return new FixedUiElement(Svg.direction_gradient_img);}
|
public static direction_gradient_ui() { return new FixedUiElement(Svg.direction_gradient_img);}
|
||||||
|
|
||||||
|
public static direction_masked = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"860.50732pt\" height=\"860.50732pt\" viewBox=\"0 0 860.50732 860.50732\" preserveAspectRatio=\"xMidYMid meet\" id=\"svg14\" sodipodi:docname=\"direction_masked.svg\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\"> <defs id=\"defs18\" /> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" id=\"namedview16\" showgrid=\"false\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:zoom=\"0.70710678\" inkscape:cx=\"418.71218\" inkscape:cy=\"725.43016\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg14\"> <sodipodi:guide position=\"430.25363,862.49682\" orientation=\"1,0\" id=\"guide832\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"-42.427977,430.25368\" orientation=\"0,1\" id=\"guide834\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> </cc:Work> </rdf:RDF> </metadata> <path style=\"fill:#000000;fill-opacity:0.39163497;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" d=\"M 124.70776,124.7084 A 432.375,432.375 0 0 0 -2.121338,430.25429 432.375,432.375 0 0 0 430.25366,862.62929 432.375,432.375 0 0 0 862.62866,430.25429 432.375,432.375 0 0 0 735.79811,124.70986 L 430.25366,430.25429 Z\" id=\"path836\" inkscape:connector-curvature=\"0\" /> </svg> "
|
||||||
|
public static direction_masked_img = Img.AsImageElement(Svg.direction_masked)
|
||||||
|
public static direction_masked_svg() { return new Img(Svg.direction_masked, true);}
|
||||||
|
public static direction_masked_ui() { return new FixedUiElement(Svg.direction_masked_img);}
|
||||||
|
|
||||||
|
public static direction_outline = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"860.50732pt\" height=\"860.50732pt\" viewBox=\"0 0 860.50732 860.50732\" preserveAspectRatio=\"xMidYMid meet\" id=\"svg14\" sodipodi:docname=\"direction_outline.svg\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\"> <defs id=\"defs18\" /> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" id=\"namedview16\" showgrid=\"false\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:zoom=\"0.35355339\" inkscape:cx=\"72.756921\" inkscape:cy=\"792.67347\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg14\"> <sodipodi:guide position=\"430.25363,862.49682\" orientation=\"1,0\" id=\"guide832\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"-42.427977,430.25368\" orientation=\"0,1\" id=\"guide834\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <path style=\"fill:#000000;fill-opacity:0.39163501;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" d=\"M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z\" id=\"path836\" inkscape:connector-curvature=\"0\" sodipodi:nodetypes=\"ccccc\" /> </svg> "
|
||||||
|
public static direction_outline_img = Img.AsImageElement(Svg.direction_outline)
|
||||||
|
public static direction_outline_svg() { return new Img(Svg.direction_outline, true);}
|
||||||
|
public static direction_outline_ui() { return new FixedUiElement(Svg.direction_outline_img);}
|
||||||
|
|
||||||
|
public static direction_stroke = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"860.50732pt\" height=\"860.50732pt\" viewBox=\"0 0 860.50732 860.50732\" preserveAspectRatio=\"xMidYMid meet\" id=\"svg14\" sodipodi:docname=\"direction_stroke.svg\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\"> <defs id=\"defs18\" /> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" id=\"namedview16\" showgrid=\"false\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:zoom=\"1\" inkscape:cx=\"482.69344\" inkscape:cy=\"931.96415\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg14\"> <sodipodi:guide position=\"430.25363,862.49682\" orientation=\"1,0\" id=\"guide832\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"-42.427977,430.25368\" orientation=\"0,1\" id=\"guide834\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata2\"> Created by potrace 1.15, written by Peter Selinger 2001-2017 <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> <dc:title /> </cc:Work> </rdf:RDF> </metadata> <path style=\"fill: none !important;fill-opacity:0.39163501;stroke:#000000;stroke-width:3.74999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1\" d=\"M 735.79953,129.20772 C 654.7909,48.098612 544.88816,2.4790648 430.25363,2.3786263 315.61911,2.4790701 205.71637,48.098617 124.70775,129.20772 l 305.54588,305.54589 z\" id=\"path836\" inkscape:connector-curvature=\"0\" sodipodi:nodetypes=\"ccccc\" /> </svg> "
|
||||||
|
public static direction_stroke_img = Img.AsImageElement(Svg.direction_stroke)
|
||||||
|
public static direction_stroke_svg() { return new Img(Svg.direction_stroke, true);}
|
||||||
|
public static direction_stroke_ui() { return new FixedUiElement(Svg.direction_stroke_img);}
|
||||||
|
|
||||||
public static down = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"700\" height=\"700\" id=\"svg6\" sodipodi:docname=\"down.svg\" inkscape:version=\"0.92.4 (5da689c313, 2019-01-14)\"> <metadata id=\"metadata12\"> <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> </cc:Work> </rdf:RDF> </metadata> <defs id=\"defs10\" /> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"1001\" id=\"namedview8\" showgrid=\"false\" inkscape:zoom=\"0.33714286\" inkscape:cx=\"477.91309\" inkscape:cy=\"350\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg6\" /> <g transform=\"rotate(-180,342.1439,335.17672)\" id=\"g4\"> <path d=\"m -20,670.71582 c 0,-1.85843 349.99229,-699.98853 350.57213,-699.28671 1.94549,2.35478 350.06752,699.46087 349.427,699.71927 -0.41837,0.16878 -79.29725,-33.69092 -175.2864,-75.24377 l -174.52574,-75.55065 -174.2421,75.53732 c -95.83317,41.54551 -174.625237,75.5373 -175.093498,75.5373 -0.46826,0 -0.851382,-0.32075 -0.851382,-0.71276 z\" style=\"fill:#00ff00;stroke:none\" id=\"path2\" inkscape:connector-curvature=\"0\" /> </g> </svg> "
|
public static down = " <svg xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" version=\"1.0\" width=\"700\" height=\"700\" id=\"svg6\" sodipodi:docname=\"down.svg\" inkscape:version=\"0.92.4 (5da689c313, 2019-01-14)\"> <metadata id=\"metadata12\"> <rdf:RDF> <cc:Work rdf:about=\"\"> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" /> </cc:Work> </rdf:RDF> </metadata> <defs id=\"defs10\" /> <sodipodi:namedview pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1\" objecttolerance=\"10\" gridtolerance=\"10\" guidetolerance=\"10\" inkscape:pageopacity=\"0\" inkscape:pageshadow=\"2\" inkscape:window-width=\"1920\" inkscape:window-height=\"1001\" id=\"namedview8\" showgrid=\"false\" inkscape:zoom=\"0.33714286\" inkscape:cx=\"477.91309\" inkscape:cy=\"350\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:current-layer=\"svg6\" /> <g transform=\"rotate(-180,342.1439,335.17672)\" id=\"g4\"> <path d=\"m -20,670.71582 c 0,-1.85843 349.99229,-699.98853 350.57213,-699.28671 1.94549,2.35478 350.06752,699.46087 349.427,699.71927 -0.41837,0.16878 -79.29725,-33.69092 -175.2864,-75.24377 l -174.52574,-75.55065 -174.2421,75.53732 c -95.83317,41.54551 -174.625237,75.5373 -175.093498,75.5373 -0.46826,0 -0.851382,-0.32075 -0.851382,-0.71276 z\" style=\"fill:#00ff00;stroke:none\" id=\"path2\" inkscape:connector-curvature=\"0\" /> </g> </svg> "
|
||||||
public static down_img = Img.AsImageElement(Svg.down)
|
public static down_img = Img.AsImageElement(Svg.down)
|
||||||
public static down_svg() { return new Img(Svg.down, true);}
|
public static down_svg() { return new Img(Svg.down, true);}
|
||||||
|
@ -319,4 +334,4 @@ export default class Svg {
|
||||||
public static wikipedia_svg() { return new Img(Svg.wikipedia, true);}
|
public static wikipedia_svg() { return new Img(Svg.wikipedia, true);}
|
||||||
public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);}
|
public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);}
|
||||||
|
|
||||||
public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
|
public static All = {"SocialImageForeground.svg": Svg.SocialImageForeground,"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"back.svg": Svg.back,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"circle.svg": Svg.circle,"clock.svg": Svg.clock,"close.svg": Svg.close,"compass.svg": Svg.compass,"cross_bottom_right.svg": Svg.cross_bottom_right,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"direction_masked.svg": Svg.direction_masked,"direction_outline.svg": Svg.direction_outline,"direction_stroke.svg": Svg.direction_stroke,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapcomplete_logo.svg": Svg.mapcomplete_logo,"mapillary.svg": Svg.mapillary,"mapillary_black.svg": Svg.mapillary_black,"min.svg": Svg.min,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-copyright.svg": Svg.osm_copyright,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"phone.svg": Svg.phone,"pin.svg": Svg.pin,"plus.svg": Svg.plus,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"ring.svg": Svg.ring,"search.svg": Svg.search,"send_email.svg": Svg.send_email,"share.svg": Svg.share,"square.svg": Svg.square,"star.svg": Svg.star,"star_half.svg": Svg.star_half,"star_outline.svg": Svg.star_outline,"star_outline_half.svg": Svg.star_outline_half,"statistics.svg": Svg.statistics,"translate.svg": Svg.translate,"up.svg": Svg.up,"wikidata.svg": Svg.wikidata,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
|
||||||
|
|
144
UI/Base/Minimap.ts
Normal file
144
UI/Base/Minimap.ts
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import * as L from "leaflet";
|
||||||
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
|
import Loc from "../../Models/Loc";
|
||||||
|
import BaseLayer from "../../Models/BaseLayer";
|
||||||
|
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||||
|
import {Map} from "leaflet";
|
||||||
|
|
||||||
|
export default class Minimap extends BaseUIElement {
|
||||||
|
|
||||||
|
private static _nextId = 0;
|
||||||
|
public readonly leafletMap: UIEventSource<Map> = new UIEventSource<Map>(undefined)
|
||||||
|
private readonly _id: string;
|
||||||
|
private readonly _background: UIEventSource<BaseLayer>;
|
||||||
|
private readonly _location: UIEventSource<Loc>;
|
||||||
|
private _isInited = false;
|
||||||
|
private _allowMoving: boolean;
|
||||||
|
|
||||||
|
constructor(options?: {
|
||||||
|
background?: UIEventSource<BaseLayer>,
|
||||||
|
location?: UIEventSource<Loc>,
|
||||||
|
allowMoving?: boolean
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
options = options ?? {}
|
||||||
|
this._background = options?.background ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto)
|
||||||
|
this._location = options?.location ?? new UIEventSource<Loc>(undefined)
|
||||||
|
this._id = "minimap" + Minimap._nextId;
|
||||||
|
this._allowMoving = options.allowMoving ?? true;
|
||||||
|
Minimap._nextId++
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected InnerConstructElement(): HTMLElement {
|
||||||
|
const div = document.createElement("div")
|
||||||
|
div.id = this._id;
|
||||||
|
div.style.height = "100%"
|
||||||
|
div.style.width = "100%"
|
||||||
|
div.style.minWidth = "40px"
|
||||||
|
div.style.minHeight = "40px"
|
||||||
|
const wrapper = document.createElement("div")
|
||||||
|
wrapper.appendChild(div)
|
||||||
|
const self = this;
|
||||||
|
// @ts-ignore
|
||||||
|
const resizeObserver = new ResizeObserver(_ => {
|
||||||
|
console.log("Change in size detected!")
|
||||||
|
self.InitMap();
|
||||||
|
self.leafletMap?.data?.invalidateSize()
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(div);
|
||||||
|
return wrapper;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private InitMap() {
|
||||||
|
if (this._constructedHtmlElement === undefined) {
|
||||||
|
// This element isn't initialized yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById(this._id) === null) {
|
||||||
|
// not yet attached, we probably got some other event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isInited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._isInited = true;
|
||||||
|
const location = this._location;
|
||||||
|
|
||||||
|
let currentLayer = this._background.data.layer()
|
||||||
|
const map = L.map(this._id, {
|
||||||
|
center: [location.data?.lat ?? 0, location.data?.lon ?? 0],
|
||||||
|
zoom: location.data?.zoom ?? 2,
|
||||||
|
layers: [currentLayer],
|
||||||
|
zoomControl: false,
|
||||||
|
attributionControl: false,
|
||||||
|
dragging: this._allowMoving,
|
||||||
|
scrollWheelZoom: this._allowMoving,
|
||||||
|
doubleClickZoom: this._allowMoving,
|
||||||
|
keyboard: this._allowMoving,
|
||||||
|
touchZoom: this._allowMoving
|
||||||
|
});
|
||||||
|
|
||||||
|
map.setMaxBounds(
|
||||||
|
[[-100, -200], [100, 200]]
|
||||||
|
);
|
||||||
|
|
||||||
|
this._background.addCallbackAndRun(layer => {
|
||||||
|
const newLayer = layer.layer()
|
||||||
|
if (currentLayer !== undefined) {
|
||||||
|
map.removeLayer(currentLayer);
|
||||||
|
}
|
||||||
|
currentLayer = newLayer;
|
||||||
|
map.addLayer(newLayer);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
let isRecursing = false;
|
||||||
|
map.on("moveend", function () {
|
||||||
|
if (isRecursing) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (map.getZoom() === location.data.zoom &&
|
||||||
|
map.getCenter().lat === location.data.lat &&
|
||||||
|
map.getCenter().lng === location.data.lon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.trace(map.getZoom(), map.getCenter(), location.data)
|
||||||
|
|
||||||
|
location.data.zoom = map.getZoom();
|
||||||
|
location.data.lat = map.getCenter().lat;
|
||||||
|
location.data.lon = map.getCenter().lng;
|
||||||
|
isRecursing = true;
|
||||||
|
location.ping();
|
||||||
|
isRecursing = false; // This is ugly, I know
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
location.addCallback(loc => {
|
||||||
|
const mapLoc = map.getCenter()
|
||||||
|
const dlat = Math.abs(loc.lat - mapLoc[0])
|
||||||
|
const dlon = Math.abs(loc.lon - mapLoc[1])
|
||||||
|
|
||||||
|
if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map.setView([loc.lat, loc.lon], loc.zoom)
|
||||||
|
})
|
||||||
|
|
||||||
|
location.map(loc => loc.zoom)
|
||||||
|
.addCallback(zoom => {
|
||||||
|
if (Math.abs(map.getZoom() - zoom) > 0.1) {
|
||||||
|
map.setZoom(zoom, {});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
this.leafletMap.setData(map)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,25 +3,21 @@ import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
||||||
*
|
*
|
||||||
* Assumes a read-only configuration, so it has no 'ListenTo'
|
* Assumes a read-only configuration, so it has no 'ListenTo'
|
||||||
*/
|
*/
|
||||||
export default abstract class BaseUIElement {
|
export default abstract class BaseUIElement {
|
||||||
|
|
||||||
|
protected _constructedHtmlElement: HTMLElement;
|
||||||
private clss: Set<string> = new Set<string>();
|
private clss: Set<string> = new Set<string>();
|
||||||
private style: string;
|
private style: string;
|
||||||
private _onClick: () => void;
|
private _onClick: () => void;
|
||||||
private _onHover: UIEventSource<boolean>;
|
private _onHover: UIEventSource<boolean>;
|
||||||
|
|
||||||
protected _constructedHtmlElement: HTMLElement;
|
|
||||||
|
|
||||||
|
|
||||||
protected abstract InnerConstructElement(): HTMLElement;
|
|
||||||
|
|
||||||
public onClick(f: (() => void)) {
|
public onClick(f: (() => void)) {
|
||||||
this._onClick = f;
|
this._onClick = f;
|
||||||
this.SetClass("clickable")
|
this.SetClass("clickable")
|
||||||
if(this._constructedHtmlElement !== undefined){
|
if (this._constructedHtmlElement !== undefined) {
|
||||||
this._constructedHtmlElement.onclick = f;
|
this._constructedHtmlElement.onclick = f;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
@ -38,12 +34,13 @@ export default abstract class BaseUIElement {
|
||||||
element.removeChild(element.firstChild);
|
element.removeChild(element.firstChild);
|
||||||
}
|
}
|
||||||
const el = this.ConstructElement();
|
const el = this.ConstructElement();
|
||||||
if(el !== undefined){
|
if (el !== undefined) {
|
||||||
element.appendChild(el)
|
element.appendChild(el)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds all the relevant classes, space seperated
|
* Adds all the relevant classes, space seperated
|
||||||
*/
|
*/
|
||||||
|
@ -55,7 +52,7 @@ export default abstract class BaseUIElement {
|
||||||
if (this.clss.has(clss)) {
|
if (this.clss.has(clss)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(c === undefined || c === ""){
|
if (c === undefined || c === "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.clss.add(c);
|
this.clss.add(c);
|
||||||
|
@ -74,19 +71,19 @@ export default abstract class BaseUIElement {
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HasClass(clss: string): boolean{
|
public HasClass(clss: string): boolean {
|
||||||
return this.clss.has(clss)
|
return this.clss.has(clss)
|
||||||
}
|
}
|
||||||
|
|
||||||
public SetStyle(style: string): BaseUIElement {
|
public SetStyle(style: string): BaseUIElement {
|
||||||
this.style = style;
|
this.style = style;
|
||||||
if(this._constructedHtmlElement !== undefined){
|
if (this._constructedHtmlElement !== undefined) {
|
||||||
this._constructedHtmlElement.style.cssText = style;
|
this._constructedHtmlElement.style.cssText = style;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The same as 'Render', but creates a HTML element instead of the HTML representation
|
* The same as 'Render', but creates a HTML element instead of the HTML representation
|
||||||
*/
|
*/
|
||||||
|
@ -99,68 +96,71 @@ export default abstract class BaseUIElement {
|
||||||
return this._constructedHtmlElement
|
return this._constructedHtmlElement
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.InnerConstructElement === undefined){
|
if (this.InnerConstructElement === undefined) {
|
||||||
throw "ERROR! This is not a correct baseUIElement: "+this.constructor.name
|
throw "ERROR! This is not a correct baseUIElement: " + this.constructor.name
|
||||||
}
|
}
|
||||||
try{
|
try {
|
||||||
|
|
||||||
|
|
||||||
const el = this.InnerConstructElement();
|
|
||||||
|
|
||||||
if(el === undefined){
|
const el = this.InnerConstructElement();
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._constructedHtmlElement = el;
|
if (el === undefined) {
|
||||||
const style = this.style
|
return undefined;
|
||||||
if (style !== undefined && style !== "") {
|
|
||||||
el.style.cssText = style
|
|
||||||
}
|
|
||||||
if (this.clss.size > 0) {
|
|
||||||
try{
|
|
||||||
el.classList.add(...Array.from(this.clss))
|
|
||||||
}catch(e){
|
|
||||||
console.error("Invalid class name detected in:", Array.from(this.clss).join(" "),"\nErr msg is ",e)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (this._onClick !== undefined) {
|
this._constructedHtmlElement = el;
|
||||||
const self = this;
|
const style = this.style
|
||||||
el.onclick = (e) => {
|
if (style !== undefined && style !== "") {
|
||||||
// @ts-ignore
|
el.style.cssText = style
|
||||||
if (e.consumed) {
|
}
|
||||||
return;
|
if (this.clss.size > 0) {
|
||||||
|
try {
|
||||||
|
el.classList.add(...Array.from(this.clss))
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Invalid class name detected in:", Array.from(this.clss).join(" "), "\nErr msg is ", e)
|
||||||
}
|
}
|
||||||
self._onClick();
|
|
||||||
// @ts-ignore
|
|
||||||
e.consumed = true;
|
|
||||||
}
|
}
|
||||||
el.style.pointerEvents = "all";
|
|
||||||
el.style.cursor = "pointer";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._onHover !== undefined) {
|
if (this._onClick !== undefined) {
|
||||||
const self = this;
|
const self = this;
|
||||||
el.addEventListener('mouseover', () => self._onHover.setData(true));
|
el.onclick = (e) => {
|
||||||
el.addEventListener('mouseout', () => self._onHover.setData(false));
|
// @ts-ignore
|
||||||
}
|
if (e.consumed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self._onClick();
|
||||||
|
// @ts-ignore
|
||||||
|
e.consumed = true;
|
||||||
|
}
|
||||||
|
el.style.pointerEvents = "all";
|
||||||
|
el.style.cursor = "pointer";
|
||||||
|
}
|
||||||
|
|
||||||
if (this._onHover !== undefined) {
|
if (this._onHover !== undefined) {
|
||||||
const self = this;
|
const self = this;
|
||||||
el.addEventListener('mouseover', () => self._onHover.setData(true));
|
el.addEventListener('mouseover', () => self._onHover.setData(true));
|
||||||
el.addEventListener('mouseout', () => self._onHover.setData(false));
|
el.addEventListener('mouseout', () => self._onHover.setData(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
return el}catch(e){
|
if (this._onHover !== undefined) {
|
||||||
|
const self = this;
|
||||||
|
el.addEventListener('mouseover', () => self._onHover.setData(true));
|
||||||
|
el.addEventListener('mouseout', () => self._onHover.setData(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return el
|
||||||
|
} catch (e) {
|
||||||
const domExc = e as DOMException;
|
const domExc = e as DOMException;
|
||||||
if(domExc){
|
if (domExc) {
|
||||||
console.log("An exception occured", domExc.code, domExc.message, domExc.name )
|
console.log("An exception occured", domExc.code, domExc.message, domExc.name)
|
||||||
}
|
}
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AsMarkdown(): string{
|
public AsMarkdown(): string {
|
||||||
throw "AsMarkdown is not implemented by "+this.constructor.name
|
throw "AsMarkdown is not implemented by " + this.constructor.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract InnerConstructElement(): HTMLElement;
|
||||||
}
|
}
|
|
@ -14,10 +14,14 @@ export class Basemap {
|
||||||
currentLayer: UIEventSource<BaseLayer>,
|
currentLayer: UIEventSource<BaseLayer>,
|
||||||
lastClickLocation?: UIEventSource<{ lat: number, lon: number }>,
|
lastClickLocation?: UIEventSource<{ lat: number, lon: number }>,
|
||||||
extraAttribution?: BaseUIElement) {
|
extraAttribution?: BaseUIElement) {
|
||||||
|
|
||||||
|
console.log("Currentlayer is" ,currentLayer, currentLayer.data, currentLayer.data?.id)
|
||||||
|
let previousLayer = currentLayer.data.layer();
|
||||||
|
|
||||||
this.map = L.map(leafletElementId, {
|
this.map = L.map(leafletElementId, {
|
||||||
center: [location.data.lat ?? 0, location.data.lon ?? 0],
|
center: [location.data.lat ?? 0, location.data.lon ?? 0],
|
||||||
zoom: location.data.zoom ?? 2,
|
zoom: location.data.zoom ?? 2,
|
||||||
layers: [currentLayer.data.layer],
|
layers: [previousLayer],
|
||||||
zoomControl: false,
|
zoomControl: false,
|
||||||
attributionControl: extraAttribution !== undefined
|
attributionControl: extraAttribution !== undefined
|
||||||
});
|
});
|
||||||
|
@ -42,16 +46,16 @@ export class Basemap {
|
||||||
extraAttribution.AttachTo('leaflet-attribution')
|
extraAttribution.AttachTo('leaflet-attribution')
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
let previousLayer = currentLayer.data;
|
|
||||||
currentLayer.addCallbackAndRun(layer => {
|
currentLayer.addCallbackAndRun(layer => {
|
||||||
if (layer === previousLayer) {
|
const newLayer = layer.layer()
|
||||||
|
if (newLayer === previousLayer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (previousLayer !== undefined) {
|
if (previousLayer !== undefined) {
|
||||||
self.map.removeLayer(previousLayer.layer);
|
self.map.removeLayer(previousLayer);
|
||||||
}
|
}
|
||||||
previousLayer = layer;
|
previousLayer = newLayer;
|
||||||
self.map.addLayer(layer.layer);
|
self.map.addLayer(newLayer);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ export default class MoreScreen extends Combine {
|
||||||
LanguagePicker.CreateLanguagePicker(Translations.t.index.title.SupportedLanguages())
|
LanguagePicker.CreateLanguagePicker(Translations.t.index.title.SupportedLanguages())
|
||||||
.SetClass("absolute top-2 right-3"),
|
.SetClass("absolute top-2 right-3"),
|
||||||
new IndexText()
|
new IndexText()
|
||||||
])
|
]);
|
||||||
|
|
||||||
themeButtonStyle = "h-32 min-h-32 max-h-32 overflow-ellipsis overflow-hidden"
|
themeButtonStyle = "h-32 min-h-32 max-h-32 overflow-ellipsis overflow-hidden"
|
||||||
themeListStyle = "md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-g4 gap-4"
|
themeListStyle = "md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-g4 gap-4"
|
||||||
}
|
}
|
||||||
|
@ -59,10 +60,23 @@ export default class MoreScreen extends Combine {
|
||||||
|
|
||||||
private static createOfficialThemesList(state: State, buttonClass: string): BaseUIElement {
|
private static createOfficialThemesList(state: State, buttonClass: string): BaseUIElement {
|
||||||
let officialThemes = AllKnownLayouts.layoutsList
|
let officialThemes = AllKnownLayouts.layoutsList
|
||||||
if (State.state.osmConnection.userDetails.data.csCount < Constants.userJourney.personalLayoutUnlock) {
|
|
||||||
officialThemes = officialThemes.filter(theme => theme.id !== personal.id)
|
let buttons = officialThemes.map((layout) => {
|
||||||
}
|
const button = MoreScreen.createLinkButton(layout)?.SetClass(buttonClass);
|
||||||
let buttons = officialThemes.map((layout) => MoreScreen.createLinkButton(layout)?.SetClass(buttonClass))
|
if(layout.id === personal.id){
|
||||||
|
return new VariableUiElement(
|
||||||
|
State.state.osmConnection.userDetails.map(userdetails => userdetails.csCount)
|
||||||
|
.map(csCount => {
|
||||||
|
if(csCount < Constants.userJourney.personalLayoutUnlock){
|
||||||
|
return undefined
|
||||||
|
}else{
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
})
|
||||||
|
|
||||||
let customGeneratorLink = MoreScreen.createCustomGeneratorButton(state)
|
let customGeneratorLink = MoreScreen.createCustomGeneratorButton(state)
|
||||||
buttons.splice(0, 0, customGeneratorLink);
|
buttons.splice(0, 0, customGeneratorLink);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Combine from "../Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import Attribution from "./Attribution";
|
import Attribution from "./Attribution";
|
||||||
import Img from "../Base/Img";
|
import Img from "../Base/Img";
|
||||||
import ImageAttributionSource from "../../Logic/Web/ImageAttributionSource";
|
import ImageAttributionSource from "../../Logic/ImageProviders/ImageAttributionSource";
|
||||||
|
|
||||||
|
|
||||||
export class AttributedImage extends Combine {
|
export class AttributedImage extends Combine {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Translations from "../i18n/Translations";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import {LicenseInfo} from "../../Logic/Web/Wikimedia";
|
import {LicenseInfo} from "../../Logic/ImageProviders/Wikimedia";
|
||||||
|
|
||||||
export default class Attribution extends VariableUiElement {
|
export default class Attribution extends VariableUiElement {
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,9 @@ import {AttributedImage} from "./AttributedImage";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import Img from "../Base/Img";
|
import Img from "../Base/Img";
|
||||||
import Toggle from "../Input/Toggle";
|
import Toggle from "../Input/Toggle";
|
||||||
import ImageAttributionSource from "../../Logic/Web/ImageAttributionSource";
|
import {Wikimedia} from "../../Logic/ImageProviders/Wikimedia";
|
||||||
import {Wikimedia} from "../../Logic/Web/Wikimedia";
|
import {Imgur} from "../../Logic/ImageProviders/Imgur";
|
||||||
import {Mapillary} from "../../Logic/Web/Mapillary";
|
import {Mapillary} from "../../Logic/ImageProviders/Mapillary";
|
||||||
import {Imgur} from "../../Logic/Web/Imgur";
|
|
||||||
|
|
||||||
export class ImageCarousel extends Toggle {
|
export class ImageCarousel extends Toggle {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import BaseUIElement from "../BaseUIElement";
|
||||||
import LicensePicker from "../BigComponents/LicensePicker";
|
import LicensePicker from "../BigComponents/LicensePicker";
|
||||||
import Toggle from "../Input/Toggle";
|
import Toggle from "../Input/Toggle";
|
||||||
import FileSelectorButton from "../Input/FileSelectorButton";
|
import FileSelectorButton from "../Input/FileSelectorButton";
|
||||||
import ImgurUploader from "../../Logic/Web/ImgurUploader";
|
import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader";
|
||||||
import UploadFlowStateUI from "../BigComponents/UploadFlowStateUI";
|
import UploadFlowStateUI from "../BigComponents/UploadFlowStateUI";
|
||||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
|
import LayerConfig from "../../Customizations/JSON/LayerConfig";
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,30 @@ import {InputElement} from "./InputElement";
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import Combine from "../Base/Combine";
|
import Combine from "../Base/Combine";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
import Loc from "../../Models/Loc";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects a direction in degrees
|
* Selects a direction in degrees
|
||||||
*/
|
*/
|
||||||
export default class DirectionInput extends InputElement<string> {
|
export default class DirectionInput extends InputElement<string> {
|
||||||
|
public static constructMinimap: ((any) => BaseUIElement);
|
||||||
|
private readonly _location: UIEventSource<Loc>;
|
||||||
|
|
||||||
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||||
private readonly value: UIEventSource<string>;
|
private readonly value: UIEventSource<string>;
|
||||||
|
private background;
|
||||||
|
|
||||||
constructor(value?: UIEventSource<string>) {
|
constructor(mapBackground: UIEventSource<any>,
|
||||||
|
location: UIEventSource<Loc>,
|
||||||
|
value?: UIEventSource<string>) {
|
||||||
super();
|
super();
|
||||||
|
this._location = location;
|
||||||
this.value = value ?? new UIEventSource<string>(undefined);
|
this.value = value ?? new UIEventSource<string>(undefined);
|
||||||
|
this.background = mapBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetValue(): UIEventSource<string> {
|
GetValue(): UIEventSource<string> {
|
||||||
|
@ -30,16 +39,23 @@ export default class DirectionInput extends InputElement<string> {
|
||||||
|
|
||||||
protected InnerConstructElement(): HTMLElement {
|
protected InnerConstructElement(): HTMLElement {
|
||||||
|
|
||||||
|
let map: BaseUIElement = new FixedUiElement("")
|
||||||
|
if (!Utils.runningFromConsole) {
|
||||||
|
map = DirectionInput.constructMinimap({
|
||||||
|
background: this.background,
|
||||||
|
allowMoving: false,
|
||||||
|
location: this._location
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const element = new Combine([
|
const element = new Combine([
|
||||||
new FixedUiElement("").SetClass("w-full h-full absolute top-0 left-O rounded-full"),
|
Svg.direction_stroke_svg().SetStyle(
|
||||||
Svg.direction_svg().SetStyle(
|
|
||||||
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`)
|
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`)
|
||||||
.SetClass("direction-svg"),
|
.SetClass("direction-svg relative")
|
||||||
Svg.compass_svg().SetStyle(
|
.SetStyle("z-index: 1000"),
|
||||||
"position: absolute;top: 0;left: 0;width: 100%;height: 100%;")
|
map.SetClass("w-full h-full absolute top-0 left-O rounded-full overflow-hidden"),
|
||||||
])
|
])
|
||||||
.SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
|
.SetStyle("position:relative;display:block;width: min(100%, 25em); height: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
|
||||||
.ConstructElement()
|
.ConstructElement()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {DropDown} from "./DropDown";
|
import {DropDown} from "./DropDown";
|
||||||
import * as EmailValidator from "email-validator";
|
import * as EmailValidator from "email-validator";
|
||||||
import {parsePhoneNumberFromString} from "libphonenumber-js";
|
import {parsePhoneNumberFromString} from "libphonenumber-js";
|
||||||
import InputElementMap from "./InputElementMap";
|
|
||||||
import {InputElement} from "./InputElement";
|
import {InputElement} from "./InputElement";
|
||||||
import {TextField} from "./TextField";
|
import {TextField} from "./TextField";
|
||||||
import {UIElement} from "../UIElement";
|
import {UIElement} from "../UIElement";
|
||||||
|
@ -12,6 +11,7 @@ import OpeningHoursInput from "../OpeningHours/OpeningHoursInput";
|
||||||
import DirectionInput from "./DirectionInput";
|
import DirectionInput from "./DirectionInput";
|
||||||
import ColorPicker from "./ColorPicker";
|
import ColorPicker from "./ColorPicker";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
|
import Loc from "../../Models/Loc";
|
||||||
|
|
||||||
interface TextFieldDef {
|
interface TextFieldDef {
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -19,7 +19,8 @@ interface TextFieldDef {
|
||||||
isValid: ((s: string, country?: () => string) => boolean),
|
isValid: ((s: string, country?: () => string) => boolean),
|
||||||
reformat?: ((s: string, country?: () => string) => string),
|
reformat?: ((s: string, country?: () => string) => string),
|
||||||
inputHelper?: (value: UIEventSource<string>, options?: {
|
inputHelper?: (value: UIEventSource<string>, options?: {
|
||||||
location: [number, number]
|
location: [number, number],
|
||||||
|
mapBackgroundLayer?: UIEventSource<any>
|
||||||
}) => InputElement<string>,
|
}) => InputElement<string>,
|
||||||
|
|
||||||
inputmode?: string
|
inputmode?: string
|
||||||
|
@ -118,8 +119,12 @@ export default class ValidatedTextField {
|
||||||
str = "" + str;
|
str = "" + str;
|
||||||
return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
|
return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
|
||||||
}, str => str,
|
}, str => str,
|
||||||
(value) => {
|
(value, options) => {
|
||||||
return new DirectionInput(value);
|
return new DirectionInput(options.mapBackgroundLayer , new UIEventSource<Loc>({
|
||||||
|
lat: options.location[0],
|
||||||
|
lon: options.location[1],
|
||||||
|
zoom: 19
|
||||||
|
}),value);
|
||||||
},
|
},
|
||||||
"numeric"
|
"numeric"
|
||||||
),
|
),
|
||||||
|
@ -216,16 +221,6 @@ export default class ValidatedTextField {
|
||||||
* {string (typename) --> TextFieldDef}
|
* {string (typename) --> TextFieldDef}
|
||||||
*/
|
*/
|
||||||
public static AllTypes = ValidatedTextField.allTypesDict();
|
public static AllTypes = ValidatedTextField.allTypesDict();
|
||||||
|
|
||||||
public static TypeDropdown(): DropDown<string> {
|
|
||||||
const values: { value: string, shown: string }[] = [];
|
|
||||||
const expl = ValidatedTextField.tpList;
|
|
||||||
for (const key in expl) {
|
|
||||||
values.push({value: expl[key].name, shown: `${expl[key].name} - ${expl[key].explanation}`})
|
|
||||||
}
|
|
||||||
return new DropDown<string>("", values)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InputForType(type: string, options?: {
|
public static InputForType(type: string, options?: {
|
||||||
placeholder?: string | UIElement,
|
placeholder?: string | UIElement,
|
||||||
value?: UIEventSource<string>,
|
value?: UIEventSource<string>,
|
||||||
|
@ -235,7 +230,8 @@ export default class ValidatedTextField {
|
||||||
textAreaRows?: number,
|
textAreaRows?: number,
|
||||||
isValid?: ((s: string, country: () => string) => boolean),
|
isValid?: ((s: string, country: () => string) => boolean),
|
||||||
country?: () => string,
|
country?: () => string,
|
||||||
location?: [number /*lat*/, number /*lon*/]
|
location?: [number /*lat*/, number /*lon*/],
|
||||||
|
mapBackgroundLayer?: UIEventSource<any>
|
||||||
}): InputElement<string> {
|
}): InputElement<string> {
|
||||||
options = options ?? {};
|
options = options ?? {};
|
||||||
options.placeholder = options.placeholder ?? type;
|
options.placeholder = options.placeholder ?? type;
|
||||||
|
@ -269,90 +265,16 @@ export default class ValidatedTextField {
|
||||||
|
|
||||||
if (tp.inputHelper) {
|
if (tp.inputHelper) {
|
||||||
input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(), {
|
input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(), {
|
||||||
location: options.location
|
location: options.location,
|
||||||
|
mapBackgroundLayer: options.mapBackgroundLayer
|
||||||
|
|
||||||
}),
|
}),
|
||||||
(a, b) => a, // We can ignore b, as they are linked earlier
|
(a, _) => a, // We can ignore b, as they are linked earlier
|
||||||
a => [a, a]
|
a => [a, a]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NumberInput(type: string = "int", extraValidation: (number: Number) => boolean = undefined): InputElement<number> {
|
|
||||||
const isValid = ValidatedTextField.AllTypes[type].isValid;
|
|
||||||
extraValidation = extraValidation ?? (() => true)
|
|
||||||
|
|
||||||
const fromString = str => {
|
|
||||||
if (!isValid(str)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const n = Number(str);
|
|
||||||
if (!extraValidation(n)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
};
|
|
||||||
const toString = num => {
|
|
||||||
if (num === undefined) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return "" + num;
|
|
||||||
};
|
|
||||||
const textField = ValidatedTextField.InputForType(type);
|
|
||||||
return new InputElementMap(textField, (n0, n1) => n0 === n1, fromString, toString)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyInput(allowEmpty: boolean = false): InputElement<string> {
|
|
||||||
|
|
||||||
function fromString(str) {
|
|
||||||
if (str?.match(/^[a-zA-Z][a-zA-Z0-9:_-]*$/)) {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
if (str === "" && allowEmpty) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const toString = str => str
|
|
||||||
|
|
||||||
function isSame(str0, str1) {
|
|
||||||
return str0 === str1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const textfield = new TextField({
|
|
||||||
placeholder: "key",
|
|
||||||
isValid: str => fromString(str) !== undefined,
|
|
||||||
value: new UIEventSource<string>("")
|
|
||||||
});
|
|
||||||
|
|
||||||
return new InputElementMap(textfield, isSame, fromString, toString);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mapped<T>(fromString: (str) => T, toString: (T) => string, options?: {
|
|
||||||
placeholder?: string | UIElement,
|
|
||||||
type?: string,
|
|
||||||
value?: UIEventSource<string>,
|
|
||||||
startValidated?: boolean,
|
|
||||||
textArea?: boolean,
|
|
||||||
textAreaRows?: number,
|
|
||||||
isValid?: ((string: string) => boolean),
|
|
||||||
country?: () => string
|
|
||||||
}): InputElement<T> {
|
|
||||||
let textField: InputElement<string>;
|
|
||||||
if (options?.type) {
|
|
||||||
textField = ValidatedTextField.InputForType(options.type, options);
|
|
||||||
} else {
|
|
||||||
textField = new TextField(options);
|
|
||||||
}
|
|
||||||
return new InputElementMap(
|
|
||||||
textField, (a, b) => a === b,
|
|
||||||
fromString, toString
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HelpText(): string {
|
public static HelpText(): string {
|
||||||
const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
|
const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
|
||||||
return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
|
return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
|
||||||
|
@ -363,7 +285,8 @@ export default class ValidatedTextField {
|
||||||
isValid?: ((s: string, country?: () => string) => boolean),
|
isValid?: ((s: string, country?: () => string) => boolean),
|
||||||
reformat?: ((s: string, country?: () => string) => string),
|
reformat?: ((s: string, country?: () => string) => string),
|
||||||
inputHelper?: (value: UIEventSource<string>, options?: {
|
inputHelper?: (value: UIEventSource<string>, options?: {
|
||||||
location: [number, number]
|
location: [number, number],
|
||||||
|
mapBackgroundLayer: UIEventSource<any>
|
||||||
}) => InputElement<string>,
|
}) => InputElement<string>,
|
||||||
inputmode?: string): TextFieldDef {
|
inputmode?: string): TextFieldDef {
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,8 @@ export default class TagRenderingQuestion extends UIElement {
|
||||||
let input: InputElement<string> = ValidatedTextField.InputForType(this._configuration.freeform.type, {
|
let input: InputElement<string> = ValidatedTextField.InputForType(this._configuration.freeform.type, {
|
||||||
isValid: (str) => (str.length <= 255),
|
isValid: (str) => (str.length <= 255),
|
||||||
country: () => this._tags.data._country,
|
country: () => this._tags.data._country,
|
||||||
location: [this._tags.data._lat, this._tags.data._lon]
|
location: [this._tags.data._lat, this._tags.data._lon],
|
||||||
|
mapBackgroundLayer: State.state.backgroundLayer
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._applicableUnit) {
|
if (this._applicableUnit) {
|
||||||
|
|
|
@ -15,13 +15,18 @@ export default class ShowDataLayer {
|
||||||
private _layerDict;
|
private _layerDict;
|
||||||
private readonly _leafletMap: UIEventSource<L.Map>;
|
private readonly _leafletMap: UIEventSource<L.Map>;
|
||||||
private _cleanCount = 0;
|
private _cleanCount = 0;
|
||||||
|
private readonly _enablePopups: boolean;
|
||||||
|
private readonly _features : UIEventSource<{ feature: any, freshness: Date }[]>
|
||||||
|
|
||||||
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
|
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
|
||||||
leafletMap: UIEventSource<L.Map>,
|
leafletMap: UIEventSource<L.Map>,
|
||||||
layoutToUse: UIEventSource<LayoutConfig>) {
|
layoutToUse: UIEventSource<LayoutConfig>,
|
||||||
|
enablePopups= true,
|
||||||
|
zoomToFeatures = false) {
|
||||||
this._leafletMap = leafletMap;
|
this._leafletMap = leafletMap;
|
||||||
|
this._enablePopups = enablePopups;
|
||||||
|
this._features = features;
|
||||||
const self = this;
|
const self = this;
|
||||||
const mp = leafletMap.data;
|
|
||||||
self._layerDict = {};
|
self._layerDict = {};
|
||||||
|
|
||||||
layoutToUse.addCallbackAndRun(layoutToUse => {
|
layoutToUse.addCallbackAndRun(layoutToUse => {
|
||||||
|
@ -39,7 +44,9 @@ export default class ShowDataLayer {
|
||||||
if (features.data === undefined) {
|
if (features.data === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (leafletMap.data === undefined) {
|
const mp = leafletMap.data;
|
||||||
|
|
||||||
|
if(mp === undefined){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +75,11 @@ export default class ShowDataLayer {
|
||||||
mp.addLayer(geoLayer)
|
mp.addLayer(geoLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(zoomToFeatures){
|
||||||
|
mp.fitBounds(geoLayer.getBounds())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
State.state.selectedElement.ping();
|
State.state.selectedElement.ping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +89,7 @@ export default class ShowDataLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private createStyleFor(feature) {
|
private createStyleFor(feature) {
|
||||||
const tagsSource = State.state.allElements.addOrGetElement(feature);
|
const tagsSource = State.state.allElements.addOrGetElement(feature);
|
||||||
// Every object is tied to exactly one layer
|
// Every object is tied to exactly one layer
|
||||||
|
@ -97,9 +110,13 @@ export default class ShowDataLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
|
const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
|
||||||
|
const baseElement = style.icon.html;
|
||||||
|
if(!this._enablePopups){
|
||||||
|
baseElement.SetStyle("cursor: initial !important")
|
||||||
|
}
|
||||||
return L.marker(latLng, {
|
return L.marker(latLng, {
|
||||||
icon: L.divIcon({
|
icon: L.divIcon({
|
||||||
html: style.icon.html.ConstructElement(),
|
html: baseElement.ConstructElement(),
|
||||||
className: style.icon.className,
|
className: style.icon.className,
|
||||||
iconAnchor: style.icon.iconAnchor,
|
iconAnchor: style.icon.iconAnchor,
|
||||||
iconUrl: style.icon.iconUrl,
|
iconUrl: style.icon.iconUrl,
|
||||||
|
@ -115,10 +132,12 @@ export default class ShowDataLayer {
|
||||||
console.warn("No layer found for object (probably a now disabled layer)", feature, this._layerDict)
|
console.warn("No layer found for object (probably a now disabled layer)", feature, this._layerDict)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (layer.title === undefined) {
|
if (layer.title === undefined || !this._enablePopups) {
|
||||||
// No popup action defined -> Don't do anything
|
// No popup action defined -> Don't do anything
|
||||||
|
// or probably a map in the popup - no popups needed!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const popup = L.popup({
|
const popup = L.popup({
|
||||||
autoPan: true,
|
autoPan: true,
|
||||||
closeOnEscapeKey: true,
|
closeOnEscapeKey: true,
|
||||||
|
@ -171,15 +190,15 @@ export default class ShowDataLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private CreateGeojsonLayer(): L.Layer {
|
private CreateGeojsonLayer(): L.Layer {
|
||||||
const self = this;
|
const self = this;
|
||||||
const data = {
|
const data = {
|
||||||
type: "FeatureCollection",
|
type: "FeatureCollection",
|
||||||
features: []
|
features: []
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return L.geoJSON(data, {
|
return L.geoJSON(data, {
|
||||||
style: feature => self.createStyleFor(feature),
|
style: feature => self.createStyleFor(feature),
|
||||||
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
|
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
|
||||||
onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer)
|
onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,10 @@ import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||||
import Title from "./Base/Title";
|
import Title from "./Base/Title";
|
||||||
import Table from "./Base/Table";
|
import Table from "./Base/Table";
|
||||||
import Histogram from "./BigComponents/Histogram";
|
import Histogram from "./BigComponents/Histogram";
|
||||||
|
import Loc from "../Models/Loc";
|
||||||
|
import ShowDataLayer from "./ShowDataLayer";
|
||||||
|
import Minimap from "./Base/Minimap";
|
||||||
|
import {Utils} from "../Utils";
|
||||||
|
|
||||||
export default class SpecialVisualizations {
|
export default class SpecialVisualizations {
|
||||||
|
|
||||||
|
@ -32,7 +36,6 @@ export default class SpecialVisualizations {
|
||||||
example?: string,
|
example?: string,
|
||||||
args: { name: string, defaultValue?: string, doc: string }[]
|
args: { name: string, defaultValue?: string, doc: string }[]
|
||||||
}[] =
|
}[] =
|
||||||
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
funcName: "all_tags",
|
funcName: "all_tags",
|
||||||
|
@ -86,7 +89,80 @@ export default class SpecialVisualizations {
|
||||||
return new ImageUploadFlow(tags, args[0])
|
return new ImageUploadFlow(tags, args[0])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
funcName: "minimap",
|
||||||
|
docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div",
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
doc: "The zoomlevel: the higher, the more zoomed in with 1 being the entire world and 19 being really close",
|
||||||
|
name: "zoomlevel",
|
||||||
|
defaultValue: "18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap.",
|
||||||
|
name: "idKey",
|
||||||
|
defaultValue: "id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`",
|
||||||
|
constr: (state, tagSource, args) => {
|
||||||
|
|
||||||
|
const keys = [...args]
|
||||||
|
keys.splice(0, 1)
|
||||||
|
const featureStore = state.allElements.ContainingFeatures
|
||||||
|
const featuresToShow: UIEventSource<{ freshness: Date, feature: any }[]> = tagSource.map(properties => {
|
||||||
|
const values: string[] = Utils.NoNull(keys.map(key => properties[key]))
|
||||||
|
const features: { freshness: Date, feature: any }[] = []
|
||||||
|
for (const value of values) {
|
||||||
|
let idList = [value]
|
||||||
|
if (value.startsWith("[")) {
|
||||||
|
// This is a list of values
|
||||||
|
idList = JSON.parse(value)
|
||||||
|
}
|
||||||
|
for (const id of idList) {
|
||||||
|
features.push({
|
||||||
|
freshness: new Date(),
|
||||||
|
feature: featureStore.get(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return features
|
||||||
|
})
|
||||||
|
const properties = tagSource.data;
|
||||||
|
|
||||||
|
let zoom = 18
|
||||||
|
if (args[0]) {
|
||||||
|
const parsed = Number(args[0])
|
||||||
|
if (!isNaN(parsed) && parsed > 0 && parsed < 25) {
|
||||||
|
zoom = parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const minimap = new Minimap(
|
||||||
|
{
|
||||||
|
background: state.backgroundLayer,
|
||||||
|
location: new UIEventSource<Loc>({
|
||||||
|
lat: Number(properties._lat),
|
||||||
|
lon: Number(properties._lon),
|
||||||
|
zoom: zoom
|
||||||
|
}),
|
||||||
|
allowMoving: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
new ShowDataLayer(
|
||||||
|
featuresToShow,
|
||||||
|
minimap.leafletMap,
|
||||||
|
State.state.layoutToUse,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
minimap.SetStyle("overflow: hidden; pointer-events: none;")
|
||||||
|
return minimap;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
funcName: "reviews",
|
funcName: "reviews",
|
||||||
docs: "Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten",
|
docs: "Adds an overview of the mangrove-reviews of this object. Mangrove.Reviews needs - in order to identify the reviewed object - a coordinate and a name. By default, the name of the object is given, but this can be overwritten",
|
||||||
|
@ -169,7 +245,7 @@ export default class SpecialVisualizations {
|
||||||
defaultValue: ""
|
defaultValue: ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "colors",
|
name: "colors*",
|
||||||
doc: "(Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33`"
|
doc: "(Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33`"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -260,33 +336,33 @@ export default class SpecialVisualizations {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{funcName: "canonical",
|
{
|
||||||
docs: "Converts a short, canonical value into the long, translated text",
|
funcName: "canonical",
|
||||||
example: "{canonical(length)} will give 42 metre (in french)",
|
docs: "Converts a short, canonical value into the long, translated text",
|
||||||
args:[{
|
example: "{canonical(length)} will give 42 metre (in french)",
|
||||||
name:"key",
|
args: [{
|
||||||
doc: "The key of the tag to give the canonical text for"
|
name: "key",
|
||||||
}],
|
doc: "The key of the tag to give the canonical text for"
|
||||||
constr: (state, tagSource, args) => {
|
}],
|
||||||
const key = args [0]
|
constr: (state, tagSource, args) => {
|
||||||
return new VariableUiElement(
|
const key = args [0]
|
||||||
tagSource.map(tags => tags[key]).map(value => {
|
return new VariableUiElement(
|
||||||
if(value === undefined){
|
tagSource.map(tags => tags[key]).map(value => {
|
||||||
return undefined
|
if (value === undefined) {
|
||||||
}
|
return undefined
|
||||||
const unit = state.layoutToUse.data.units.filter(unit => unit.isApplicableToKey(key))[0]
|
}
|
||||||
if(unit === undefined){
|
const unit = state.layoutToUse.data.units.filter(unit => unit.isApplicableToKey(key))[0]
|
||||||
return value;
|
if (unit === undefined) {
|
||||||
}
|
return value;
|
||||||
|
}
|
||||||
return unit.asHumanLongValue(value);
|
|
||||||
|
return unit.asHumanLongValue(value);
|
||||||
},
|
|
||||||
[ state.layoutToUse])
|
},
|
||||||
|
[state.layoutToUse])
|
||||||
|
)
|
||||||
)
|
}
|
||||||
}}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();
|
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();
|
||||||
|
@ -313,7 +389,7 @@ export default class SpecialVisualizations {
|
||||||
return new Combine([
|
return new Combine([
|
||||||
new Title("Special tag renderings", 3),
|
new Title("Special tag renderings", 3),
|
||||||
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
"In a tagrendering, some special values are substituted by an advanced UI-element. This allows advanced features and visualizations to be reused by custom themes or even to query third-party API's.",
|
||||||
"General usage is <b>{func_name()}</b> or <b>{func_name(arg, someotherarg)}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
"General usage is <b>{func_name()}</b>, <b>{func_name(arg, someotherarg)}</b> or <b>{func_name(args):cssStyle}</b>. Note that you <i>do not</i> need to use quotes around your arguments, the comma is enough to seperate them. This also implies you cannot use a comma in your args",
|
||||||
...helpTexts
|
...helpTexts
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,14 +17,14 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
super(
|
super(
|
||||||
tagsSource.map(tags => {
|
tagsSource.map(tags => {
|
||||||
const txt = Utils.SubstituteKeys(translation.txt, tags)
|
const txt = Utils.SubstituteKeys(translation.txt, tags)
|
||||||
if (txt === undefined) {
|
if (txt === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tagsSource))
|
return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tagsSource))
|
||||||
}, [Locale.language])
|
}, [Locale.language])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
this.SetClass("w-full")
|
this.SetClass("w-full")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +34,14 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
||||||
|
|
||||||
// Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
|
// Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
|
||||||
const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)}(.*)`);
|
const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)(:.*)?}(.*)`);
|
||||||
if (matched != null) {
|
if (matched != null) {
|
||||||
|
|
||||||
// We found a special component that should be brought to live
|
// We found a special component that should be brought to live
|
||||||
const partBefore = SubstitutedTranslation.EvaluateSpecialComponents(matched[1], tags);
|
const partBefore = SubstitutedTranslation.EvaluateSpecialComponents(matched[1], tags);
|
||||||
const argument = matched[2].trim();
|
const argument = matched[2].trim();
|
||||||
const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[3], tags);
|
const style = matched[3]?.substring(1) ?? ""
|
||||||
|
const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[4], tags);
|
||||||
try {
|
try {
|
||||||
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
||||||
if (argument.length > 0) {
|
if (argument.length > 0) {
|
||||||
|
@ -56,13 +57,14 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
|
|
||||||
|
|
||||||
let element: BaseUIElement = new FixedUiElement(`Constructing ${knownSpecial}(${args.join(", ")})`)
|
let element: BaseUIElement = new FixedUiElement(`Constructing ${knownSpecial}(${args.join(", ")})`)
|
||||||
try{
|
try {
|
||||||
element = knownSpecial.constr(State.state, tags, args);
|
element = knownSpecial.constr(State.state, tags, args);
|
||||||
}catch(e){
|
element.SetStyle(style)
|
||||||
|
} catch (e) {
|
||||||
console.error("SPECIALRENDERING FAILED for", tags.data.id, e)
|
console.error("SPECIALRENDERING FAILED for", tags.data.id, e)
|
||||||
element = new FixedUiElement(`Could not generate special rendering for ${knownSpecial}(${args.join(", ")}) ${e}`).SetClass("alert")
|
element = new FixedUiElement(`Could not generate special rendering for ${knownSpecial}(${args.join(", ")}) ${e}`).SetClass("alert")
|
||||||
}
|
}
|
||||||
|
|
||||||
return [...partBefore, element, ...partAfter]
|
return [...partBefore, element, ...partAfter]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
29
Utils.ts
29
Utils.ts
|
@ -1,5 +1,4 @@
|
||||||
import * as colors from "./assets/colors.json"
|
import * as colors from "./assets/colors.json"
|
||||||
import {Util} from "leaflet";
|
|
||||||
|
|
||||||
export class Utils {
|
export class Utils {
|
||||||
|
|
||||||
|
@ -324,6 +323,34 @@ export class Utils {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static externalDownloadFunction: (url: string) => Promise<any>;
|
||||||
|
|
||||||
|
public static downloadJson(url: string): Promise<any>{
|
||||||
|
if(this.externalDownloadFunction !== undefined){
|
||||||
|
return this.externalDownloadFunction(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(
|
||||||
|
(resolve, reject) => {
|
||||||
|
try{
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.onload = () => {
|
||||||
|
if (xhr.status == 200) {
|
||||||
|
resolve(JSON.parse(xhr.response))
|
||||||
|
} else {
|
||||||
|
reject(xhr.statusText)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.open('GET', url);
|
||||||
|
xhr.send();
|
||||||
|
}catch(e){
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a 'download file' popup which will download the contents
|
* Triggers a 'download file' popup which will download the contents
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"contributors":[{"contributor":"Pieter Vander Vennet", "commits":738},{"contributor":"pietervdvn", "commits":718},{"contributor":"Weblate", "commits":35},{"contributor":"Tobias", "commits":35},{"contributor":"Christian Neumann", "commits":33},{"contributor":"Win Olario", "commits":31},{"contributor":"Pieter Fiers", "commits":31},{"contributor":"Sebastian Kürten", "commits":16},{"contributor":"Marco", "commits":16},{"contributor":"Joost", "commits":16},{"contributor":"ToastHawaii", "commits":15},{"contributor":"J. Lavoie", "commits":14},{"contributor":"Bavo Vanderghote", "commits":12},{"contributor":"Artem", "commits":12},{"contributor":"Supaplex", "commits":9},{"contributor":"Jacque Fresco", "commits":9},{"contributor":"Midgard", "commits":8},{"contributor":"Mateusz Konieczny", "commits":8},{"contributor":"yopaseopor", "commits":7},{"contributor":"Flo Edelmann", "commits":7},{"contributor":"Binnette", "commits":7},{"contributor":"Allan Nordhøy", "commits":7},{"contributor":"pelderson", "commits":6},{"contributor":"lvgx", "commits":6},{"contributor":"dependabot[bot]", "commits":6},{"contributor":"Alexey Shabanov", "commits":6},{"contributor":"SiegbjornSitumeang", "commits":4},{"contributor":"Polgár Sándor", "commits":4},{"contributor":"Hiroshi Miura", "commits":4},{"contributor":"vankos", "commits":3},{"contributor":"Léo Villeveygoux", "commits":3},{"contributor":"JCGF-OSM", "commits":3},{"contributor":"Jan Zabel", "commits":3},{"contributor":"Hosted Weblate", "commits":3},{"contributor":"David Haberthür", "commits":3},{"contributor":"快乐的老鼠宝宝", "commits":2},{"contributor":"Wiktor Przybylski", "commits":2},{"contributor":"Vinicius", "commits":2},{"contributor":"Stanislas Gueniffey", "commits":2},{"contributor":"Robin van der Linde", "commits":2},{"contributor":"riiga", "commits":2},{"contributor":"pbarban", "commits":2},{"contributor":"mic140", "commits":2},{"contributor":"Leo Alcaraz", "commits":2},{"contributor":"Jose Luis Infante", "commits":2},{"contributor":"Heiko", "commits":2},{"contributor":"graveelius", "commits":2},{"contributor":"Tomas Fiers", "commits":1},{"contributor":"Thibault Molleman", "commits":1},{"contributor":"tbowdecl97", "commits":1},{"contributor":"Sebastian", "commits":1},{"contributor":"Sean Young", "commits":1},{"contributor":"Schouppe Joost", "commits":1},{"contributor":"Noémie", "commits":1},{"contributor":"mozita", "commits":1},{"contributor":"Michał Targoński", "commits":1},{"contributor":"Iváns", "commits":1},{"contributor":"Eric Armijo", "commits":1},{"contributor":"Damian Pułka", "commits":1},{"contributor":"Carlos Ramos Carreño", "commits":1},{"contributor":"Beardhatcode", "commits":1}]}
|
{"contributors":[{"contributor":"pietervdvn", "commits":794},{"contributor":"Pieter Vander Vennet", "commits":744},{"contributor":"Weblate", "commits":38},{"contributor":"Tobias", "commits":35},{"contributor":"Christian Neumann", "commits":33},{"contributor":"Win Olario", "commits":31},{"contributor":"Pieter Fiers", "commits":31},{"contributor":"Sebastian Kürten", "commits":17},{"contributor":"Joost", "commits":17},{"contributor":"Marco", "commits":16},{"contributor":"Artem", "commits":16},{"contributor":"Allan Nordhøy", "commits":16},{"contributor":"ToastHawaii", "commits":15},{"contributor":"Supaplex", "commits":14},{"contributor":"J. Lavoie", "commits":14},{"contributor":"Bavo Vanderghote", "commits":12},{"contributor":"Jacque Fresco", "commits":9},{"contributor":"Midgard", "commits":8},{"contributor":"Mateusz Konieczny", "commits":8},{"contributor":"yopaseopor", "commits":7},{"contributor":"Hosted Weblate", "commits":7},{"contributor":"Flo Edelmann", "commits":7},{"contributor":"Binnette", "commits":7},{"contributor":"pelderson", "commits":6},{"contributor":"lvgx", "commits":6},{"contributor":"dependabot[bot]", "commits":6},{"contributor":"Alexey Shabanov", "commits":6},{"contributor":"SiegbjornSitumeang", "commits":4},{"contributor":"Polgár Sándor", "commits":4},{"contributor":"Hiroshi Miura", "commits":4},{"contributor":"Wiktor Przybylski", "commits":3},{"contributor":"vankos", "commits":3},{"contributor":"Léo Villeveygoux", "commits":3},{"contributor":"JCGF-OSM", "commits":3},{"contributor":"Jan Zabel", "commits":3},{"contributor":"Erik Palm", "commits":3},{"contributor":"David Haberthür", "commits":3},{"contributor":"快乐的老鼠宝宝", "commits":2},{"contributor":"Vinicius", "commits":2},{"contributor":"Stanislas Gueniffey", "commits":2},{"contributor":"Robin van der Linde", "commits":2},{"contributor":"riiga", "commits":2},{"contributor":"pbarban", "commits":2},{"contributor":"mic140", "commits":2},{"contributor":"Leo Alcaraz", "commits":2},{"contributor":"Jose Luis Infante", "commits":2},{"contributor":"Heiko", "commits":2},{"contributor":"graveelius", "commits":2},{"contributor":"Damian Tokarski", "commits":2},{"contributor":"Tomas Fiers", "commits":1},{"contributor":"Thibault Molleman", "commits":1},{"contributor":"tbowdecl97", "commits":1},{"contributor":"Sebastian", "commits":1},{"contributor":"Sean Young", "commits":1},{"contributor":"Schouppe Joost", "commits":1},{"contributor":"Noémie", "commits":1},{"contributor":"mozita", "commits":1},{"contributor":"Michał Targoński", "commits":1},{"contributor":"liimee", "commits":1},{"contributor":"Jeff Huang", "commits":1},{"contributor":"Iváns", "commits":1},{"contributor":"Eric Armijo", "commits":1},{"contributor":"Damian Pułka", "commits":1},{"contributor":"Carlos Ramos Carreño", "commits":1},{"contributor":"Beardhatcode", "commits":1}]}
|
|
@ -72,6 +72,9 @@
|
||||||
],
|
],
|
||||||
"tagRenderings": [
|
"tagRenderings": [
|
||||||
"images",
|
"images",
|
||||||
|
{
|
||||||
|
"render": "{minimap():height: 9rem; border-radius: 2.5rem; overflow:hidden;border:1px solid gray}"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"render": {
|
"render": {
|
||||||
"en": "The name of this bookcase is {name}",
|
"en": "The name of this bookcase is {name}",
|
||||||
|
|
|
@ -6,48 +6,97 @@
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
id="svg8"
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
version="1.1"
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
viewBox="0 0 100 100"
|
version="1.0"
|
||||||
height="100"
|
width="860.50732pt"
|
||||||
width="100">
|
height="860.50732pt"
|
||||||
<metadata
|
viewBox="0 0 860.50732 860.50732"
|
||||||
id="metadata8">
|
preserveAspectRatio="xMidYMid meet"
|
||||||
<rdf:RDF>
|
id="svg14"
|
||||||
<cc:Work
|
sodipodi:docname="direction_gradient.svg"
|
||||||
rdf:about="">
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<defs
|
<defs
|
||||||
id="defs6">
|
id="defs18">
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="linearGradient820">
|
inkscape:collect="always"
|
||||||
|
id="linearGradient832">
|
||||||
<stop
|
<stop
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
offset="0"
|
offset="0"
|
||||||
id="innercolor" />
|
id="stop828" />
|
||||||
<stop
|
<stop
|
||||||
style="stop-color:#000000;stop-opacity:0"
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
offset="1"
|
offset="1"
|
||||||
id="outercolor" />
|
id="stop830" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<radialGradient
|
<radialGradient
|
||||||
xlink:href="#linearGradient820"
|
inkscape:collect="always"
|
||||||
id="radialGradient828"
|
xlink:href="#linearGradient832"
|
||||||
cx="49.787739"
|
id="radialGradient838"
|
||||||
cy="53.828533"
|
cx="430.25363"
|
||||||
fx="49.787739"
|
cy="519.61188"
|
||||||
fy="53.828533"
|
fx="430.25363"
|
||||||
r="28.883806"
|
fy="519.61188"
|
||||||
gradientTransform="matrix(1.5439431,-0.01852438,0.02075364,1.7297431,-27.986574,-42.187244)"
|
r="305.54589"
|
||||||
|
gradientTransform="matrix(0.95288409,-0.94890664,0.94542304,0.94938587,-470.98122,345.21193)"
|
||||||
gradientUnits="userSpaceOnUse" />
|
gradientUnits="userSpaceOnUse" />
|
||||||
</defs>
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="999"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="0.70710678"
|
||||||
|
inkscape:cx="279.00239"
|
||||||
|
inkscape:cy="856.75313"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg14">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="430.25363,862.49682"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide832"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-42.427977,430.25368"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide834"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="398.27788,720.18823"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide840"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
<path
|
<path
|
||||||
id="path821"
|
style="fill:url(#radialGradient838);fill-opacity:1;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="M 50,50 21.042889,9.3993342 C 36.191421,-2.001434 60.726552,-3.6768009 78.8105,9.1490935 Z"
|
d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z"
|
||||||
style="fill:url(#radialGradient828);fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
id="path836"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.2 KiB |
70
assets/svg/direction_masked.svg
Normal file
70
assets/svg/direction_masked.svg
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
width="860.50732pt"
|
||||||
|
height="860.50732pt"
|
||||||
|
viewBox="0 0 860.50732 860.50732"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="direction_masked.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="999"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="0.70710678"
|
||||||
|
inkscape:cx="418.71218"
|
||||||
|
inkscape:cy="725.43016"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg14">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="430.25363,862.49682"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide832"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-42.427977,430.25368"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide834"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:0.39163497;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 124.70776,124.7084 A 432.375,432.375 0 0 0 -2.121338,430.25429 432.375,432.375 0 0 0 430.25366,862.62929 432.375,432.375 0 0 0 862.62866,430.25429 432.375,432.375 0 0 0 735.79811,124.70986 L 430.25366,430.25429 Z"
|
||||||
|
id="path836"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
72
assets/svg/direction_outline.svg
Normal file
72
assets/svg/direction_outline.svg
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
width="860.50732pt"
|
||||||
|
height="860.50732pt"
|
||||||
|
viewBox="0 0 860.50732 860.50732"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="direction_outline.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="999"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="0.35355339"
|
||||||
|
inkscape:cx="72.756921"
|
||||||
|
inkscape:cy="792.67347"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg14">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="430.25363,862.49682"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide832"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-42.427977,430.25368"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide834"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:0.39163501;stroke:none;stroke-width:2.83464575;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 735.79979,124.70799 C 654.79116,43.598883 544.88842,-2.0206645 430.25389,-2.121103 315.61937,-2.0206592 205.71663,43.598888 124.70801,124.70799 l 305.54588,305.54589 z"
|
||||||
|
id="path836"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
72
assets/svg/direction_stroke.svg
Normal file
72
assets/svg/direction_stroke.svg
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
width="860.50732pt"
|
||||||
|
height="860.50732pt"
|
||||||
|
viewBox="0 0 860.50732 860.50732"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
id="svg14"
|
||||||
|
sodipodi:docname="direction_stroke.svg"
|
||||||
|
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||||
|
<defs
|
||||||
|
id="defs18" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="999"
|
||||||
|
id="namedview16"
|
||||||
|
showgrid="false"
|
||||||
|
showguides="true"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="482.69344"
|
||||||
|
inkscape:cy="931.96415"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg14">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="430.25363,862.49682"
|
||||||
|
orientation="1,0"
|
||||||
|
id="guide832"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="-42.427977,430.25368"
|
||||||
|
orientation="0,1"
|
||||||
|
id="guide834"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata2">
|
||||||
|
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:0.39163501;stroke:#000000;stroke-width:3.74999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 735.79953,129.20772 C 654.7909,48.098612 544.88816,2.4790648 430.25363,2.3786263 315.61911,2.4790701 205.71637,48.098617 124.70775,129.20772 l 305.54588,305.54589 z"
|
||||||
|
id="path836"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -5,6 +5,9 @@
|
||||||
"reviews": {
|
"reviews": {
|
||||||
"render": "{reviews()}"
|
"render": "{reviews()}"
|
||||||
},
|
},
|
||||||
|
"minimap": {
|
||||||
|
"render": "{minimap(19, id): width:100%; height:6rem; border-radius:999rem; overflow: hidden; pointer-events: none;}"
|
||||||
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"question": {
|
"question": {
|
||||||
"en": "What is the phone number of {name}?",
|
"en": "What is the phone number of {name}?",
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
"ru": "Найти места остановки, чтобы провести ночь в автофургоне",
|
"ru": "Найти места остановки, чтобы провести ночь в автофургоне",
|
||||||
"ja": "キャンパーと夜を共にするキャンプサイトを見つける",
|
"ja": "キャンパーと夜を共にするキャンプサイトを見つける",
|
||||||
"fr": "Trouver des sites pour passer la nuit avec votre camping-car",
|
"fr": "Trouver des sites pour passer la nuit avec votre camping-car",
|
||||||
"zh_Hant": "露營者尋找渡過夜晚的場地"
|
"zh_Hant": "露營者尋找渡過夜晚的場地",
|
||||||
|
"nl": "Vind locaties waar je de nacht kan doorbrengen met je mobilehome"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"en": "This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app.",
|
"en": "This site collects all official camper stopover places and places where you can dump grey and black water. You can add details about the services provided and the cost. Add pictures and reviews. This is a website and a webapp. The data is stored in OpenStreetMap, so it will be free forever and can be re-used by any app.",
|
||||||
|
|
|
@ -781,7 +781,8 @@
|
||||||
"en": "Climbing is not possible here",
|
"en": "Climbing is not possible here",
|
||||||
"de": "Hier kann nicht geklettert werden",
|
"de": "Hier kann nicht geklettert werden",
|
||||||
"ja": "ここでは登ることができない",
|
"ja": "ここでは登ることができない",
|
||||||
"nb_NO": "Klatring er ikke mulig her"
|
"nb_NO": "Klatring er ikke mulig her",
|
||||||
|
"nl": "Klimmen is hier niet mogelijk"
|
||||||
},
|
},
|
||||||
"hideInAnswer": true
|
"hideInAnswer": true
|
||||||
},
|
},
|
||||||
|
@ -795,7 +796,8 @@
|
||||||
"en": "Climbing is possible here",
|
"en": "Climbing is possible here",
|
||||||
"de": "Hier kann geklettert werden",
|
"de": "Hier kann geklettert werden",
|
||||||
"ja": "ここでは登ることができる",
|
"ja": "ここでは登ることができる",
|
||||||
"nb_NO": "Klatring er mulig her"
|
"nb_NO": "Klatring er mulig her",
|
||||||
|
"nl": "Klimmen is hier niet toegelaten"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -804,7 +806,8 @@
|
||||||
"en": "Climbing is not possible here",
|
"en": "Climbing is not possible here",
|
||||||
"de": "Hier kann nicht geklettert werden",
|
"de": "Hier kann nicht geklettert werden",
|
||||||
"ja": "ここでは登ることができない",
|
"ja": "ここでは登ることができない",
|
||||||
"nb_NO": "Klatring er ikke mulig her"
|
"nb_NO": "Klatring er ikke mulig her",
|
||||||
|
"nl": "Klimmen is hier niet toegelaten"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -848,7 +851,8 @@
|
||||||
"question": {
|
"question": {
|
||||||
"en": "Is there a (unofficial) website with more informations (e.g. topos)?",
|
"en": "Is there a (unofficial) website with more informations (e.g. topos)?",
|
||||||
"de": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?",
|
"de": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?",
|
||||||
"ja": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?"
|
"ja": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?",
|
||||||
|
"nl": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
|
||||||
},
|
},
|
||||||
"condition": {
|
"condition": {
|
||||||
"and": [
|
"and": [
|
||||||
|
@ -870,13 +874,15 @@
|
||||||
{
|
{
|
||||||
"if": "_embedding_feature:access=yes",
|
"if": "_embedding_feature:access=yes",
|
||||||
"then": {
|
"then": {
|
||||||
"en": "<span class='subtle'>The <a href='#{_embedding_feature:id}'>containing feature</a> states that this is</span> publicly accessible<br/>{_embedding_feature:access:description}"
|
"en": "<span class='subtle'>The <a href='#{_embedding_feature:id}'>containing feature</a> states that this is</span> publicly accessible<br/>{_embedding_feature:access:description}",
|
||||||
|
"nl": "<span class='subtle'>Een <a href='#{_embedding_feature:id}'>omvattend element</a> geeft aan dat dit <span>publiek toegangkelijk is</span><br/>{_embedding_feature:access:description}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"if": "_embedding_feature:access=permit",
|
"if": "_embedding_feature:access=permit",
|
||||||
"then": {
|
"then": {
|
||||||
"en": "<span class='subtle'>The <a href='#{_embedding_feature:id}'>containing feature</a> states that </span> a permit is needed to access<br/>{_embedding_feature:access:description}"
|
"en": "<span class='subtle'>The <a href='#{_embedding_feature:id}'>containing feature</a> states that </span> a permit is needed to access<br/>{_embedding_feature:access:description}",
|
||||||
|
"nl": "<span class='subtle'>Een <a href='#{_embedding_feature:id}'>omvattend element</a> geeft aan dat</span> een toelating nodig is om hier te klimmen<br/>{_embedding_feature:access:description}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
6
index.ts
6
index.ts
|
@ -14,10 +14,12 @@ import Translations from "./UI/i18n/Translations";
|
||||||
import CountryCoder from "latlon2country"
|
import CountryCoder from "latlon2country"
|
||||||
|
|
||||||
import SimpleMetaTagger from "./Logic/SimpleMetaTagger";
|
import SimpleMetaTagger from "./Logic/SimpleMetaTagger";
|
||||||
|
import Minimap from "./UI/Base/Minimap";
|
||||||
|
import DirectionInput from "./UI/Input/DirectionInput";
|
||||||
|
|
||||||
// Workaround for a stupid crash: inject the function
|
// Workaround for a stupid crash: inject some functions which would give stupid circular dependencies or crash the other nodejs scripts
|
||||||
SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
|
SimpleMetaTagger.coder = new CountryCoder("https://pietervdvn.github.io/latlon2country/");
|
||||||
|
DirectionInput.constructMinimap = options => new Minimap(options)
|
||||||
|
|
||||||
let defaultLayout = ""
|
let defaultLayout = ""
|
||||||
// --------------------- Special actions based on the parameters -----------------
|
// --------------------- Special actions based on the parameters -----------------
|
||||||
|
|
|
@ -72,6 +72,12 @@
|
||||||
"emailOf": "What is the email address of {category}?",
|
"emailOf": "What is the email address of {category}?",
|
||||||
"emailIs": "The email address of this {category} is <a href='mailto:{email}' target='_blank'>{email}</a>"
|
"emailIs": "The email address of this {category} is <a href='mailto:{email}' target='_blank'>{email}</a>"
|
||||||
},
|
},
|
||||||
|
"morescreen": {
|
||||||
|
"intro": "<h3>More thematic maps?</h3>Do you enjoy collecting geodata? <br/>There are more themes available.",
|
||||||
|
"requestATheme": "If you want a custom-built quest, request it in the issue tracker",
|
||||||
|
"streetcomplete": "Another, similar application is <a href='https://play.google.com/store/apps/details?id=de.westnordost.streetcomplete' class='underline hover:text-blue-800' class='underline hover:text-blue-800' target='_blank'>StreetComplete</a>.",
|
||||||
|
"createYourOwnTheme": "Create your own MapComplete theme from scratch"
|
||||||
|
},
|
||||||
"sharescreen": {
|
"sharescreen": {
|
||||||
"intro": "<h3>Share this map</h3> Share this map by copying the link below and sending it to friends and family:",
|
"intro": "<h3>Share this map</h3> Share this map by copying the link below and sending it to friends and family:",
|
||||||
"addToHomeScreen": "<h3>Add to your home screen</h3>You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen button' in the URL bar to do this.",
|
"addToHomeScreen": "<h3>Add to your home screen</h3>You can easily add this website to your smartphone home screen for a native feel. Click the 'add to home screen button' in the URL bar to do this.",
|
||||||
|
|
|
@ -1072,7 +1072,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
"1": {
|
"2": {
|
||||||
"render": "Der Name dieses Bücherschrank lautet {name}",
|
"render": "Der Name dieses Bücherschrank lautet {name}",
|
||||||
"question": "Wie heißt dieser öffentliche Bücherschrank?",
|
"question": "Wie heißt dieser öffentliche Bücherschrank?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1081,11 +1081,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2": {
|
"3": {
|
||||||
"render": "{capacity} Bücher passen in diesen Bücherschrank",
|
"render": "{capacity} Bücher passen in diesen Bücherschrank",
|
||||||
"question": "Wie viele Bücher passen in diesen öffentlichen Bücherschrank?"
|
"question": "Wie viele Bücher passen in diesen öffentlichen Bücherschrank?"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"question": "Welche Art von Büchern sind in diesem öffentlichen Bücherschrank zu finden?",
|
"question": "Welche Art von Büchern sind in diesem öffentlichen Bücherschrank zu finden?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1099,7 +1099,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4": {
|
"5": {
|
||||||
"question": "Befindet sich dieser Bücherschrank im Freien?",
|
"question": "Befindet sich dieser Bücherschrank im Freien?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1113,7 +1113,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"5": {
|
"6": {
|
||||||
"question": "Ist dieser öffentliche Bücherschrank frei zugänglich?",
|
"question": "Ist dieser öffentliche Bücherschrank frei zugänglich?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1124,11 +1124,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"6": {
|
"7": {
|
||||||
"question": "Wer unterhält diesen öffentlichen Bücherschrank?",
|
"question": "Wer unterhält diesen öffentlichen Bücherschrank?",
|
||||||
"render": "Betrieben von {operator}"
|
"render": "Betrieben von {operator}"
|
||||||
},
|
},
|
||||||
"7": {
|
"8": {
|
||||||
"question": "Ist dieser öffentliche Bücherschrank Teil eines größeren Netzwerks?",
|
"question": "Ist dieser öffentliche Bücherschrank Teil eines größeren Netzwerks?",
|
||||||
"render": "Dieser Bücherschrank ist Teil von {brand}",
|
"render": "Dieser Bücherschrank ist Teil von {brand}",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1140,7 +1140,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8": {
|
"9": {
|
||||||
"render": "Die Referenznummer dieses öffentlichen Bücherschranks innerhalb {brand} lautet {ref}",
|
"render": "Die Referenznummer dieses öffentlichen Bücherschranks innerhalb {brand} lautet {ref}",
|
||||||
"question": "Wie lautet die Referenznummer dieses öffentlichen Bücherschranks?",
|
"question": "Wie lautet die Referenznummer dieses öffentlichen Bücherschranks?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1149,11 +1149,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"9": {
|
"10": {
|
||||||
"question": "Wann wurde dieser öffentliche Bücherschrank installiert?",
|
"question": "Wann wurde dieser öffentliche Bücherschrank installiert?",
|
||||||
"render": "Installiert am {start_date}"
|
"render": "Installiert am {start_date}"
|
||||||
},
|
},
|
||||||
"10": {
|
"11": {
|
||||||
"render": "Weitere Informationen auf <a href='{website}' target='_blank'>der Webseite</a>",
|
"render": "Weitere Informationen auf <a href='{website}' target='_blank'>der Webseite</a>",
|
||||||
"question": "Gibt es eine Website mit weiteren Informationen über diesen öffentlichen Bücherschrank?"
|
"question": "Gibt es eine Website mit weiteren Informationen über diesen öffentlichen Bücherschrank?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1183,7 +1183,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
"1": {
|
"2": {
|
||||||
"render": "The name of this bookcase is {name}",
|
"render": "The name of this bookcase is {name}",
|
||||||
"question": "What is the name of this public bookcase?",
|
"question": "What is the name of this public bookcase?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1192,11 +1192,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2": {
|
"3": {
|
||||||
"render": "{capacity} books fit in this bookcase",
|
"render": "{capacity} books fit in this bookcase",
|
||||||
"question": "How many books fit into this public bookcase?"
|
"question": "How many books fit into this public bookcase?"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"question": "What kind of books can be found in this public bookcase?",
|
"question": "What kind of books can be found in this public bookcase?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1210,7 +1210,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4": {
|
"5": {
|
||||||
"question": "Is this bookcase located outdoors?",
|
"question": "Is this bookcase located outdoors?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1224,7 +1224,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"5": {
|
"6": {
|
||||||
"question": "Is this public bookcase freely accessible?",
|
"question": "Is this public bookcase freely accessible?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1235,11 +1235,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"6": {
|
"7": {
|
||||||
"question": "Who maintains this public bookcase?",
|
"question": "Who maintains this public bookcase?",
|
||||||
"render": "Operated by {operator}"
|
"render": "Operated by {operator}"
|
||||||
},
|
},
|
||||||
"7": {
|
"8": {
|
||||||
"question": "Is this public bookcase part of a bigger network?",
|
"question": "Is this public bookcase part of a bigger network?",
|
||||||
"render": "This public bookcase is part of {brand}",
|
"render": "This public bookcase is part of {brand}",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1251,7 +1251,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8": {
|
"9": {
|
||||||
"render": "The reference number of this public bookcase within {brand} is {ref}",
|
"render": "The reference number of this public bookcase within {brand} is {ref}",
|
||||||
"question": "What is the reference number of this public bookcase?",
|
"question": "What is the reference number of this public bookcase?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1260,11 +1260,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"9": {
|
"10": {
|
||||||
"question": "When was this public bookcase installed?",
|
"question": "When was this public bookcase installed?",
|
||||||
"render": "Installed on {start_date}"
|
"render": "Installed on {start_date}"
|
||||||
},
|
},
|
||||||
"10": {
|
"11": {
|
||||||
"render": "More info on <a href='{website}' target='_blank'>the website</a>",
|
"render": "More info on <a href='{website}' target='_blank'>the website</a>",
|
||||||
"question": "Is there a website with more information about this public bookcase?"
|
"question": "Is there a website with more information about this public bookcase?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1073,7 +1073,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
"1": {
|
"2": {
|
||||||
"render": "Le nom de cette microbibliothèque est {name}",
|
"render": "Le nom de cette microbibliothèque est {name}",
|
||||||
"question": "Quel est le nom de cette microbibliothèque ?",
|
"question": "Quel est le nom de cette microbibliothèque ?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1082,11 +1082,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2": {
|
"3": {
|
||||||
"render": "{capacity} livres peuvent entrer dans cette microbibliothèque",
|
"render": "{capacity} livres peuvent entrer dans cette microbibliothèque",
|
||||||
"question": "Combien de livres peuvent entrer dans cette microbibliothèque ?"
|
"question": "Combien de livres peuvent entrer dans cette microbibliothèque ?"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"question": "Quel type de livres peut-on dans cette microbibliothèque ?",
|
"question": "Quel type de livres peut-on dans cette microbibliothèque ?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1100,7 +1100,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4": {
|
"5": {
|
||||||
"question": "Cette microbiliothèque est-elle en extérieur ?",
|
"question": "Cette microbiliothèque est-elle en extérieur ?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1114,7 +1114,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"5": {
|
"6": {
|
||||||
"question": "Cette microbibliothèque est-elle librement accèssible ?",
|
"question": "Cette microbibliothèque est-elle librement accèssible ?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1125,11 +1125,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"6": {
|
"7": {
|
||||||
"question": "Qui entretien cette microbibliothèque ?",
|
"question": "Qui entretien cette microbibliothèque ?",
|
||||||
"render": "Entretenue par {operator}"
|
"render": "Entretenue par {operator}"
|
||||||
},
|
},
|
||||||
"7": {
|
"8": {
|
||||||
"question": "Cette microbibliothèque fait-elle partie d'un réseau/groupe ?",
|
"question": "Cette microbibliothèque fait-elle partie d'un réseau/groupe ?",
|
||||||
"render": "Cette microbibliothèque fait partie du groupe {brand}",
|
"render": "Cette microbibliothèque fait partie du groupe {brand}",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1141,7 +1141,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8": {
|
"9": {
|
||||||
"render": "Cette microbibliothèque du réseau {brand} possède le numéro {ref}",
|
"render": "Cette microbibliothèque du réseau {brand} possède le numéro {ref}",
|
||||||
"question": "Quelle est le numéro de référence de cette microbibliothèque ?",
|
"question": "Quelle est le numéro de référence de cette microbibliothèque ?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1150,11 +1150,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"9": {
|
"10": {
|
||||||
"question": "Quand a été installée cette microbibliothèque ?",
|
"question": "Quand a été installée cette microbibliothèque ?",
|
||||||
"render": "Installée le {start_date}"
|
"render": "Installée le {start_date}"
|
||||||
},
|
},
|
||||||
"10": {
|
"11": {
|
||||||
"render": "Plus d'infos sur <a href='{website}' target='_blank'>le site web</a>",
|
"render": "Plus d'infos sur <a href='{website}' target='_blank'>le site web</a>",
|
||||||
"question": "Y a-t-il un site web avec plus d'informations sur cette microbibliothèque ?"
|
"question": "Y a-t-il un site web avec plus d'informations sur cette microbibliothèque ?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1361,7 +1361,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
"1": {
|
"2": {
|
||||||
"render": "De naam van dit boekenruilkastje is {name}",
|
"render": "De naam van dit boekenruilkastje is {name}",
|
||||||
"question": "Wat is de naam van dit boekenuilkastje?",
|
"question": "Wat is de naam van dit boekenuilkastje?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1370,11 +1370,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2": {
|
"3": {
|
||||||
"render": "Er passen {capacity} boeken",
|
"render": "Er passen {capacity} boeken",
|
||||||
"question": "Hoeveel boeken passen er in dit boekenruilkastje?"
|
"question": "Hoeveel boeken passen er in dit boekenruilkastje?"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"question": "Voor welke doelgroep zijn de meeste boeken in dit boekenruilkastje?",
|
"question": "Voor welke doelgroep zijn de meeste boeken in dit boekenruilkastje?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1388,7 +1388,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"4": {
|
"5": {
|
||||||
"question": "Staat dit boekenruilkastje binnen of buiten?",
|
"question": "Staat dit boekenruilkastje binnen of buiten?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1402,7 +1402,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"5": {
|
"6": {
|
||||||
"question": "Is dit boekenruilkastje publiek toegankelijk?",
|
"question": "Is dit boekenruilkastje publiek toegankelijk?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
|
@ -1413,11 +1413,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"6": {
|
"7": {
|
||||||
"question": "Wie is verantwoordelijk voor dit boekenruilkastje?",
|
"question": "Wie is verantwoordelijk voor dit boekenruilkastje?",
|
||||||
"render": "Onderhouden door {operator}"
|
"render": "Onderhouden door {operator}"
|
||||||
},
|
},
|
||||||
"7": {
|
"8": {
|
||||||
"question": "Is dit boekenruilkastje deel van een netwerk?",
|
"question": "Is dit boekenruilkastje deel van een netwerk?",
|
||||||
"render": "Dit boekenruilkastje is deel van het netwerk {brand}",
|
"render": "Dit boekenruilkastje is deel van het netwerk {brand}",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1429,7 +1429,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"8": {
|
"9": {
|
||||||
"render": "Het referentienummer binnen {brand} is {ref}",
|
"render": "Het referentienummer binnen {brand} is {ref}",
|
||||||
"question": "Wat is het referentienummer van dit boekenruilkastje?",
|
"question": "Wat is het referentienummer van dit boekenruilkastje?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -1438,11 +1438,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"9": {
|
"10": {
|
||||||
"question": "Op welke dag werd dit boekenruilkastje geinstalleerd?",
|
"question": "Op welke dag werd dit boekenruilkastje geinstalleerd?",
|
||||||
"render": "Geplaatst op {start_date}"
|
"render": "Geplaatst op {start_date}"
|
||||||
},
|
},
|
||||||
"10": {
|
"11": {
|
||||||
"render": "Meer info op <a href='{website}' target='_blank'>de website</a>",
|
"render": "Meer info op <a href='{website}' target='_blank'>de website</a>",
|
||||||
"question": "Is er een website over dit boekenruilkastje?"
|
"question": "Is er een website over dit boekenruilkastje?"
|
||||||
}
|
}
|
||||||
|
|
|
@ -578,7 +578,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tagRenderings": {
|
"tagRenderings": {
|
||||||
"1": {
|
"2": {
|
||||||
"render": "Название книжного шкафа — {name}",
|
"render": "Название книжного шкафа — {name}",
|
||||||
"question": "Как называется общественный книжный шкаф?",
|
"question": "Как называется общественный книжный шкаф?",
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -587,10 +587,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"2": {
|
"3": {
|
||||||
"question": "Сколько книг помещается в этом общественном книжном шкафу?"
|
"question": "Сколько книг помещается в этом общественном книжном шкафу?"
|
||||||
},
|
},
|
||||||
"3": {
|
"4": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
"then": "В основном детские книги"
|
"then": "В основном детские книги"
|
||||||
|
@ -600,7 +600,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"10": {
|
"11": {
|
||||||
"render": "Более подробная информация <a href='{website}' target='_blank'>на сайте</a>"
|
"render": "Более подробная информация <a href='{website}' target='_blank'>на сайте</a>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"campersite": {
|
"campersite": {
|
||||||
"title": "Kampeersite"
|
"title": "Kampeersite",
|
||||||
|
"shortDescription": "Vind locaties waar je de nacht kan doorbrengen met je mobilehome"
|
||||||
},
|
},
|
||||||
"climbing": {
|
"climbing": {
|
||||||
"title": "Open Klimkaart",
|
"title": "Open Klimkaart",
|
||||||
|
@ -368,10 +369,38 @@
|
||||||
"title": {
|
"title": {
|
||||||
"render": "Klimgelegenheid?"
|
"render": "Klimgelegenheid?"
|
||||||
},
|
},
|
||||||
"description": "Een klimgelegenheid?"
|
"description": "Een klimgelegenheid?",
|
||||||
|
"tagRenderings": {
|
||||||
|
"1": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": "Klimmen is hier niet mogelijk"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"then": "Klimmen is hier niet toegelaten"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"then": "Klimmen is hier niet toegelaten"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"roamingRenderings": {
|
"roamingRenderings": {
|
||||||
|
"0": {
|
||||||
|
"question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"mappings": {
|
||||||
|
"0": {
|
||||||
|
"then": "<span class='subtle'>Een <a href='#{_embedding_feature:id}'>omvattend element</a> geeft aan dat dit <span>publiek toegangkelijk is</span><br/>{_embedding_feature:access:description}"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"then": "<span class='subtle'>Een <a href='#{_embedding_feature:id}'>omvattend element</a> geeft aan dat</span> een toelating nodig is om hier te klimmen<br/>{_embedding_feature:access:description}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"render": "De klimroutes zijn gemiddeld <b>{climbing:length}m</b> lang",
|
"render": "De klimroutes zijn gemiddeld <b>{climbing:length}m</b> lang",
|
||||||
"question": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?"
|
"question": "Wat is de (gemiddelde) lengte van de klimroutes, in meter?"
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
"reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json",
|
"reset:layeroverview": "echo {\\\"layers\\\":[], \\\"themes\\\":[]} > ./assets/generated/known_layers_and_themes.json",
|
||||||
"generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:licenses && npm run generate:licenses && npm run generate:layeroverview",
|
"generate": "mkdir -p ./assets/generated && npm run reset:layeroverview && npm run generate:images && npm run generate:translations && npm run generate:licenses && npm run generate:licenses && npm run generate:layeroverview",
|
||||||
"build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*",
|
"build": "rm -rf dist/ && npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*",
|
||||||
"prepare-deploy": "npm run generate:contributor-list && npm run generate && npm run test && npm run generate:editor-layer-index && npm run generate:layeroverview && npm run generate:layouts && npm run build && rm -rf .cache && npm run generate:docs",
|
"prepare-deploy": "npm run generate && npm run test && npm run generate:editor-layer-index && npm run generate:layeroverview && npm run generate:layouts && npm run build && rm -rf .cache && npm run generate:docs",
|
||||||
"deploy:staging": "npm run prepare-deploy && rm -rf ~/git/pietervdvn.github.io/Staging/* && cp -r dist/* ~/git/pietervdvn.github.io/Staging/ && cd ~/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
"deploy:staging": "npm run prepare-deploy && rm -rf ~/git/pietervdvn.github.io/Staging/* && cp -r dist/* ~/git/pietervdvn.github.io/Staging/ && cd ~/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
||||||
"deploy:pietervdvn": "cd ~/git/pietervdvn.github.io/ && git pull && cd - && npm run prepare-deploy && rm -rf ~/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* ~/git/pietervdvn.github.io/MapComplete/ && cd ~/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
"deploy:pietervdvn": "cd ~/git/pietervdvn.github.io/ && git pull && cd - && npm run prepare-deploy && rm -rf ~/git/pietervdvn.github.io/MapComplete/* && cp -r dist/* ~/git/pietervdvn.github.io/MapComplete/ && cd ~/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
|
||||||
"deploy:production": "cd ~/git/mapcomplete.github.io/ && git pull && cd - && rm -rf ./assets/generated && npm run prepare-deploy && npm run optimize-images && rm -rf ~/git/mapcomplete.github.io/* && cp -r dist/* ~/git/mapcomplete.github.io/ && cd ~/git/mapcomplete.github.io/ && echo \"mapcomplete.osm.be\" > CNAME && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean && npm run gittag",
|
"deploy:production": "cd ~/git/mapcomplete.github.io/ && git pull && cd - && rm -rf ./assets/generated && npm run prepare-deploy && npm run optimize-images && rm -rf ~/git/mapcomplete.github.io/* && cp -r dist/* ~/git/mapcomplete.github.io/ && cd ~/git/mapcomplete.github.io/ && echo \"mapcomplete.osm.be\" > CNAME && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean && npm run gittag",
|
||||||
|
|
|
@ -3,7 +3,6 @@ import Combine from "./UI/Base/Combine";
|
||||||
import {Button} from "./UI/Base/Button";
|
import {Button} from "./UI/Base/Button";
|
||||||
import {TextField} from "./UI/Input/TextField";
|
import {TextField} from "./UI/Input/TextField";
|
||||||
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
||||||
import {UIElement} from "./UI/UIElement";
|
|
||||||
import {UIEventSource} from "./Logic/UIEventSource";
|
import {UIEventSource} from "./Logic/UIEventSource";
|
||||||
import {Utils} from "./Utils";
|
import {Utils} from "./Utils";
|
||||||
import {SubtleButton} from "./UI/Base/SubtleButton";
|
import {SubtleButton} from "./UI/Base/SubtleButton";
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
import {lstatSync, readdirSync, readFileSync} from "fs";
|
import {lstatSync, readdirSync, readFileSync} from "fs";
|
||||||
|
import {Utils} from "../Utils";
|
||||||
|
Utils.runningFromConsole = true
|
||||||
import * as https from "https";
|
import * as https from "https";
|
||||||
import {LayerConfigJson} from "../Customizations/JSON/LayerConfigJson";
|
import {LayerConfigJson} from "../Customizations/JSON/LayerConfigJson";
|
||||||
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
|
||||||
export default class ScriptUtils {
|
export default class ScriptUtils {
|
||||||
|
|
||||||
|
|
||||||
|
public static fixUtils() {
|
||||||
|
Utils.externalDownloadFunction = ScriptUtils.DownloadJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static readDirRecSync(path, maxDepth = 999): string[] {
|
public static readDirRecSync(path, maxDepth = 999): string[] {
|
||||||
const result = []
|
const result = []
|
||||||
if(maxDepth <= 0){
|
if (maxDepth <= 0) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
for (const entry of readdirSync(path)) {
|
for (const entry of readdirSync(path)) {
|
||||||
|
@ -23,6 +34,20 @@ export default class ScriptUtils {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DownloadFileTo(url, targetFilePath: string): void {
|
||||||
|
console.log("Downloading ", url, "to", targetFilePath)
|
||||||
|
https.get(url, (res) => {
|
||||||
|
const filePath = fs.createWriteStream(targetFilePath);
|
||||||
|
res.pipe(filePath);
|
||||||
|
filePath.on('finish', () => {
|
||||||
|
filePath.close();
|
||||||
|
console.log('Download Completed');
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static DownloadJSON(url): Promise<any> {
|
public static DownloadJSON(url): Promise<any> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
@ -77,7 +102,7 @@ export default class ScriptUtils {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getThemeFiles() : {parsed: LayoutConfigJson, path: string}[] {
|
public static getThemeFiles(): { parsed: LayoutConfigJson, path: string }[] {
|
||||||
return ScriptUtils.readDirRecSync("./assets/themes")
|
return ScriptUtils.readDirRecSync("./assets/themes")
|
||||||
.filter(path => path.endsWith(".json"))
|
.filter(path => path.endsWith(".json"))
|
||||||
.filter(path => path.indexOf("license_info.json") < 0)
|
.filter(path => path.indexOf("license_info.json") < 0)
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This script attempt to automatically fix some basic issues when a theme from the custom generator is loaded
|
* This script attempt to automatically fix some basic issues when a theme from the custom generator is loaded
|
||||||
*/
|
*/
|
||||||
import {Utils} from "../Utils"
|
import {Utils} from "../Utils"
|
||||||
Utils.runningFromConsole = true;
|
Utils.runningFromConsole = true;
|
||||||
|
|
||||||
import {readFileSync, writeFileSync} from "fs";
|
import {readFileSync, writeFileSync} from "fs";
|
||||||
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
||||||
import {Layer} from "leaflet";
|
|
||||||
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||||
import SmallLicense from "../Models/smallLicense";
|
import SmallLicense from "../Models/smallLicense";
|
||||||
import AllKnownLayers from "../Customizations/AllKnownLayers";
|
import AllKnownLayers from "../Customizations/AllKnownLayers";
|
||||||
|
import ScriptUtils from "./ScriptUtils";
|
||||||
|
import AllImageProviders from "../Logic/ImageProviders/AllImageProviders";
|
||||||
|
|
||||||
|
|
||||||
|
ScriptUtils.fixUtils()
|
||||||
|
|
||||||
if(process.argv.length == 2){
|
if(process.argv.length == 2){
|
||||||
console.log("USAGE: ts-node scripts/fixTheme <path to theme>")
|
console.log("USAGE: ts-node scripts/fixTheme <path to theme>")
|
||||||
|
@ -23,7 +27,6 @@ console.log("Fixing up ", path)
|
||||||
|
|
||||||
const themeConfigJson : LayoutConfigJson = JSON.parse(readFileSync(path, "UTF8"))
|
const themeConfigJson : LayoutConfigJson = JSON.parse(readFileSync(path, "UTF8"))
|
||||||
|
|
||||||
const linuxHints = []
|
|
||||||
const licenses : SmallLicense[] = []
|
const licenses : SmallLicense[] = []
|
||||||
|
|
||||||
const replacements: {source: string, destination: string}[] = []
|
const replacements: {source: string, destination: string}[] = []
|
||||||
|
@ -41,15 +44,32 @@ for (const layerConfigJson of themeConfigJson.layers) {
|
||||||
const layerConfig = new LayerConfig(layerConfigJson, AllKnownLayers.sharedUnits, "fix theme",true)
|
const layerConfig = new LayerConfig(layerConfigJson, AllKnownLayers.sharedUnits, "fix theme",true)
|
||||||
const images : string[] = Array.from(layerConfig.ExtractImages())
|
const images : string[] = Array.from(layerConfig.ExtractImages())
|
||||||
const remoteImages = images.filter(img => img.startsWith("http"))
|
const remoteImages = images.filter(img => img.startsWith("http"))
|
||||||
|
|
||||||
for (const remoteImage of remoteImages) {
|
for (const remoteImage of remoteImages) {
|
||||||
linuxHints.push("wget " + remoteImage)
|
|
||||||
|
|
||||||
|
const filename = remoteImage.substring(remoteImage.lastIndexOf("/"))
|
||||||
|
ScriptUtils.DownloadFileTo(remoteImage, dir + "/" + filename)
|
||||||
|
|
||||||
|
|
||||||
const imgPath = remoteImage.substring(remoteImage.lastIndexOf("/") + 1)
|
const imgPath = remoteImage.substring(remoteImage.lastIndexOf("/") + 1)
|
||||||
licenses.push({
|
|
||||||
path: imgPath,
|
for (const attributionSrc of AllImageProviders.ImageAttributionSource) {
|
||||||
license: "",
|
try {
|
||||||
authors: [],
|
attributionSrc.GetAttributionFor(remoteImage).addCallbackAndRun(license => {
|
||||||
sources: [remoteImage]
|
console.log("Downloaded an attribution!")
|
||||||
})
|
licenses.push({
|
||||||
|
path: imgPath,
|
||||||
|
license: license?.license ?? "",
|
||||||
|
authors:Utils.NoNull([license?.artist]),
|
||||||
|
sources: [remoteImage]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}catch(e){
|
||||||
|
// Hush hush
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
replacements.push({source: remoteImage, destination: `${dir}/${imgPath}`})
|
replacements.push({source: remoteImage, destination: `${dir}/${imgPath}`})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,13 +79,9 @@ for (const replacement of replacements) {
|
||||||
fixedThemeJson = fixedThemeJson.replace(new RegExp(replacement.source, "g"), replacement.destination)
|
fixedThemeJson = fixedThemeJson.replace(new RegExp(replacement.source, "g"), replacement.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixScriptPath = dir + "/fix_script_"+path.replace(/\//g,"_")+".sh"
|
|
||||||
writeFileSync(dir + "/generated.license_info.json", JSON.stringify(licenses, null, " "))
|
writeFileSync(dir + "/generated.license_info.json", JSON.stringify(licenses, null, " "))
|
||||||
writeFileSync(fixScriptPath, linuxHints.join("\n"))
|
|
||||||
writeFileSync(path+".autofixed.json", fixedThemeJson)
|
writeFileSync(path+".autofixed.json", fixedThemeJson)
|
||||||
|
|
||||||
console.log(`IMPORTANT:
|
console.log(`IMPORTANT:
|
||||||
1) run ${fixScriptPath}
|
1) Copy generated.license_info.json over into license_info.json and add the missing attributions and authors
|
||||||
2) Copy generated.license_info.json over into license_info.json and add the missing attributions and authors
|
2) Verify ${path}.autofixed.json as theme, and rename it to ${path}`)
|
||||||
3) Verify ${path}.autofixed.json as theme, and rename it to ${path}
|
|
||||||
4) Delete the fix script and other unneeded files`)
|
|
|
@ -1,8 +1,5 @@
|
||||||
import ScriptUtils from "./ScriptUtils";
|
import ScriptUtils from "./ScriptUtils";
|
||||||
import {Utils} from "../Utils";
|
import {writeFileSync} from "fs";
|
||||||
import {readFileSync, writeFileSync} from "fs";
|
|
||||||
|
|
||||||
Utils.runningFromConsole = true
|
|
||||||
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||||
import * as licenses from "../assets/generated/license_info.json"
|
import * as licenses from "../assets/generated/license_info.json"
|
||||||
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||||
|
@ -10,6 +7,7 @@ import {LayerConfigJson} from "../Customizations/JSON/LayerConfigJson";
|
||||||
import {Translation} from "../UI/i18n/Translation";
|
import {Translation} from "../UI/i18n/Translation";
|
||||||
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
import {LayoutConfigJson} from "../Customizations/JSON/LayoutConfigJson";
|
||||||
import AllKnownLayers from "../Customizations/AllKnownLayers";
|
import AllKnownLayers from "../Customizations/AllKnownLayers";
|
||||||
|
|
||||||
// This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files.
|
// This scripts scans 'assets/layers/*.json' for layer definition files and 'assets/themes/*.json' for theme definition files.
|
||||||
// It spits out an overview of those to be used to load them
|
// It spits out an overview of those to be used to load them
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Small tests</title>
|
<title>Small tests</title>
|
||||||
<link href="index.css" rel="stylesheet"/>
|
<link href="index.css" rel="stylesheet"/>
|
||||||
|
<link rel="stylesheet" href="./vendor/leaflet.css"/>
|
||||||
<link href="css/tabbedComponent.css" rel="stylesheet"/>
|
<link href="css/tabbedComponent.css" rel="stylesheet"/>
|
||||||
<link href="css/openinghourstable.css" rel="stylesheet"/>
|
<link href="css/openinghourstable.css" rel="stylesheet"/>
|
||||||
<link href="css/tagrendering.css" rel="stylesheet"/>
|
<link href="css/tagrendering.css" rel="stylesheet"/>
|
||||||
|
|
101
test.ts
101
test.ts
|
@ -9,12 +9,16 @@ import {SlideShow} from "./UI/Image/SlideShow";
|
||||||
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
||||||
import Img from "./UI/Base/Img";
|
import Img from "./UI/Base/Img";
|
||||||
import {AttributedImage} from "./UI/Image/AttributedImage";
|
import {AttributedImage} from "./UI/Image/AttributedImage";
|
||||||
import {Imgur} from "./Logic/Web/Imgur";
|
import {Imgur} from "./Logic/ImageProviders/Imgur";
|
||||||
import ReviewForm from "./UI/Reviews/ReviewForm";
|
import Minimap from "./UI/Base/Minimap";
|
||||||
import {OsmConnection} from "./Logic/Osm/OsmConnection";
|
import Loc from "./Models/Loc";
|
||||||
|
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
|
||||||
|
import ShowDataLayer from "./UI/ShowDataLayer";
|
||||||
|
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
|
||||||
|
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
||||||
|
|
||||||
|
|
||||||
function TestSlideshow(){
|
function TestSlideshow() {
|
||||||
const elems = new UIEventSource([
|
const elems = new UIEventSource([
|
||||||
new FixedUiElement("A"),
|
new FixedUiElement("A"),
|
||||||
new FixedUiElement("qmsldkfjqmlsdkjfmqlskdjfmqlksdf").SetClass("text-xl"),
|
new FixedUiElement("qmsldkfjqmlsdkjfmqlskdjfmqlksdf").SetClass("text-xl"),
|
||||||
|
@ -25,17 +29,17 @@ function TestSlideshow(){
|
||||||
new SlideShow(elems).AttachTo("maindiv")
|
new SlideShow(elems).AttachTo("maindiv")
|
||||||
}
|
}
|
||||||
|
|
||||||
function TestTagRendering(){
|
function TestTagRendering() {
|
||||||
State.state = new State(undefined)
|
State.state = new State(undefined)
|
||||||
const tagsSource = new UIEventSource({
|
const tagsSource = new UIEventSource({
|
||||||
id:"node/1"
|
id: "node/1"
|
||||||
})
|
})
|
||||||
new TagRenderingQuestion(
|
new TagRenderingQuestion(
|
||||||
tagsSource,
|
tagsSource,
|
||||||
new TagRenderingConfig({
|
new TagRenderingConfig({
|
||||||
multiAnswer: false,
|
multiAnswer: false,
|
||||||
freeform: {
|
freeform: {
|
||||||
key:"valve"
|
key: "valve"
|
||||||
},
|
},
|
||||||
question: "What valves are supported?",
|
question: "What valves are supported?",
|
||||||
render: "This pump supports {valve}",
|
render: "This pump supports {valve}",
|
||||||
|
@ -45,8 +49,8 @@ function TestTagRendering(){
|
||||||
then: "This pump supports dunlop"
|
then: "This pump supports dunlop"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
if:"valve=shrader",
|
if: "valve=shrader",
|
||||||
then:"shrader is supported",
|
then: "shrader is supported",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -56,15 +60,84 @@ function TestTagRendering(){
|
||||||
new VariableUiElement(tagsSource.map(tags => tags["valves"])).SetClass("alert").AttachTo("extradiv")
|
new VariableUiElement(tagsSource.map(tags => tags["valves"])).SetClass("alert").AttachTo("extradiv")
|
||||||
}
|
}
|
||||||
|
|
||||||
function TestAllInputMethods(){
|
function TestAllInputMethods() {
|
||||||
|
|
||||||
new Combine(ValidatedTextField.tpList.map(tp => {
|
new Combine(ValidatedTextField.tpList.map(tp => {
|
||||||
const tf = ValidatedTextField.InputForType(tp.name);
|
const tf = ValidatedTextField.InputForType(tp.name);
|
||||||
|
|
||||||
return new Combine([tf, new VariableUiElement(tf.GetValue()).SetClass("alert")]);
|
return new Combine([tf, new VariableUiElement(tf.GetValue()).SetClass("alert")]);
|
||||||
})).AttachTo("maindiv")
|
})).AttachTo("maindiv")
|
||||||
}
|
}
|
||||||
|
|
||||||
new ReviewForm(() => {
|
|
||||||
return undefined;
|
const location = new UIEventSource<Loc>({
|
||||||
}, new OsmConnection(true, new UIEventSource<string>(undefined), "test")).AttachTo("maindiv");
|
lon: 4.84771728515625,
|
||||||
|
lat: 51.17920846421931,
|
||||||
|
zoom: 14
|
||||||
|
})
|
||||||
|
const map0 = new Minimap({
|
||||||
|
location: location,
|
||||||
|
allowMoving: true,
|
||||||
|
background: new AvailableBaseLayers(location).availableEditorLayers.map(layers => layers[2])
|
||||||
|
})
|
||||||
|
map0.SetStyle("width: 500px; height: 250px; overflow: hidden; border: 2px solid red")
|
||||||
|
.AttachTo("maindiv")
|
||||||
|
|
||||||
|
const layout = AllKnownLayouts.layoutsList[1]
|
||||||
|
State.state = new State(layout)
|
||||||
|
console.log("LAYOUT is", layout.id)
|
||||||
|
|
||||||
|
const feature = {
|
||||||
|
"type": "Feature",
|
||||||
|
_matching_layer_id: "bike_repair_station",
|
||||||
|
"properties": {
|
||||||
|
id: "node/-1",
|
||||||
|
"amenity": "bicycle_repair_station"
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [
|
||||||
|
4.84771728515625,
|
||||||
|
51.17920846421931
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
State.state.allElements.addOrGetElement(feature)
|
||||||
|
|
||||||
|
const featureSource = new UIEventSource([{
|
||||||
|
freshness: new Date(),
|
||||||
|
feature: feature
|
||||||
|
}])
|
||||||
|
|
||||||
|
new ShowDataLayer(
|
||||||
|
featureSource,
|
||||||
|
map0.leafletMap,
|
||||||
|
new UIEventSource<LayoutConfig>(layout)
|
||||||
|
)
|
||||||
|
|
||||||
|
const map1 = new Minimap({
|
||||||
|
location: location,
|
||||||
|
allowMoving: true,
|
||||||
|
background: new AvailableBaseLayers(location).availableEditorLayers.map(layers => layers[5])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
map1.SetStyle("width: 500px; height: 250px; overflow: hidden; border : 2px solid black")
|
||||||
|
.AttachTo("extradiv")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new ShowDataLayer(
|
||||||
|
featureSource,
|
||||||
|
map1.leafletMap,
|
||||||
|
new UIEventSource<LayoutConfig>(layout)
|
||||||
|
)
|
||||||
|
|
||||||
|
featureSource.ping()
|
||||||
|
|
||||||
|
// */
|
Loading…
Add table
Add a link
Reference in a new issue