Merge develop

This commit is contained in:
pietervdvn 2021-07-29 10:39:16 +02:00
commit 1571fa478b
42 changed files with 927 additions and 461 deletions

View file

@ -43,6 +43,8 @@ export default class LayoutConfig {
public readonly enableBackgroundLayerSelection: boolean;
public readonly enableShowAllQuestions: boolean;
public readonly enableExportButton: boolean;
public readonly enablePdfDownload: boolean;
public readonly customCss?: string;
/*
How long is the cache valid, in seconds?
@ -153,7 +155,8 @@ export default class LayoutConfig {
this.enableAddNewPoints = json.enableAddNewPoints ?? true;
this.enableBackgroundLayerSelection = json.enableBackgroundLayerSelection ?? true;
this.enableShowAllQuestions = json.enableShowAllQuestions ?? false;
this.enableExportButton = json.enableExportButton ?? false;
this.enableExportButton = json.enableDownload ?? false;
this.enablePdfDownload = json.enablePdfDownload ?? false;
this.customCss = json.customCss;
this.cacheTimeout = json.cacheTimout ?? (60 * 24 * 60 * 60)
@ -176,7 +179,7 @@ export default class LayoutConfig {
return
}
} else {
console.log("Layer ", layer," not kown, try one of", Array.from(AllKnownLayers.sharedLayers.keys()).join(", "))
console.log("Layer ", layer, " not kown, try one of", Array.from(AllKnownLayers.sharedLayers.keys()).join(", "))
throw `Unknown builtin layer ${layer} at ${context}.layers[${i}]`;
}
}

View file

@ -336,5 +336,7 @@ export interface LayoutConfigJson {
enableGeolocation?: boolean;
enableBackgroundLayerSelection?: boolean;
enableShowAllQuestions?: boolean;
enableExportButton?: boolean;
enableDownload?: boolean;
enablePdfDownload?: boolean;
}

View file

@ -87,6 +87,10 @@ export default class TagRenderingConfig {
if (this.freeform.key === undefined || this.freeform.key === "") {
throw `Freeform.key is undefined or the empty string - this is not allowed; either fill out something or remove the freeform block alltogether. Error in ${context}`
}
if(json.freeform["args"] !== undefined){
throw `Freeform.args is defined. This should probably be 'freeform.helperArgs' (at ${context})`
}
if (ValidatedTextField.AllTypes[this.freeform.type] === undefined) {

View file

@ -10,7 +10,6 @@ import SimpleAddUI from "./UI/BigComponents/SimpleAddUI";
import CenterMessageBox from "./UI/CenterMessageBox";
import UserBadge from "./UI/BigComponents/UserBadge";
import SearchAndGo from "./UI/BigComponents/SearchAndGo";
import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {Utils} from "./Utils";
import Svg from "./Svg";
@ -23,25 +22,22 @@ import UserDetails from "./Logic/Osm/OsmConnection";
import Attribution from "./UI/BigComponents/Attribution";
import LayerResetter from "./Logic/Actors/LayerResetter";
import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
import ShowDataLayer from "./UI/ShowDataLayer";
import Hash from "./Logic/Web/Hash";
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen";
import Translations from "./UI/i18n/Translations";
import MapControlButton from "./UI/MapControlButton";
import Combine from "./UI/Base/Combine";
import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
import LZString from "lz-string";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
import AttributionPanel from "./UI/BigComponents/AttributionPanel";
import ContributorCount from "./Logic/ContributorCount";
import FeatureSource from "./Logic/FeatureSource/FeatureSource";
import AllKnownLayers from "./Customizations/AllKnownLayers";
import LayerConfig from "./Customizations/JSON/LayerConfig";
import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers";
import {TagsFilter} from "./Logic/Tags/TagsFilter";
import FilterView from "./UI/BigComponents/FilterView";
import LeftControls from "./UI/BigComponents/LeftControls";
import RightControls from "./UI/BigComponents/RightControls";
export class InitUiElements {
static InitAll(
@ -189,38 +185,10 @@ export class InitUiElements {
marker.addTo(State.state.leafletMap.data);
});
const geolocationButton = new Toggle(
new MapControlButton(
new GeoLocationHandler(
State.state.currentGPSLocation,
State.state.leafletMap,
State.state.layoutToUse
),
{
dontStyle: true,
}
),
undefined,
State.state.featureSwitchGeolocation
);
const plus = new MapControlButton(Svg.plus_zoom_svg()).onClick(() => {
State.state.locationControl.data.zoom++;
State.state.locationControl.ping();
});
const min = new MapControlButton(Svg.min_zoom_svg()).onClick(() => {
State.state.locationControl.data.zoom--;
State.state.locationControl.ping();
});
new Combine([plus, min, geolocationButton])
.SetClass("flex flex-col")
.AttachTo("bottom-right");
if (layoutToUse.id === personal.id) {
updateFavs();
}
InitUiElements.setupAllLayerElements();
if (layoutToUse.id === personal.id) {
@ -328,82 +296,7 @@ export class InitUiElements {
Hash.hash.data == "welcome"
);
}
private static InitLayerSelection(featureSource: FeatureSource) {
const copyrightNotice = new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle.Clone(),
() =>
new AttributionPanel(
State.state.layoutToUse,
new ContributorCount(featureSource).Contributors
),
"copyright"
);
const copyrightButton = new Toggle(
copyrightNotice,
new MapControlButton(Svg.copyright_svg()),
copyrightNotice.isShown
)
.ToggleOnClick()
.SetClass("p-0.5");
const layerControlPanel = new LayerControlPanel(
State.state.layerControlIsOpened
).SetClass("block p-1 rounded-full");
const layerControlButton = new Toggle(
layerControlPanel,
new MapControlButton(Svg.layers_svg()),
State.state.layerControlIsOpened
).ToggleOnClick();
const layerControl = new Toggle(
layerControlButton,
"",
State.state.featureSwitchLayers
);
const filterView = new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(State.state.filteredLayers).SetClass(
"block p-1 rounded-full"
),
undefined,
State.state.filterIsOpened
);
const filterMapControlButton = new MapControlButton(Svg.filter_svg());
const filterButton = new Toggle(
filterView,
filterMapControlButton,
State.state.filterIsOpened
).ToggleOnClick();
const filterControl = new Toggle(
filterButton,
undefined,
State.state.featureSwitchFilter
);
new Combine([copyrightButton, layerControl, filterControl])
.SetClass("flex flex-col")
.AttachTo("bottom-left");
State.state.locationControl.addCallback(() => {
// Close the layer selection when the map is moved
layerControlButton.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
});
State.state.selectedElement.addCallbackAndRunD((_) => {
layerControlButton.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
});
}
private static InitBaseMap() {
State.state.availableBackgroundLayers =
AvailableBaseLayers.AvailableLayersAt(State.state.locationControl);
@ -537,7 +430,9 @@ export class InitUiElements {
// ------------- Setup the layers -------------------------------
const source = InitUiElements.InitLayers();
InitUiElements.InitLayerSelection(source);
new LeftControls(source).AttachTo("bottom-left");
new RightControls().AttachTo("bottom-right");
// ------------------ Setup various other UI elements ------------

View file

@ -1,37 +0,0 @@
/**
* Creates screenshoter to take png screenshot
* Creates jspdf and downloads it
* - landscape pdf
*
* To add new layout:
* - add new possible layout name in constructor
* - add new layout in "PDFLayout"
* -> in there are more instructions
*/
import jsPDF from "jspdf";
import { SimpleMapScreenshoter } from "leaflet-simple-map-screenshoter";
import State from "../../State";
import Minimap from "../../UI/Base/Minimap";
import { PDFLayout } from "./PDFLayout";
export default class ExportPDF {
constructor(
name: string,
layout: "natuurpunt"
){
const screenshotter = new SimpleMapScreenshoter();
//let temporaryMap = new Minimap();
//temporaryMap.SetStyle('visibility: hidden');
//temporaryMap.AttachTo("tempScreenshotDiv");
//minimap op index.html -> hidden daar alles op doen en dan weg
//minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline
screenshotter.addTo(State.state.leafletMap.data);
let doc = new jsPDF('l');
screenshotter.takeScreen('image').then(image => {
let file = new PDFLayout();
file.AddLayout(layout, doc, image);
doc.save(name);
})
}
}

View file

@ -1,20 +0,0 @@
/**
* Adds a theme to the pdf
*/
import jsPDF from "jspdf";
export class PDFLayout {
public AddLayout(layout: string, doc: jsPDF, image: Blob){
if(layout === "natuurpunt") this.AddNatuurpuntLayout(doc, image);
}
public AddNatuurpuntLayout(doc: jsPDF, image: Blob){
// Add Natuurpunt layout
const screenRatio = screen.width/screen.height;
let img = document.createElement('img');
img.src = './assets/themes/natuurpunt/natuurpunt.png';
doc.addImage(img, 'PNG', 15, 5, 20, 20);
doc.addImage(image, 'PNG', 15, 30, 150*screenRatio, 150);
return doc;
}
}

View file

@ -24,13 +24,14 @@ export class FeatureSourceUtils {
options = Utils.setDefaults(options, defaults);
// Select all features, ignore the freshness and other data
let featureList: any[] = featurePipeline.features.data.map((feature) => feature.feature);
let featureList: any[] = featurePipeline.features.data.map((feature) =>
JSON.parse(JSON.stringify((feature.feature)))); // Make a deep copy!
if (!options.metadata) {
for (let i = 0; i < featureList.length; i++) {
let feature = featureList[i];
for (let property in feature.properties) {
if (property[0] == "_") {
if (property[0] == "_" && property !== "_lat" && property !== "_lon") {
delete featureList[i]["properties"][property];
}
}

View file

@ -322,7 +322,7 @@ export class GeoOperations {
if (value === undefined) {
line += ","
} else {
line += JSON.stringify(value)+","
line += JSON.stringify(value) + ","
}
}
lines.push(line)
@ -334,7 +334,7 @@ export class GeoOperations {
}
class BBox {
export class BBox {
readonly maxLat: number;
readonly maxLon: number;
@ -357,33 +357,20 @@ class BBox {
this.check();
}
static fromLeafletBounds(bounds) {
return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]])
}
static get(feature) {
if (feature.bbox?.overlapsWith === undefined) {
if (feature.geometry.type === "MultiPolygon") {
let coordinates = [];
for (const coorlist of feature.geometry.coordinates) {
coordinates = coordinates.concat(coorlist[0]);
}
feature.bbox = new BBox(coordinates);
} else if (feature.geometry.type === "Polygon") {
feature.bbox = new BBox(feature.geometry.coordinates[0]);
} else if (feature.geometry.type === "LineString") {
feature.bbox = new BBox(feature.geometry.coordinates);
} else if (feature.geometry.type === "Point") {
// Point
feature.bbox = new BBox([feature.geometry.coordinates]);
} else {
throw "Cannot calculate bbox, unknown type " + feature.geometry.type;
}
const turfBbox: number[] = turf.bbox(feature)
feature.bbox = new BBox([[turfBbox[0], turfBbox[1]],[turfBbox[2], turfBbox[3]]]);
}
return feature.bbox;
}
public overlapsWith(other: BBox) {
this.check();
other.check();
if (this.maxLon < other.minLon) {
return false;
}
@ -397,6 +384,22 @@ class BBox {
}
public isContainedIn(other: BBox) {
if (this.maxLon > other.maxLon) {
return false;
}
if (this.maxLat > other.maxLat) {
return false;
}
if (this.minLon < other.minLon) {
return false;
}
if (this.minLat < other.minLat) {
return false
}
return true;
}
private check() {
if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) {
console.log(this);

View file

@ -27,7 +27,9 @@ export class Changes {
private readonly previouslyCreated : OsmObject[] = []
constructor() {
this.isUploading.addCallbackAndRun(uploading => {
console.trace("Is uploading changed:", uploading)
})
}
private static createChangesetFor(csId: string,
@ -255,6 +257,9 @@ export class Changes {
console.log("Needed ids", neededIds)
OsmObject.DownloadAll(neededIds, true).addCallbackAndRunD(osmObjects => {
console.log("Got the fresh objects!", osmObjects, "pending: ", pending)
try{
const changes: {
newObjects: OsmObject[],
modifiedObjects: OsmObject[]
@ -283,6 +288,11 @@ export class Changes {
return self.isUploading.setData(false);
} // Failed - mark to try again
)
}catch(e){
console.error("Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", e)
self.pendingChanges.setData([])
self.isUploading.setData(false)
}
return true;
});

View file

@ -437,6 +437,11 @@ export class OsmWay extends OsmObject {
for (const nodeId of element.nodes) {
const node = nodeDict.get(nodeId)
if(node === undefined){
console.error("Error: node ", nodeId, "not found in ", nodeDict)
// This is probably part of a relation which hasn't been fully downloaded
continue;
}
const cp = node.centerpoint();
this.coordinates.push(cp);
latSum = cp[0]

View file

@ -305,7 +305,7 @@ export default class SimpleMetaTagger {
} else if (_otherParkingMode.matchesProperties(properties)) {
parallelParkingCount = 0;
} else {
console.log("No parking data for ", properties.name, properties.id, properties)
console.log("No parking data for ", properties.name, properties.id)
}

View file

@ -86,7 +86,6 @@ The core strings and builtin themes of MapComplete are translated on [Hosted Web
You can easily make an account and start translating in their web-environment - no installation required.
[![Translation status](https://hosted.weblate.org/widgets/mapcomplete/-/multi-blue.svg)](https://hosted.weblate.org/engage/mapcomplete/)
[![Translation status](https://hosted.weblate.org/widgets/mapcomplete/-/multi-blue.svg)](https://hosted.weblate.org/engage/mapcomplete/)
## Architecture

View file

@ -61,7 +61,7 @@ export default class State {
public osmApiFeatureSource: OsmApiFeatureSource;
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([],"filteredLayers");
public filteredLayers: UIEventSource<FilteredLayer[]> = new UIEventSource<FilteredLayer[]>([], "filteredLayers");
/**
The latest element that was selected
@ -79,7 +79,7 @@ export default class State {
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
public readonly featureSwitchSearch: UIEventSource<boolean>;
public readonly featureSwitchLayers: UIEventSource<boolean>;
public readonly featureSwitchBackgroundSlection: UIEventSource<boolean>;
public readonly featureSwitchAddNew: UIEventSource<boolean>;
public readonly featureSwitchWelcomeMessage: UIEventSource<boolean>;
public readonly featureSwitchIframe: UIEventSource<boolean>;
@ -93,7 +93,7 @@ export default class State {
public readonly featureSwitchFilter: UIEventSource<boolean>;
public readonly featureSwitchEnableExport: UIEventSource<boolean>;
public readonly featureSwitchFakeUser: UIEventSource<boolean>;
public readonly featureSwitchExportAsPdf: UIEventSource<boolean>;
public featurePipeline: FeaturePipeline;
@ -125,11 +125,11 @@ export default class State {
public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
public layerControlIsOpened: UIEventSource<boolean> =
public downloadControlIsOpened: UIEventSource<boolean> =
QueryParameters.GetQueryParameter(
"layer-control-toggle",
"download-control-toggle",
"false",
"Whether or not the layer control is shown"
"Whether or not the download panel is shown"
).map<boolean>(
(str) => str !== "false",
[],
@ -249,11 +249,12 @@ export default class State {
(layoutToUse) => layoutToUse?.enableSearch ?? true,
"Disables/Enables the search bar"
);
this.featureSwitchLayers = featSw(
"fs-layers",
(layoutToUse) => layoutToUse?.enableLayers ?? true,
"Disables/Enables the layer control"
this.featureSwitchBackgroundSlection = featSw(
"fs-background",
(layoutToUse) => layoutToUse?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control"
);
this.featureSwitchFilter = featSw(
"fs-filter",
(layoutToUse) => layoutToUse?.enableLayers ?? true,
@ -295,6 +296,17 @@ export default class State {
"Always show all questions"
);
this.featureSwitchEnableExport = featSw(
"fs-export",
(layoutToUse) => layoutToUse?.enableExportButton ?? false,
"Enable the export as GeoJSON and CSV button"
);
this.featureSwitchExportAsPdf = featSw(
"fs-pdf",
(layoutToUse) => layoutToUse?.enablePdfDownload ?? false,
"Enable the PDF download button"
);
this.featureSwitchIsTesting = QueryParameters.GetQueryParameter(
"test",
"false",
@ -327,7 +339,6 @@ export default class State {
);
this.featureSwitchUserbadge.addCallbackAndRun(userbadge => {
if (!userbadge) {
this.featureSwitchAddNew.setData(false)
@ -372,9 +383,9 @@ export default class State {
this.allElements = new ElementStorage();
this.changes = new Changes();
new ChangeToElementsActor(this.changes, this.allElements)
this.osmApiFeatureSource = new OsmApiFeatureSource()
new PendingChangesUploader(this.changes, this.selectedElement);

View file

@ -17,12 +17,14 @@ export default class Minimap extends BaseUIElement {
private _isInited = false;
private _allowMoving: boolean;
private readonly _leafletoptions: any;
private readonly _onFullyLoaded: (leaflet: L.Map) => void
constructor(options?: {
background?: UIEventSource<BaseLayer>,
location?: UIEventSource<Loc>,
allowMoving?: boolean,
leafletOptions?: any
leafletOptions?: any,
onFullyLoaded?: (leaflet: L.Map) => void
}
) {
super()
@ -32,6 +34,7 @@ export default class Minimap extends BaseUIElement {
this._id = "minimap" + Minimap._nextId;
this._allowMoving = options.allowMoving ?? true;
this._leafletoptions = options.leafletOptions ?? {}
this._onFullyLoaded = options.onFullyLoaded
Minimap._nextId++
}
@ -74,10 +77,10 @@ export default class Minimap extends BaseUIElement {
}
this._isInited = true;
const location = this._location;
const self = this;
let currentLayer = this._background.data.layer()
const options = {
center: <[number, number]> [location.data?.lat ?? 0, location.data?.lon ?? 0],
center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0],
zoom: location.data?.zoom ?? 2,
layers: [currentLayer],
zoomControl: false,
@ -90,10 +93,17 @@ export default class Minimap extends BaseUIElement {
// Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving,
fadeAnimation: this._allowMoving
}
Utils.Merge(this._leafletoptions, options)
const map = L.map(this._id, options);
if (self._onFullyLoaded !== undefined) {
currentLayer.on("load", () => {
console.log("Fully loaded all tiles!")
self._onFullyLoaded(map)
})
}
map.setMaxBounds(
[[-100, -200], [100, 200]]
@ -105,6 +115,13 @@ export default class Minimap extends BaseUIElement {
map.removeLayer(currentLayer);
}
currentLayer = newLayer;
if (self._onFullyLoaded !== undefined) {
currentLayer.on("load", () => {
console.log("Fully loaded all tiles!")
self._onFullyLoaded(map)
})
}
map.addLayer(newLayer);
})

View file

@ -0,0 +1,56 @@
import State from "../../State";
import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import Toggle from "../Input/Toggle";
import {DownloadPanel} from "./DownloadPanel";
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import ExportPDF from "../ExportPDF";
export default class AllDownloads extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) {
super(AllDownloads.GenTitle, AllDownloads.GeneratePanel, "layers", isShown);
}
private static GenTitle(): BaseUIElement {
return Translations.t.general.download.title
.Clone()
.SetClass("text-2xl break-words font-bold p-2");
}
private static GeneratePanel(): BaseUIElement {
const generatePdf = () => {
new ExportPDF(
{
freeDivId: "belowmap",
background: State.state.backgroundLayer,
location: State.state.locationControl,
features: State.state.featurePipeline.features,
layout: State.state.layoutToUse,
})
}
const pdf = new Toggle(
new SubtleButton(Svg.floppy_ui(),
new Combine([Translations.t.general.download.downloadAsPdf.Clone().SetClass("font-bold"),
Translations.t.general.download.downloadAsPdfHelper.Clone()]
).SetClass("flex flex-col"))
.onClick(generatePdf),
undefined,
State.state.featureSwitchExportAsPdf
)
const exportPanel = new Toggle(
new DownloadPanel(),
undefined,
State.state.featureSwitchEnableExport
)
return new Combine([pdf, exportPanel]).SetClass("flex flex-col");
}
}

View file

@ -50,6 +50,8 @@ export class Basemap {
}
previousLayer = newLayer;
self.map.addLayer(newLayer);
extraAttribution.AttachTo('leaflet-attribution')
})

View file

@ -15,6 +15,7 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import State from "../../State";
import FilteredLayer from "../../Models/FilteredLayer";
import BackgroundSelector from "./BackgroundSelector";
/**
@ -23,9 +24,14 @@ import FilteredLayer from "../../Models/FilteredLayer";
export default class FilterView extends VariableUiElement {
constructor(filteredLayer: UIEventSource<FilteredLayer[]>) {
const backgroundSelector = new Toggle(
new BackgroundSelector(),
undefined,
State.state.featureSwitchBackgroundSlection
)
super(
filteredLayer.map((filteredLayers) =>
filteredLayers?.map(l => FilterView.createOneFilteredLayerElement(l))
filteredLayers?.map(l => FilterView.createOneFilteredLayerElement(l)).concat(backgroundSelector)
)
);
}

View file

@ -1,52 +0,0 @@
import State from "../../State";
import BackgroundSelector from "./BackgroundSelector";
import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
import Toggle from "../Input/Toggle";
import {DownloadPanel} from "./DownloadPanel";
export default class LayerControlPanel extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) {
super(LayerControlPanel.GenTitle, LayerControlPanel.GeneratePanel, "layers", isShown);
}
private static GenTitle(): BaseUIElement {
return Translations.t.general.layerSelection.title
.Clone()
.SetClass("text-2xl break-words font-bold p-2");
}
private static GeneratePanel(): BaseUIElement {
const elements: BaseUIElement[] = [];
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
const backgroundSelector = new BackgroundSelector();
backgroundSelector.SetStyle("margin:1em");
backgroundSelector.onClick(() => {
});
elements.push(backgroundSelector)
}
elements.push(new Toggle(
new DownloadPanel(),
undefined,
State.state.featureSwitchEnableExport
))
elements.push(
new Toggle(
new DownloadPanel(),
undefined,
State.state.featureSwitchEnableExport
)
);
return new Combine(elements).SetClass("flex flex-col");
}
}

View file

@ -0,0 +1,96 @@
import Combine from "../Base/Combine";
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
import Translations from "../i18n/Translations";
import AttributionPanel from "./AttributionPanel";
import State from "../../State";
import ContributorCount from "../../Logic/ContributorCount";
import Toggle from "../Input/Toggle";
import MapControlButton from "../MapControlButton";
import Svg from "../../Svg";
import AllDownloads from "./AllDownloads";
import FilterView from "./FilterView";
import FeatureSource from "../../Logic/FeatureSource/FeatureSource";
export default class LeftControls extends Combine {
constructor(featureSource: FeatureSource) {
const toggledCopyright = new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle.Clone(),
() =>
new AttributionPanel(
State.state.layoutToUse,
new ContributorCount(featureSource).Contributors
),
undefined
);
const copyrightButton = new Toggle(
toggledCopyright,
new MapControlButton(Svg.copyright_svg())
.onClick(() => toggledCopyright.isShown.setData(true)),
toggledCopyright.isShown
)
.SetClass("p-0.5");
const toggledDownload = new Toggle(
new AllDownloads(
State.state.downloadControlIsOpened
).SetClass("block p-1 rounded-full"),
new MapControlButton(Svg.download_svg())
.onClick(() => State.state.downloadControlIsOpened.setData(true)),
State.state.downloadControlIsOpened
)
const downloadButtonn = new Toggle(
toggledDownload,
undefined,
State.state.featureSwitchEnableExport.map(downloadEnabled => downloadEnabled || State.state.featureSwitchExportAsPdf.data,
[State.state.featureSwitchExportAsPdf])
);
const toggledFilter = new Toggle(
new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(State.state.filteredLayers).SetClass(
"block p-1 rounded-full"
),
undefined,
State.state.filterIsOpened
),
new MapControlButton(Svg.filter_svg())
.onClick(() => State.state.filterIsOpened.setData(true)),
State.state.filterIsOpened
)
const filterButton = new Toggle(
toggledFilter,
undefined,
State.state.featureSwitchFilter
);
State.state.locationControl.addCallback(() => {
// Close the layer selection when the map is moved
toggledDownload.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
toggledFilter.isEnabled.setData(false);
});
State.state.selectedElement.addCallbackAndRunD((_) => {
toggledDownload.isEnabled.setData(false);
copyrightButton.isEnabled.setData(false);
toggledFilter.isEnabled.setData(false);
});
super([filterButton,
downloadButtonn,
copyrightButton])
this.SetClass("flex flex-col")
}
}

View file

@ -0,0 +1,43 @@
import Combine from "../Base/Combine";
import Toggle from "../Input/Toggle";
import MapControlButton from "../MapControlButton";
import GeoLocationHandler from "../../Logic/Actors/GeoLocationHandler";
import State from "../../State";
import Svg from "../../Svg";
export default class RightControls extends Combine {
constructor() {
const geolocationButton = new Toggle(
new MapControlButton(
new GeoLocationHandler(
State.state.currentGPSLocation,
State.state.leafletMap,
State.state.layoutToUse
), {
dontStyle: true
}
),
undefined,
State.state.featureSwitchGeolocation
);
const plus = new MapControlButton(
Svg.plus_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom++;
State.state.locationControl.ping();
});
const min = new MapControlButton(
Svg.min_zoom_svg()
).onClick(() => {
State.state.locationControl.data.zoom--;
State.state.locationControl.ping();
});
super([plus, min, geolocationButton].map(el => el.SetClass("m-0.5 md:m-1")))
this.SetClass("flex flex-col")
}
}

View file

@ -166,7 +166,7 @@ export default class SimpleAddUI extends Toggle {
])
)
.onClick(() => State.state.layerControlIsOpened.setData(true))
.onClick(() => State.state.filterIsOpened.setData(true))
const openLayerOrConfirm = new Toggle(
confirmButton,
@ -238,7 +238,13 @@ export default class SimpleAddUI extends Toggle {
const allButtons = [];
for (const layer of State.state.filteredLayers.data) {
if (layer.isDisplayed.data === false && State.state.featureSwitchLayers) {
if (layer.isDisplayed.data === false && !State.state.featureSwitchFilter.data) {
// The layer is not displayed and we cannot enable the layer control -> we skip
continue;
}
if(layer.layerDef.name === undefined){
// this is a parlty hidden layer
continue;
}

193
UI/ExportPDF.ts Normal file
View file

@ -0,0 +1,193 @@
/**
* Creates screenshoter to take png screenshot
* Creates jspdf and downloads it
* - landscape pdf
*
* To add new layout:
* - add new possible layout name in constructor
* - add new layout in "PDFLayout"
* -> in there are more instructions
*/
import jsPDF from "jspdf";
import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter";
import {UIEventSource} from "../Logic/UIEventSource";
import Minimap from "./Base/Minimap";
import Loc from "../Models/Loc";
import {BBox} from "../Logic/GeoOperations";
import ShowDataLayer from "./ShowDataLayer";
import BaseLayer from "../Models/BaseLayer";
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
import {FixedUiElement} from "./Base/FixedUiElement";
import Translations from "./i18n/Translations";
import State from "../State";
import Constants from "../Models/Constants";
export default class ExportPDF {
// dimensions of the map in milimeter
// A4: 297 * 210mm
private readonly mapW = 297;
private readonly mapH = 210;
private readonly scaling = 2
private readonly freeDivId: string;
private readonly _layout: UIEventSource<LayoutConfig>;
private _screenhotTaken = false;
constructor(
options: {
freeDivId: string,
location: UIEventSource<Loc>,
background?: UIEventSource<BaseLayer>
features: UIEventSource<{ feature: any }[]>,
layout: UIEventSource<LayoutConfig>
}
) {
this.freeDivId = options.freeDivId;
this._layout = options.layout;
const self = this;
// We create a minimap at the given location and attach it to the given 'hidden' element
const l = options.location.data;
const loc = {
lat: l.lat,
lon: l.lon,
zoom: l.zoom + 1
}
const minimap = new Minimap({
location: new UIEventSource<Loc>(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
background: options.background,
allowMoving: false,
onFullyLoaded: leaflet => window.setTimeout(() => {
if (self._screenhotTaken) {
return;
}
try {
self.CreatePdf(leaflet)
.then(() => self.cleanup())
.catch(() => self.cleanup())
} catch (e) {
console.error(e)
self.cleanup()
}
}, 500)
})
minimap.SetStyle(`width: ${this.mapW * this.scaling}mm; height: ${this.mapH * this.scaling}mm;`)
minimap.AttachTo(options.freeDivId)
// Next: we prepare the features. Only fully contained features are shown
const bounded = options.features.map(feats => {
const leaflet = minimap.leafletMap.data;
if (leaflet === undefined) {
return feats
}
const bounds = BBox.fromLeafletBounds(leaflet.getBounds().pad(0.2))
return feats.filter(f => BBox.get(f.feature).isContainedIn(bounds))
}, [minimap.leafletMap])
// Add the features to the minimap
new ShowDataLayer(
bounded,
minimap.leafletMap,
options.layout,
false
)
}
private cleanup() {
// new FixedUiElement("Screenshot taken!").AttachTo(this.freeDivId)
this._screenhotTaken = true;
}
private async CreatePdf(leaflet: L.Map) {
const t = Translations.t.general.pdf;
const layout = this._layout.data
const screenshotter = new SimpleMapScreenshoter();
//minimap op index.html -> hidden daar alles op doen en dan weg
//minimap - leaflet map ophalen - boundaries ophalen - State.state.featurePipeline
screenshotter.addTo(leaflet);
console.log("Taking screenshot")
let doc = new jsPDF('landscape');
const image = (await screenshotter.takeScreen('image'))
// @ts-ignore
doc.addImage(image, 'PNG', 0, 0, this.mapW, this.mapH);
doc.setDrawColor(255, 255, 255)
doc.setFillColor(255, 255, 255)
doc.roundedRect(12, 10, 145, 25, 5, 5, 'FD')
doc.setFontSize(20)
doc.textWithLink(layout.title.txt, 40, 18.5, {
maxWidth: 125,
url: window.location.href
})
doc.setFontSize(10)
doc.text(t.generatedWith.txt, 40, 23, {
maxWidth: 125
})
const backgroundLayer : BaseLayer = State.state.backgroundLayer.data
const attribution = new FixedUiElement(backgroundLayer.layer().getAttribution() ?? backgroundLayer.name).ConstructElement().innerText
doc.textWithLink(t.attr.txt, 40, 26.5, {
maxWidth: 125,
url: "https://www.openstreetmap.org/copyright"
})
doc.text(t.attrBackground.Subs({
background: attribution
}).txt, 40, 30)
let date = new Date().toISOString().substr(0,16)
doc.setFontSize(7)
doc.text(t.versionInfo.Subs({
version: Constants.vNumber,
date: date
}).txt, 40, 34, {
maxWidth: 125
})
// Add the logo of the layout
let img = document.createElement('img');
const imgSource = layout.icon
const imgType = imgSource.substr(imgSource.lastIndexOf(".") + 1);
img.src = imgSource
console.log(imgType)
if (imgType.toLowerCase() === "svg") {
new FixedUiElement("").AttachTo(this.freeDivId)
// This is an svg image, we use the canvas to convert it to a png
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d');
canvas.width = 500
canvas.height = 500
img.style.width = "100%"
img.style.height = "100%"
ctx.drawImage(img, 0, 0, 500, 500);
const base64img = canvas.toDataURL("image/png")
doc.addImage(base64img, 'png', 15, 12, 20, 20);
} else {
try {
doc.addImage(img, imgType, 15, 12, 20, 20);
} catch (e) {
console.error(e)
}
}
doc.save(`MapComplete_${layout.title.txt}_${date}.pdf`);
}
}

View file

@ -142,7 +142,7 @@ export default class LengthInput extends InputElement<string> {
if (leaflet) {
const first = leaflet.layerPointToLatLng(firstClickXY)
const last = leaflet.layerPointToLatLng([dx, dy])
const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 100000) / 100
const geoDist = Math.floor(GeoOperations.distanceBetween([first.lng, first.lat], [last.lng, last.lat]) * 10000) / 10
self.value.setData("" + geoDist)
}

View file

@ -63,13 +63,15 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
}
return new EditableTagRendering(tags, tr, layerConfig.units);
});
let editElements : BaseUIElement[] = []
if (!questionBoxIsUsed) {
renderings.push(questionBox);
editElements.push(questionBox);
}
if (layerConfig.deletion) {
renderings.push(
editElements.push(
new VariableUiElement(tags.map(tags => tags.id).map(id =>
new DeleteWizard(
id,
@ -79,7 +81,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
}
if (layerConfig.allowSplit) {
renderings.push(
editElements.push(
new VariableUiElement(tags.map(tags => tags.id).map(id =>
new SplitRoadWizard(id))
))
@ -91,7 +93,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
renderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap")))
}
renderings.push(
editElements.push(
new VariableUiElement(
State.state.osmConnection.userDetails
.map(ud => ud.csCount)
@ -109,7 +111,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
)
renderings.push(
editElements.push(
new VariableUiElement(
State.state.featureSwitchIsDebugging.map(isDebugging => {
if (isDebugging) {
@ -119,6 +121,16 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
})
)
)
const editors = new VariableUiElement(State.state.featureSwitchUserbadge.map(
userbadge => {
if(!userbadge){
return undefined
}
return new Combine(editElements)
}
))
renderings.push(editors)
return new Combine(renderings).SetClass("block")

View file

@ -45,8 +45,8 @@ export default class SplitRoadWizard extends Toggle {
const roadElement = State.state.allElements.ContainingFeatures.get(id)
const roadEventSource = new UIEventSource([{feature: roadElement, freshness: new Date()}]);
// Datalayer displaying the road and the cut points (if any)
new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true, "splitRoadWay");
new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false, "splitRoad: splitpoints")
new ShowDataLayer(roadEventSource, miniMap.leafletMap, State.state.layoutToUse, false, true);
new ShowDataLayer(splitPoints, miniMap.leafletMap, SplitRoadWizard.splitLayout, false, false)
/**
* Handles a click on the overleaf map.

View file

@ -16,14 +16,13 @@ export default class ShowDataLayer {
private readonly _leafletMap: UIEventSource<L.Map>;
private _cleanCount = 0;
private readonly _enablePopups: boolean;
private readonly _features: UIEventSource<{ feature: any, freshness: Date }[]>
private readonly _features: UIEventSource<{ feature: any}[]>
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
constructor(features: UIEventSource<{ feature: any}[]>,
leafletMap: UIEventSource<L.Map>,
layoutToUse: UIEventSource<LayoutConfig>,
enablePopups = true,
zoomToFeatures = false,
name?: string) {
zoomToFeatures = false) {
this._leafletMap = leafletMap;
this._enablePopups = enablePopups;
this._features = features;

View file

@ -1 +1 @@
{"contributors":[{"contributor":"pietervdvn", "commits":835},{"contributor":"Pieter Vander Vennet", "commits":746},{"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}]}
{"contributors":[{"contributor":"pietervdvn", "commits":1088},{"contributor":"Pieter Vander Vennet", "commits":775},{"contributor":"Weblate", "commits":47},{"contributor":"Robin van der Linde", "commits":45},{"contributor":"Tobias", "commits":35},{"contributor":"Christian Neumann", "commits":33},{"contributor":"Win Olario", "commits":31},{"contributor":"Pieter Fiers", "commits":31},{"contributor":"karelleketers", "commits":26},{"contributor":"Artem", "commits":22},{"contributor":"Ward", "commits":20},{"contributor":"Sebastian Kürten", "commits":19},{"contributor":"Arno Deceuninck", "commits":18},{"contributor":"pgm-chardelv1", "commits":17},{"contributor":"Joost", "commits":17},{"contributor":"Marco", "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":"LiamSimons", "commits":10},{"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":"Vinicius", "commits":5},{"contributor":"Irina", "commits":5},{"contributor":"Ward Beyens", "commits":4},{"contributor":"SiegbjornSitumeang", "commits":4},{"contributor":"Polgár Sándor", "commits":4},{"contributor":"Jan Zabel", "commits":4},{"contributor":"Hiroshi Miura", "commits":4},{"contributor":"Fabio Bettani", "commits":4},{"contributor":"Wiktor Przybylski", "commits":3},{"contributor":"vankos", "commits":3},{"contributor":"seppesantens", "commits":3},{"contributor":"Nikolay Korotkiy", "commits":3},{"contributor":"Léo Villeveygoux", "commits":3},{"contributor":"JCGF-OSM", "commits":3},{"contributor":"Erik Palm", "commits":3},{"contributor":"David Haberthür", "commits":3},{"contributor":"快乐的老鼠宝宝", "commits":2},{"contributor":"Stanislas Gueniffey", "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":"Eduardo Addad de Oliveira", "commits":2},{"contributor":"Damian Tokarski", "commits":2},{"contributor":"Charlotte Delvaux", "commits":2},{"contributor":"Tomas Fiers", "commits":1},{"contributor":"Thibault Molleman", "commits":1},{"contributor":"tbowdecl97", "commits":1},{"contributor":"Seppe Santens", "commits":1},{"contributor":"Sebastian", "commits":1},{"contributor":"Sean Young", "commits":1},{"contributor":"Schouppe Joost", "commits":1},{"contributor":"root", "commits":1},{"contributor":"Rodrigo Tavares", "commits":1},{"contributor":"Raphael Das Gupta", "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}]}

View file

@ -132,7 +132,7 @@
"if": "crossing_ref=",
"then": {
"en": "This is not a zebra crossing",
"nl": "Dit is niet een zebrapad"
"nl": "Dit is geen zebrapad"
}
}
]
@ -155,7 +155,7 @@
"if": "bicycle=no",
"then": {
"en": "A cyclist can not use this crossing",
"nl": "Een fietser kan niet deze oversteekplaats gebruiken"
"nl": "Een fietser kan deze oversteekplaats niet gebruiken"
}
}
]
@ -178,7 +178,7 @@
"if": "crossing:island=no",
"then": {
"en": "This crossing does not have an island in the middle",
"nl": "Deze oversteekplaats heeft niet een verkeerseiland in het midden"
"nl": "Deze oversteekplaats heeft geen verkeerseiland in het midden"
}
}
]
@ -201,7 +201,7 @@
"if": "tactile_paving=no",
"then": {
"en": "This crossing does not have tactile paving",
"nl": "Deze oversteekplaats heeft niet een geleidelijn"
"nl": "Deze oversteekplaats heeft geen geleidelijn"
}
},
{
@ -237,7 +237,7 @@
"if": "button_operated=no",
"then": {
"en": "This traffic light does not have a button to request green light",
"nl": "Dit verkeerlicht heeft niet een knop voor groen licht"
"nl": "Dit verkeerlicht heeft geen knop voor groen licht"
}
}
]
@ -252,8 +252,8 @@
{
"if": "red_turn:right:bicycle=yes",
"then": {
"en": "A cyclist can turn right if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='height: 3em'>",
"nl": "Een fietser mag wel rechtsaf slaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='height: 3em'>"
"en": "A cyclist can turn right if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='width: 3em'>",
"nl": "Een fietser mag wel rechtsaf slaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be"
},
@ -284,8 +284,8 @@
{
"if": "red_turn:straight:bicycle=yes",
"then": {
"en": "A cyclist can go straight on if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='height: 3em'>",
"nl": "Een fietser mag wel rechtdoor gaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='height: 3em'>"
"en": "A cyclist can go straight on if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='width: 3em'>",
"nl": "Een fietser mag wel rechtdoor gaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be"
},

View file

@ -30,7 +30,12 @@
"then": "./assets/layers/toilet/wheelchair.svg"
},
{
"if": "toilets:position=urinals",
"if": {
"or": [
"toilets:position=urinals",
"toilets:position=urinal"
]
},
"then": "./assets/layers/toilet/urinal.svg"
}
]
@ -260,7 +265,7 @@
}
},
{
"if": "toilets:position=urinals",
"if": "toilets:position=urinal",
"then": {
"en": "There are only urinals here",
"de": "Hier gibt es nur Pissoirs",
@ -280,7 +285,7 @@
}
},
{
"if": "toilets:position=seated;urinals",
"if": "toilets:position=seated;urinal",
"then": {
"en": "Both seated toilets and urinals are available here",
"de": "Sowohl Sitztoiletten als auch Pissoirs sind hier verfügbar",

View file

@ -169,19 +169,5 @@
},
"color": {
"render": "#FFC0CB"
},
"presets": [
{
"tags": [
"man_made=watermill",
"fixme=Toegevoegd met MapComplete, geometry nog uit te tekenen"
],
"title": {
"nl": "Paden"
},
"description": {
"nl": "Voeg een ontbrekend, erkend pad toe."
}
}
]
}
}

View file

@ -1,3 +1,67 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.08 8.86C8.13 8.53 8.24 8.24 8.38 7.99C8.52 7.74 8.72 7.53 8.97 7.37C9.21 7.22 9.51 7.15 9.88 7.14C10.11 7.15 10.32 7.19 10.51 7.27C10.71 7.36 10.89 7.48 11.03 7.63C11.17 7.78 11.28 7.96 11.37 8.16C11.46 8.36 11.5 8.58 11.51 8.8H13.3C13.28 8.33 13.19 7.9 13.02 7.51C12.85 7.12 12.62 6.78 12.32 6.5C12.02 6.22 11.66 6 11.24 5.84C10.82 5.68 10.36 5.61 9.85 5.61C9.2 5.61 8.63 5.72 8.15 5.95C7.67 6.18 7.27 6.48 6.95 6.87C6.63 7.26 6.39 7.71 6.24 8.23C6.09 8.75 6 9.29 6 9.87V10.14C6 10.72 6.08 11.26 6.23 11.78C6.38 12.3 6.62 12.75 6.94 13.13C7.26 13.51 7.66 13.82 8.14 14.04C8.62 14.26 9.19 14.38 9.84 14.38C10.31 14.38 10.75 14.3 11.16 14.15C11.57 14 11.93 13.79 12.24 13.52C12.55 13.25 12.8 12.94 12.98 12.58C13.16 12.22 13.27 11.84 13.28 11.43H11.49C11.48 11.64 11.43 11.83 11.34 12.01C11.25 12.19 11.13 12.34 10.98 12.47C10.83 12.6 10.66 12.7 10.46 12.77C10.27 12.84 10.07 12.86 9.86 12.87C9.5 12.86 9.2 12.79 8.97 12.64C8.72 12.48 8.52 12.27 8.38 12.02C8.24 11.77 8.13 11.47 8.08 11.14C8.03 10.81 8 10.47 8 10.14V9.87C8 9.52 8.03 9.19 8.08 8.86V8.86ZM10 0C4.48 0 0 4.48 0 10C0 15.52 4.48 20 10 20C15.52 20 20 15.52 20 10C20 4.48 15.52 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18Z" fill="white"/>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 600 600"
enable-background="new 0 0 600 600"
xml:space="preserve"
sodipodi:docname="download.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"><metadata
id="metadata17"><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="defs15">
</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="namedview13"
showgrid="false"
inkscape:zoom="0.78666667"
inkscape:cx="257.94125"
inkscape:cy="387.47074"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<rect
display="none"
fill="#020202"
stroke="#000000"
stroke-width="1.1344"
stroke-miterlimit="10"
width="600"
height="600"
id="rect2" />
<polygon
display="none"
fill="#FFFFFF"
points="0,0 0,600 600,300 "
id="polygon4" />
<path
d="M 449.2,197.8 H 349.4 V 44.5 c 0,-14 -11.2,-25.5 -25,-25.5 h -49.9 c -13.7,0 -25,11.5 -25,25.5 v 153.3 h -99.8 l 149.7,204.4 z m 112.4,76.7 c -20.7,0 -37.4,17.2 -37.4,38.3 V 504.4 H 74.9 V 312.8 C 74.9,291.6 58.1,274.5 37.5,274.5 16.8,274.5 0,291.6 0,312.8 V 542.7 C 0,563.8 16.8,581 37.4,581 h 524.1 c 20.7,0 37.4,-17.2 37.4,-38.3 V 312.8 c 0.1,-21.2 -16.7,-38.3 -37.3,-38.3 z"
id="path6"
style="fill:#000000;fill-opacity:1"
inkscape:connector-curvature="0" />
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -216,6 +216,16 @@
"license": "CC0; trivial",
"sources": []
},
{
"authors": [
"Engr.eponce"
],
"path": "download.svg",
"license": "CC-BY-SA 4.0",
"sources": [
"https://commons.wikimedia.org/wiki/File:Download-icon.svg"
]
},
{
"authors": [],
"path": "down.svg",

View file

@ -23,9 +23,9 @@
"startLat": 51,
"startLon": 3.75,
"startZoom": 11,
"widenFactor": 0.05,
"widenFactor": 0,
"socialImage": "./assets/themes/cycle_infra/cycle-infra.svg",
"enableExportButton": true,
"enableDownload": true,
"layers": [
{
"id": "cycleways",
@ -33,7 +33,7 @@
"en": "Cycleways",
"nl": "Fietspaden"
},
"minzoom": 14,
"minzoom": 16,
"source": {
"osmTags": {
"or": [
@ -239,7 +239,7 @@
"if": "cyclestreet=",
"then": {
"en": "This is not a cyclestreet.",
"nl": "Dit is niet een fietstraat"
"nl": "Dit is geen fietsstraat"
},
"addExtraTags": [
"overtaking:motor_vehicle="
@ -427,49 +427,57 @@
{
"if": "cycleway:smoothness=excellent",
"then": {
"en": "Usable for thin rollers: rollerblade, skateboard"
"en": "Usable for thin rollers: rollerblade, skateboard",
"nl": "Geschikt voor fijne rollers: rollerblade, skateboard"
}
},
{
"if": "cycleway:smoothness=good",
"then": {
"en": "Usable for thin wheels: racing bike"
"en": "Usable for thin wheels: racing bike",
"nl": "Geschikt voor fijne wielen: racefiets"
}
},
{
"if": "cycleway:smoothness=intermediate",
"then": {
"en": "Usable for normal wheels: city bike, wheelchair, scooter"
"en": "Usable for normal wheels: city bike, wheelchair, scooter",
"nl": "Geschikt voor normale wielen: stadsfiets, rolstoel, scooter"
}
},
{
"if": "cycleway:smoothness=bad",
"then": {
"en": "Usable for robust wheels: trekking bike, car, rickshaw"
"en": "Usable for robust wheels: trekking bike, car, rickshaw",
"nl": "Geschikt voor brede wielen: trekfiets, auto, rickshaw"
}
},
{
"if": "cycleway:smoothness=very_bad",
"then": {
"en": "Usable for vehicles with high clearance: light duty off-road vehicle"
"en": "Usable for vehicles with high clearance: light duty off-road vehicle",
"nl": "Geschikt voor voertuigen met hoge banden: lichte terreinwagen"
}
},
{
"if": "cycleway:smoothness=horrible",
"then": {
"en": "Usable for off-road vehicles: heavy duty off-road vehicle"
"en": "Usable for off-road vehicles: heavy duty off-road vehicle",
"nl": "Geschikt voor terreinwagens: zware terreinwagen"
}
},
{
"if": "cycleway:smoothness=very_horrible",
"then": {
"en": "Usable for specialized off-road vehicles: tractor, ATV"
"en": "Usable for specialized off-road vehicles: tractor, ATV",
"nl": "Geschikt voor gespecialiseerde terreinwagens: tractor, alleterreinwagen"
}
},
{
"if": "cycleway:smoothness=impassable",
"then": {
"en": "Impassable / No wheeled vehicle"
"en": "Impassable / No wheeled vehicle",
"nl": "Niet geschikt voor voertuigen met wielen"
}
}
]
@ -625,8 +633,11 @@
},
"freeform": {
"key": "width:carriageway",
"addExtraTags": [],
"type": "pfloat"
"type": "length",
"helperArgs": [
"20",
"map"
]
},
"question": {
"en": "What is the carriage width of this road (in meters)?",
@ -648,24 +659,24 @@
{
"if": "cycleway:traffic_sign=BE:D7",
"then": {
"en": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>",
"nl": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"en": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>",
"nl": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be"
},
{
"if": "cycleway:traffic_sign~BE:D7;.*",
"then": {
"en": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'> ",
"nl": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"en": "Compulsory cycleway (with supplementary sign)<br><img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'> ",
"nl": "Verplicht fietspad (met onderbord)<br><img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"hideInAnswer": true
},
{
"if": "cycleway:traffic_sign=BE:D9",
"then": {
"en": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>",
"nl": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"en": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>",
"nl": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -676,8 +687,8 @@
{
"if": "cycleway:traffic_sign=BE:D10",
"then": {
"en": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>",
"nl": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"en": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>",
"nl": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -709,8 +720,8 @@
{
"if": "traffic_sign=BE:D7",
"then": {
"en": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>",
"nl": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"en": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>",
"nl": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -723,16 +734,16 @@
{
"if": "traffic_sign~BE:D7;.*",
"then": {
"en": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'> ",
"nl": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"en": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'> ",
"nl": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"hideInAnswer": true
},
{
"if": "traffic_sign=BE:D9",
"then": {
"en": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>",
"nl": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"en": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>",
"nl": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -747,8 +758,8 @@
{
"if": "traffic_sign=BE:D10",
"then": {
"en": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>",
"nl": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"en": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>",
"nl": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -771,8 +782,8 @@
},
{
"question": {
"en": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) have a supplementary sign?",
"nl": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) een onderbord?"
"en": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) have a supplementary sign?",
"nl": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) een onderbord?"
},
"condition": {
"or": [
@ -784,8 +795,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M6",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -795,8 +806,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M13",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -806,8 +817,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M14",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -818,8 +829,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M7",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -829,8 +840,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M15",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -840,8 +851,8 @@
{
"if": "cycleway:traffic_sign=BE:D7;BE:M16",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -860,8 +871,8 @@
},
{
"question": {
"en": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) have a supplementary sign?",
"nl": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) een onderbord?"
"en": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) have a supplementary sign?",
"nl": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) een onderbord?"
},
"condition": {
"or": [
@ -873,8 +884,8 @@
{
"if": "traffic_sign=BE:D7;BE:M6",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -884,8 +895,8 @@
{
"if": "traffic_sign=BE:D7;BE:M13",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -895,8 +906,8 @@
{
"if": "traffic_sign=BE:D7;BE:M14",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -907,8 +918,8 @@
{
"if": "traffic_sign=BE:D7;BE:M7",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -918,8 +929,8 @@
{
"if": ":traffic_sign=BE:D7;BE:M15",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -929,8 +940,8 @@
{
"if": "traffic_sign=BE:D7;BE:M16",
"then": {
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"en": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>",
"nl": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"hideInAnswer": "_country!=be",
"addExtraTags": [
@ -964,7 +975,11 @@
},
"freeform": {
"key": "cycleway:buffer",
"type": "pfloat"
"type": "length",
"helperArgs": [
"20",
"map"
]
}
},
{
@ -1151,7 +1166,7 @@
"calculatedTags": [
"_comfort_score=feat.score('https://raw.githubusercontent.com/pietervdvn/AspectedRouting/master/Examples/bicycle/aspects/bicycle.comfort.json')"
],
"minzoom": 14,
"minzoom": 16,
"wayHandling": 0,
"title": {
"render": {
@ -1286,13 +1301,31 @@
"if": "cyclestreet=",
"then": {
"en": "This is not a cyclestreet.",
"nl": "Dit is niet een fietstraat"
"nl": "Dit is geen fietsstraat"
},
"addExtraTags": [
"overtaking:motor_vehicle="
]
}
]
},
{
"render": {
"en": "The carriage width of this road is <strong>{width:carriageway}m</strong>",
"nl": "De breedte van deze rijbaan in deze straat is <strong>{width:carriageway}m</strong>"
},
"freeform": {
"key": "width:carriageway",
"type": "length",
"helperArgs": [
"20",
"map"
]
},
"question": {
"en": "What is the carriage width of this road (in meters)?<br/><span class='subtle'>This is measured from kerb to kerb, including parking lanes</span>",
"nl": "Hoe breed is de rijbaan in deze straat (in meters)?<br/><span class='subtle'>Gemeten van stoepsteen tot stoepsten, inclusief parkeerstroken</span>"
}
}
]
},
@ -1465,7 +1498,8 @@
{
"if": "cycle_barrier:type=squeeze",
"then": {
"en": "Squeeze gate, gap is smaller at top, than at the bottom <img src='./assets/themes/cycle_infra/Cycle_barrier_squeeze.png' style='width:8em'>"
"en": "Squeeze gate, gap is smaller at top, than at the bottom <img src='./assets/themes/cycle_infra/Cycle_barrier_squeeze.png' style='width:8em'>",
"nl": "Knijppoort, ruimte is smaller aan de top, dan aan de bodem <img src='./assets/themes/cycle_infra/Cycle_barrier_squeeze.png' style='width:8em'>"
}
}
]
@ -1487,15 +1521,21 @@
},
"freeform": {
"key": "maxwidth:physical",
"type": "pfloat"
"type": "length",
"helperArgs": [
"20",
"map"
]
}
},
{
"render": {
"en": "Space between barriers (along the length of the road): {width:seperation} m"
"en": "Space between barriers (along the length of the road): {width:seperation} m",
"nl": "Ruimte tussen barrières (langs de lengte van de weg): {width:seperation} m"
},
"question": {
"en": "How much space is there between the barriers (along the length of the road)?"
"en": "How much space is there between the barriers (along the length of the road)?",
"nl": "Hoeveel ruimte is er tussen de barrières (langs de lengte van de weg)?"
},
"condition": {
"or": [
@ -1505,15 +1545,21 @@
},
"freeform": {
"key": "width:seperation",
"type": "pfloat"
"type": "length",
"helperArgs": [
"20",
"map"
]
}
},
{
"render": {
"en": "Width of opening: {width:opening} m"
"en": "Width of opening: {width:opening} m",
"nl": "Breedte van de opening: {width:opening} m"
},
"question": {
"en": "How wide is the smallest opening next to the barriers?"
"en": "How wide is the smallest opening next to the barriers?",
"nl": "Hoe breed is de smalste opening naast de barrières?"
},
"condition": {
"or": [
@ -1531,7 +1577,8 @@
"en": "Overlap: {overlap} m"
},
"question": {
"en": "How much overlap do the barriers have?"
"en": "How much overlap do the barriers have?",
"nl": "Hoeveel overlappen de barrières?"
},
"condition": {
"or": [

View file

@ -25,6 +25,8 @@
"widenFactor": 0.05,
"socialImage": "",
"defaultBackgroundId": "CartoDB.Positron",
"enablePdfDownload": true,
"enableDownload": true,
"layers": [
{
"#": "Nature reserve with geometry, z>=13",

View file

@ -1,4 +1,3 @@
<!DOCTYPE html>
<!-- WARNING: index.html serves as a template. If you want to change something, change it there -->
<html lang="en">
@ -24,8 +23,9 @@
<link rel="icon" href="./assets/svg/add.svg" sizes="any" type="image/svg+xml">
<meta property="og:image" content="./assets/SocialImage.png">
<meta property="og:title" content="MapComplete - editable, thematic maps with OpenStreetMap">
<meta property="og:description" content="MapComplete is a platform to visualize OpenStreetMap on a specific topic and to easily contribute data back to it.">
<meta property="og:description"
content="MapComplete is a platform to visualize OpenStreetMap on a specific topic and to easily contribute data back to it.">
<link rel="apple-touch-icon" sizes="512x512" href="./assets/generated/svg_mapcomplete_logo512.png">
<link rel="apple-touch-icon" sizes="384x384" href="./assets/generated/svg_mapcomplete_logo384.png">
<link rel="apple-touch-icon" sizes="192x192" href="./assets/generated/svg_mapcomplete_logo192.png">
@ -74,7 +74,7 @@
Loading MapComplete, hang on...
</div>
<span id="belowmap" class="absolute" style="z-index: -1">Below</span>
<div id="leafletDiv"></div>
<script src="./index.ts"></script>

View file

@ -61,6 +61,12 @@
"readMessages": "You have unread messages. Read these before deleting a point - someone might have feedback"
},
"general": {
"pdf": {
"generatedWith": "Generated with MapComplete.osm.be",
"attr": "Map data © OpenStreetMap Contributors, reusable under ODbL",
"attrBackground": "Background layer: {background}",
"versionInfo": "v{version} - generated on {date}"
},
"loginWithOpenStreetMap": "Login with OpenStreetMap",
"welcomeBack": "You are logged in, welcome back!",
"loginToStart": "Login to answer this question",
@ -159,9 +165,11 @@
},
"download": {
"title": "Download visible data",
"downloadAsPdf": "Download a PDF of the current map",
"downloadAsPdfHelper": "Ideal to print the current map",
"downloadGeojson": "Download visible data as geojson",
"downloadGeoJsonHelper": "Compatible with QGIS, OsmAnd, ArcGIS, ESRI, ...",
"downloadCSV": "Download as CSV",
"downloadGeoJsonHelper": "Compatible with QGIS, ArcGIS, ESRI, ...",
"downloadCSV": "Download visible data as CSV",
"downloadCSVHelper": "Compatible with LibreOffice Calc, Excel, ...",
"includeMetaData": "Include metadata (last editor, calculated values, ...)",
"licenseInfo": "<h3>Copyright notice</h3>The provided is available under ODbL. Reusing this data is free for any purpose, but <ul><li>the attribution <b>© OpenStreetMap contributors</b> is required</li><li>Any change to this data must be republished under the same license</li></ul> Please read the full <a href='https://www.openstreetmap.org/copyright' target='_blank'>copyright notice</a> for details",

View file

@ -802,7 +802,7 @@
"question": "Can a cyclist turn right when the light is red?",
"mappings": {
"0": {
"then": "A cyclist can turn right if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='height: 3em'>"
"then": "A cyclist can turn right if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='width: 3em'>"
},
"1": {
"then": "A cyclist can turn right if the light is red"
@ -816,7 +816,7 @@
"question": "Can a cyclist go straight on when the light is red?",
"mappings": {
"0": {
"then": "A cyclist can go straight on if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='height: 3em'>"
"then": "A cyclist can go straight on if the light is red <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='width: 3em'>"
},
"1": {
"then": "A cyclist can go straight on if the light is red"

View file

@ -837,7 +837,7 @@
"then": "Dit is een zebrapad"
},
"1": {
"then": "Dit is niet een zebrapad"
"then": "Dit is geen zebrapad"
}
}
},
@ -848,7 +848,7 @@
"then": "Een fietser kan deze oversteekplaats gebruiken"
},
"1": {
"then": "Een fietser kan niet deze oversteekplaats gebruiken"
"then": "Een fietser kan deze oversteekplaats niet gebruiken"
}
}
},
@ -859,7 +859,7 @@
"then": "Deze oversteekplaats heeft een verkeerseiland in het midden"
},
"1": {
"then": "Deze oversteekplaats heeft niet een verkeerseiland in het midden"
"then": "Deze oversteekplaats heeft geen verkeerseiland in het midden"
}
}
},
@ -870,7 +870,7 @@
"then": "Deze oversteekplaats heeft een geleidelijn"
},
"1": {
"then": "Deze oversteekplaats heeft niet een geleidelijn"
"then": "Deze oversteekplaats heeft geen geleidelijn"
},
"2": {
"then": "Deze oversteekplaats heeft een geleidelijn, die incorrect is."
@ -884,7 +884,7 @@
"then": "Dit verkeerslicht heeft een knop voor groen licht"
},
"1": {
"then": "Dit verkeerlicht heeft niet een knop voor groen licht"
"then": "Dit verkeerlicht heeft geen knop voor groen licht"
}
}
},
@ -892,7 +892,7 @@
"question": "Mag een fietser rechtsaf slaan als het licht rood is?",
"mappings": {
"0": {
"then": "Een fietser mag wel rechtsaf slaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='height: 3em'>"
"then": "Een fietser mag wel rechtsaf slaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B22.svg' style='width: 3em'>"
},
"1": {
"then": "Een fietser mag wel rechtsaf slaan als het licht rood is"
@ -906,7 +906,7 @@
"question": "Mag een fietser rechtdoor gaan als het licht rood is?",
"mappings": {
"0": {
"then": "Een fietser mag wel rechtdoor gaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='height: 3em'>"
"then": "Een fietser mag wel rechtdoor gaan als het licht rood is <img src='./assets/layers/crossings/Belgian_road_sign_B23.svg' style='width: 3em'>"
},
"1": {
"then": "Een fietser mag wel rechtdoor gaan als het licht rood is"
@ -2364,12 +2364,6 @@
}
}
}
},
"presets": {
"0": {
"title": "Paden",
"description": "Voeg een ontbrekend, erkend pad toe."
}
}
}
}

View file

@ -703,7 +703,7 @@
"render": "Cycleways",
"mappings": {
"0": {
"then": "Bike road"
"then": "Cycleway"
},
"1": {
"then": "Shared lane"
@ -712,7 +712,7 @@
"then": "Bike lane"
},
"3": {
"then": "Bike road next to the road"
"then": "Cycleway next to the road"
},
"4": {
"then": "Cyclestreet"
@ -933,16 +933,16 @@
"question": "What traffic sign does this cycleway have?",
"mappings": {
"0": {
"then": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"1": {
"then": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'> "
"then": "Compulsory cycleway (with supplementary sign)<br><img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'> "
},
"2": {
"then": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"then": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"3": {
"then": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"then": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"4": {
"then": "No traffic sign present"
@ -953,16 +953,16 @@
"question": "What traffic sign does this cycleway have?",
"mappings": {
"0": {
"then": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Compulsory cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"1": {
"then": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'> "
"then": "Compulsory cycleway (with supplementary sign)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'> "
},
"2": {
"then": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"then": "Segregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"3": {
"then": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"then": "Unsegregated foot/cycleway <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"4": {
"then": "No traffic sign present"
@ -970,25 +970,25 @@
}
},
"11": {
"question": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) have a supplementary sign?",
"question": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) have a supplementary sign?",
"mappings": {
"0": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"1": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"2": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"3": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"4": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"5": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"6": {
"then": "No supplementary traffic sign present"
@ -996,25 +996,25 @@
}
},
"12": {
"question": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) have a supplementary sign?",
"question": "Does the traffic sign D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) have a supplementary sign?",
"mappings": {
"0": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"1": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"2": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"3": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"4": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"5": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"6": {
"then": "No supplementary traffic sign present"
@ -1108,6 +1108,10 @@
"then": "This is not a cyclestreet."
}
}
},
"2": {
"render": "The carriage width of this road is <strong>{width:carriageway}m</strong>",
"question": "What is the carriage width of this road (in meters)?<br/><span class='subtle'>This is measured from kerb to kerb, including parking lanes</span>"
}
}
},

View file

@ -652,13 +652,13 @@
"question": "Is dit een fietsstraat?",
"mappings": {
"0": {
"then": "Dit is een fietstraat, en dus een 30km/h zone"
"then": "Dit is een fietsstraat, en dus een 30km/h zone"
},
"1": {
"then": "Dit is een fietstraat"
"then": "Dit is een fietsstraat"
},
"2": {
"then": "Dit is niet een fietstraat"
"then": "Dit is geen fietsstraat"
}
}
},
@ -726,7 +726,33 @@
"question": "Waaruit is het oppervlak van het fietspad van gemaakt?"
},
"5": {
"question": "Wat is de kwaliteit van dit fietspad?"
"question": "Wat is de kwaliteit van dit fietspad?",
"mappings": {
"0": {
"then": "Geschikt voor fijne rollers: rollerblade, skateboard"
},
"1": {
"then": "Geschikt voor fijne wielen: racefiets"
},
"2": {
"then": "Geschikt voor normale wielen: stadsfiets, rolstoel, scooter"
},
"3": {
"then": "Geschikt voor brede wielen: trekfiets, auto, rickshaw"
},
"4": {
"then": "Geschikt voor voertuigen met hoge banden: lichte terreinwagen"
},
"5": {
"then": "Geschikt voor terreinwagens: zware terreinwagen"
},
"6": {
"then": "Geschikt voor gespecialiseerde terreinwagens: tractor, alleterreinwagen"
},
"7": {
"then": "Niet geschikt voor voertuigen met wielen"
}
}
},
"6": {
"render": "Deze weg is gemaakt van {surface}",
@ -769,16 +795,16 @@
"question": "Welk verkeersbord heeft dit fietspad?",
"mappings": {
"0": {
"then": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"1": {
"then": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Verplicht fietspad (met onderbord)<br><img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"2": {
"then": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"then": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"3": {
"then": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"then": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"4": {
"then": "Geen verkeersbord aanwezig"
@ -789,16 +815,16 @@
"question": "Welk verkeersbord heeft dit fietspad?",
"mappings": {
"0": {
"then": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Verplicht fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"1": {
"then": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 3em'>"
"then": "Verplicht fietspad (met onderbord)<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 3em'>"
},
"2": {
"then": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='height: 3em'>"
"then": "Afgescheiden voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D09.svg' style='width: 3em'>"
},
"3": {
"then": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='height: 3em'>"
"then": "Gedeeld voet-/fietspad <img src='./assets/themes/cycle_infra/Belgian_road_sign_D10.svg' style='width: 3em'>"
},
"4": {
"then": "Geen verkeersbord aanwezig"
@ -806,25 +832,25 @@
}
},
"11": {
"question": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) een onderbord?",
"question": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) een onderbord?",
"mappings": {
"0": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"1": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"2": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"3": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"4": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"5": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"6": {
"then": "Geen onderbord aanwezig"
@ -832,25 +858,25 @@
}
},
"12": {
"question": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='height: 1.5em'>) een onderbord?",
"question": "Heeft het verkeersbord D7 (<img src='./assets/themes/cycle_infra/Belgian_road_sign_D07.svg' style='width: 1.5em'>) een onderbord?",
"mappings": {
"0": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M6.svg' style='width: 3em'>"
},
"1": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M13.svg' style='width: 3em'>"
},
"2": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M14.svg' style='width: 3em'>"
},
"3": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M7.svg' style='width: 3em'>"
},
"4": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M15.svg' style='width: 3em'>"
},
"5": {
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='height: 3em'>"
"then": "<img src='./assets/themes/cycle_infra/Belgian_traffic_sign_M16.svg' style='width: 3em'>"
},
"6": {
"then": "Geen onderbord aanwezig"
@ -935,15 +961,19 @@
"question": "Is dit een fietsstraat?",
"mappings": {
"0": {
"then": "Dit is een fietstraat, en dus een 30km/h zone"
"then": "Dit is een fietsstraat, en dus een 30km/h zone"
},
"1": {
"then": "Dit is een fietstraat"
"then": "Dit is een fietsstraat"
},
"2": {
"then": "Dit is niet een fietstraat"
"then": "Dit is geen fietsstraat"
}
}
},
"2": {
"render": "De breedte van deze rijbaan in deze straat is <strong>{width:carriageway}m</strong>",
"question": "Hoe breed is de rijbaan in deze straat (in meters)?<br/><span class='subtle'>Gemeten van stoepsteen tot stoepsten, inclusief parkeerstroken</span>"
}
}
},
@ -1014,12 +1044,26 @@
},
"2": {
"then": "Drievoudig, drie hekjes achter elkaar <img src='./assets/themes/cycle_infra/Cycle_barrier_triple.png' style='width:8em'>"
},
"3": {
"then": "Knijppoort, ruimte is smaller aan de top, dan aan de bodem <img src='./assets/themes/cycle_infra/Cycle_barrier_squeeze.png' style='width:8em'>"
}
}
},
"3": {
"render": "Maximumbreedte: {maxwidth:physical} m",
"question": "Hoe breed is de ruimte naast de barrière?"
},
"4": {
"render": "Ruimte tussen barrières (langs de lengte van de weg): {width:seperation} m",
"question": "Hoeveel ruimte is er tussen de barrières (langs de lengte van de weg)?"
},
"5": {
"render": "Breedte van de opening: {width:opening} m",
"question": "Hoe breed is de smalste opening naast de barrières?"
},
"6": {
"question": "Hoeveel overlappen de barrières?"
}
}
}

View file

@ -2,6 +2,7 @@ import * as fs from "fs";
import {readFileSync, writeFileSync} from "fs";
import {Utils} from "../Utils";
import ScriptUtils from "./ScriptUtils";
import {Layer} from "leaflet";
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"];
@ -40,12 +41,12 @@ class TranslationPart {
}
}
recursiveAdd(object: any) {
recursiveAdd(object: any, context: string) {
const isProbablyTranslationObject = knownLanguages.map(l => object.hasOwnProperty(l)).filter(x => x).length > 0;
if (isProbablyTranslationObject) {
this.addTranslationObject(object)
this.addTranslationObject(object, context)
return;
}
@ -68,7 +69,7 @@ class TranslationPart {
this.contents.set(key, new TranslationPart())
}
(this.contents.get(key) as TranslationPart).recursiveAdd(v);
(this.contents.get(key) as TranslationPart).recursiveAdd(v, context + "." + key);
}
}
@ -190,7 +191,7 @@ function generateTranslationsObjectFrom(objects: { path: string, parsed: { id: s
if (config === undefined) {
throw "Got something not parsed! Path is " + layerFile.path
}
layerTr.recursiveAdd(config)
layerTr.recursiveAdd(config, layerFile.path)
tr.contents.set(config.id, layerTr)
}
@ -301,7 +302,7 @@ function mergeThemeTranslations() {
const oldLanguages = config.language;
const allTranslations = new TranslationPart();
allTranslations.recursiveAdd(config)
allTranslations.recursiveAdd(config, themeFile.path)
const newLanguages = allTranslations.knownLanguages()
const languageDiff = newLanguages.filter(l => oldLanguages.indexOf(l) < 0).join(", ")
if (languageDiff !== "") {

47
test.ts
View file

@ -0,0 +1,47 @@
import {UIEventSource} from "./Logic/UIEventSource";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import State from "./State";
const layout = new UIEventSource<LayoutConfig>(AllKnownLayouts.allKnownLayouts.get("bookcases"))
State.state = new State(layout.data)
const features = new UIEventSource<{ feature: any }[]>([
{
feature: {
"type": "Feature",
"properties": {"amenity": "public_bookcase", "id": "node/123"},
id: "node/123",
_matching_layer_id: "public_bookcase",
"geometry": {
"type": "Point",
"coordinates": [
3.220506906509399,
51.215009243433094
]
}
}
}, {
feature: {
"type": "Feature",
"properties": {
amenity: "public_bookcase",
id: "node/456"
},
_matching_layer_id: "public_bookcase",
id: "node/456",
"geometry": {
"type": "Point",
"coordinates": [
3.4243011474609375,
51.138432319543924
]
}
}
}
])
features.data.map(f => State.state.allElements.addOrGetElement(f.feature))