Merge develop

This commit is contained in:
pietervdvn 2021-07-19 16:15:15 +02:00
commit 40400591d0
53 changed files with 921 additions and 339 deletions

View file

@ -42,6 +42,7 @@ export default class LayoutConfig {
public readonly enableGeolocation: boolean;
public readonly enableBackgroundLayerSelection: boolean;
public readonly enableShowAllQuestions: boolean;
public readonly enableExportButton: boolean;
public readonly customCss?: string;
/*
How long is the cache valid, in seconds?
@ -152,6 +153,7 @@ 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.customCss = json.customCss;
this.cacheTimeout = json.cacheTimout ?? (60 * 24 * 60 * 60)

View file

@ -15,6 +15,7 @@ import UnitConfigJson from "./UnitConfigJson";
* General remark: a type (string | any) indicates either a fixed or a translatable string.
*/
export interface LayoutConfigJson {
/**
* The id of this layout.
*
@ -335,4 +336,5 @@ export interface LayoutConfigJson {
enableGeolocation?: boolean;
enableBackgroundLayerSelection?: boolean;
enableShowAllQuestions?: boolean;
enableExportButton?: boolean;
}

View file

@ -20,126 +20,158 @@ the URL-parameters are stated in the part between the `?` and the `#`. There are
Finally, the URL-hash is the part after the `#`. It is `node/1234` in this case.
layer-control-toggle
----------------------
Whether or not the layer control is shown The default value is _false_
tab
-----
The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_
z
---
The initial/current zoom level The default value is _0_
lat
-----
The initial/current latitude The default value is _0_
lon
-----
The initial/current longitude of the app The default value is _0_
fs-userbadge
--------------
Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_
fs-search
-----------
Disables/Enables the search bar The default value is _true_
fs-layers
-----------
Disables/Enables the layer control The default value is _true_
fs-add-new
------------
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_
fs-welcome-message
--------------------
Disables/enables the help menu or welcome message The default value is _true_
fs-iframe
-----------
Disables/Enables the iframe-popup The default value is _false_
fs-more-quests
----------------
Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_
fs-share-screen
-----------------
Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_
fs-geolocation
----------------
Disables/Enables the geolocation button The default value is _true_
fs-all-questions
------------------
Always show all questions The default value is _false_
test
------
If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org The default value is _false_
debug
-------
If true, shows some extra debugging help such as all the available tags on every object The default value is _false_
backend
backend
---------
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test' The default value is _osm_
custom-css
test
------
If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org The default value is _false_
layout
--------
The layout to load into MapComplete The default value is __
userlayout
------------
If specified, the custom css from the given link will be loaded additionaly The default value is __
If not 'false', a custom (non-official) theme is loaded. This custom layout can be done in multiple ways:
- The hash of the URL contains a base64-encoded .json-file containing the theme definition
- The hash of the URL contains a lz-compressed .json-file, as generated by the custom theme generator
- The parameter itself is an URL, in which case that URL will be downloaded. It should point to a .json of a theme The default value is _false_
background
layer-control-toggle
----------------------
Whether or not the layer control is shown The default value is _false_
tab
-----
The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >50 changesets) The default value is _0_
z
---
The initial/current zoom level The default value is _14_
lat
-----
The initial/current latitude The default value is _51.2095_
lon
-----
The initial/current longitude of the app The default value is _3.2228_
fs-userbadge
--------------
Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode. The default value is _true_
fs-search
-----------
Disables/Enables the search bar The default value is _true_
fs-layers
-----------
Disables/Enables the layer control The default value is _true_
fs-add-new
------------
The id of the background layer to start with The default value is _osm_
Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place) The default value is _true_
fs-welcome-message
--------------------
Disables/enables the help menu or welcome message The default value is _true_
fs-iframe
-----------
Disables/Enables the iframe-popup The default value is _false_
fs-more-quests
----------------
Disables/Enables the 'More Quests'-tab in the welcome message The default value is _true_
fs-share-screen
-----------------
Disables/Enables the 'Share-screen'-tab in the welcome message The default value is _true_
fs-geolocation
----------------
Disables/Enables the geolocation button The default value is _true_
fs-all-questions
------------------
Always show all questions The default value is _false_
fs-export
-----------
If set, enables the 'download'-button to download everything as geojson The default value is _false_
fake-user
-----------
If true, 'dryrun' mode is activated and a fake user account is loaded The default value is _false_
debug
-------
If true, shows some extra debugging help such as all the available tags on every object The default value is _false_
custom-css
------------
If specified, the custom css from the given link will be loaded additionaly The default value is __
background
------------
The id of the background layer to start with The default value is _osm_
oauth_token
-------------
Used to complete the login No default value set
layer-<layer-id>
------------------

View file

@ -427,6 +427,8 @@ export class InitUiElements {
state.locationControl,
state.selectedElement);
State.state.featurePipeline = source;
new ShowDataLayer(source.features, State.state.leafletMap, State.state.layoutToUse);
const selectedFeatureHandler = new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source, State.state.osmApiFeatureSource);

View file

@ -90,10 +90,10 @@ export default class AvailableBaseLayers {
for (const category of prefered) {
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
available.sort((a, b) => {
if (a.category === preferedCategory && b.category === preferedCategory) {
if (a.category === category && b.category === category) {
return 0;
}
if (a.category !== preferedCategory) {
if (a.category !== category) {
return 1
}

View file

@ -47,7 +47,12 @@ export default class StrayClickHandler {
popupAnchor: [0, -45]
})
});
const popup = L.popup().setContent("<div id='strayclick'></div>");
const popup = L.popup({
autoPan: true,
autoPanPaddingTopLeft: [15,15],
closeOnEscapeKey: true,
autoClose: true
}).setContent("<div id='strayclick' style='height: 65vh'></div>");
self._lastMarker.addTo(leafletMap.data);
self._lastMarker.bindPopup(popup);

View file

@ -1,9 +1,45 @@
import {UIEventSource} from "../UIEventSource";
import {Utils} from "../../Utils";
export default interface FeatureSource {
features: UIEventSource<{feature: any, freshness: Date}[]>;
features: UIEventSource<{ feature: any, freshness: Date }[]>;
/**
* Mainly used for debuging
*/
name: string;
}
export class FeatureSourceUtils {
/**
* Exports given featurePipeline as a geojson FeatureLists (downloads as a json)
* @param featurePipeline The FeaturePipeline you want to export
* @param options The options object
* @param options.metadata True if you want to include the MapComplete metadata, false otherwise
*/
public static extractGeoJson(featurePipeline: FeatureSource, options: { metadata?: boolean } = {}) {
let defaults = {
metadata: false,
}
options = Utils.setDefaults(options, defaults);
// Select all features, ignore the freshness and other data
let featureList: any[] = featurePipeline.features.data.map((feature) => feature.feature);
if (!options.metadata) {
for (let i = 0; i < featureList.length; i++) {
let feature = featureList[i];
for (let property in feature.properties) {
if (property[0] == "_") {
delete featureList[i]["properties"][property];
}
}
}
}
return {type: "FeatureCollection", features: featureList}
}
}

View file

@ -47,6 +47,7 @@ export class OsmConnection {
public auth;
public userDetails: UIEventSource<UserDetails>;
public isLoggedIn: UIEventSource<boolean>
private fakeUser: boolean;
_dryRun: boolean;
public preferencesHandler: OsmPreferences;
public changesetHandler: ChangesetHandler;
@ -59,12 +60,15 @@ export class OsmConnection {
url: string
};
constructor(dryRun: boolean, oauth_token: UIEventSource<string>,
constructor(dryRun: boolean,
fakeUser: boolean,
oauth_token: UIEventSource<string>,
// Used to keep multiple changesets open and to write to the correct changeset
layoutName: string,
singlePage: boolean = true,
osmConfiguration: "osm" | "osm-test" = 'osm'
) {
this.fakeUser = fakeUser;
this._singlePage = singlePage;
this._oauth_config = OsmConnection.oauth_configs[osmConfiguration] ?? OsmConnection.oauth_configs.osm;
console.debug("Using backend", this._oauth_config.url)
@ -72,7 +76,15 @@ export class OsmConnection {
this._iframeMode = Utils.runningFromConsole ? false : window !== window.top;
this.userDetails = new UIEventSource<UserDetails>(new UserDetails(this._oauth_config.url), "userDetails");
this.userDetails.data.dryRun = dryRun;
this.userDetails.data.dryRun = dryRun || fakeUser;
if(fakeUser){
const ud = this.userDetails.data;
ud.csCount = 5678
ud.loggedIn= true;
ud.unreadMessages = 0
ud.name = "Fake user"
ud.totalMessages = 42;
}
const self =this;
this.isLoggedIn = this.userDetails.map(user => user.loggedIn).addCallback(isLoggedIn => {
if(self.userDetails.data.loggedIn == false && isLoggedIn == true){
@ -138,6 +150,10 @@ export class OsmConnection {
}
public AttemptLogin() {
if(this.fakeUser){
console.log("AttemptLogin called, but ignored as fakeUser is set")
return;
}
const self = this;
console.log("Trying to log in...");
this.updateAuthObject();

View file

@ -19,6 +19,7 @@ import TitleHandler from "./Logic/Actors/TitleHandler";
import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader";
import {Relation} from "./Logic/Osm/ExtractRelations";
import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource";
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
/**
* Contains the global state: a bunch of UI-event sources
@ -58,8 +59,8 @@ export default class State {
public favouriteLayers: UIEventSource<string[]>;
public layerUpdater: OverpassFeatureSource;
public osmApiFeatureSource : OsmApiFeatureSource ;
public osmApiFeatureSource: OsmApiFeatureSource;
public filteredLayers: UIEventSource<{
@ -80,7 +81,7 @@ export default class State {
* Keeps track of relations: which way is part of which other way?
* Set by the overpass-updater; used in the metatagging
*/
public readonly knownRelations = new UIEventSource<Map<string, {role: string, relation: Relation}[]>>(undefined, "Relation memberships")
public readonly knownRelations = new UIEventSource<Map<string, { role: string, relation: Relation }[]>>(undefined, "Relation memberships")
public readonly featureSwitchUserbadge: UIEventSource<boolean>;
public readonly featureSwitchSearch: UIEventSource<boolean>;
@ -95,6 +96,11 @@ export default class State {
public readonly featureSwitchIsDebugging: UIEventSource<boolean>;
public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>;
public readonly featureSwitchApiURL: UIEventSource<string>;
public readonly featureSwitchEnableExport: UIEventSource<boolean>;
public readonly featureSwitchFakeUser: UIEventSource<boolean>;
public readonly featurePipeline: FeaturePipeline;
/**
@ -125,7 +131,7 @@ export default class State {
public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter("tab", "0", `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)`).map<number>(
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
);
constructor(layoutToUse: LayoutConfig) {
const self = this;
@ -187,6 +193,12 @@ export default class State {
"Disables/Enables the layer control");
this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true,
"Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)");
this.featureSwitchUserbadge.addCallbackAndRun(userbadge => {
if (!userbadge) {
this.featureSwitchAddNew.setData(false)
}
})
this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true,
"Disables/enables the help menu or welcome message");
this.featureSwitchIframe = featSw("fs-iframe", () => false,
@ -199,16 +211,22 @@ export default class State {
"Disables/Enables the geolocation button");
this.featureSwitchShowAllQuestions = featSw("fs-all-questions", (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false,
"Always show all questions");
this.featureSwitchEnableExport = featSw("fs-export", (layoutToUse) => layoutToUse?.enableExportButton ?? false,
"If set, enables the 'download'-button to download everything as geojson")
this.featureSwitchIsTesting = QueryParameters.GetQueryParameter("test", "false",
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org")
.map(str => str === "true", [], b => "" + b);
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug","false",
this.featureSwitchFakeUser = QueryParameters.GetQueryParameter("fake-user", "false",
"If true, 'dryrun' mode is activated and a fake user account is loaded")
.map(str => str === "true", [], b => "" + b);
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug", "false",
"If true, shows some extra debugging help such as all the available tags on every object")
.map(str => str === "true", [], b => "" + b)
this.featureSwitchApiURL = QueryParameters.GetQueryParameter("backend","osm",
this.featureSwitchApiURL = QueryParameters.GetQueryParameter("backend", "osm",
"The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'")
}
@ -221,18 +239,19 @@ export default class State {
this.backgroundLayerId = QueryParameters.GetQueryParameter("background",
layoutToUse?.defaultBackgroundId ?? "osm",
"The id of the background layer to start with")
layoutToUse?.defaultBackgroundId ?? "osm",
"The id of the background layer to start with")
}
if(Utils.runningFromConsole){
if (Utils.runningFromConsole) {
return;
}
this.osmConnection = new OsmConnection(
this.featureSwitchIsTesting.data,
this.featureSwitchFakeUser.data,
QueryParameters.GetQueryParameter("oauth_token", undefined,
"Used to complete the login"),
layoutToUse?.id,
@ -245,7 +264,7 @@ export default class State {
this.allElements = new ElementStorage();
this.changes = new Changes();
this.osmApiFeatureSource = new OsmApiFeatureSource()
new PendingChangesUploader(this.changes, this.selectedElement);
this.mangroveIdentity = new MangroveIdentity(

2
Svg.ts
View file

@ -94,7 +94,7 @@ export default class Svg {
public static crosshair_empty_svg() { return new Img(Svg.crosshair_empty, true);}
public static crosshair_empty_ui() { return new FixedUiElement(Svg.crosshair_empty_img);}
public static crosshair_locked = " <!-- Created with Inkscape (http://www.inkscape.org/) --> <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\" width=\"100\" height=\"100\" viewBox=\"0 0 26.458333 26.458334\" version=\"1.1\" id=\"svg8\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\" sodipodi:docname=\"crosshair-locked.svg\"> <defs id=\"defs2\" /> <sodipodi:namedview id=\"base\" pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1.0\" inkscape:pageopacity=\"0.0\" inkscape:pageshadow=\"2\" inkscape:zoom=\"2.8284271\" inkscape:cx=\"67.47399\" inkscape:cy=\"29.788021\" inkscape:document-units=\"px\" inkscape:current-layer=\"layer1\" showgrid=\"false\" units=\"px\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:snap-global=\"false\"> <sodipodi:guide position=\"13.229167,23.859748\" orientation=\"1,0\" id=\"guide815\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"14.944824,13.229167\" orientation=\"0,1\" id=\"guide817\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata5\"> <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> <g inkscape:label=\"Layer 1\" inkscape:groupmode=\"layer\" id=\"layer1\" transform=\"translate(0,-270.54165)\"> <circle style=\"fill: none !important;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529\" id=\"path815\" cx=\"13.16302\" cy=\"283.77081\" r=\"8.8715391\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"M 3.2841366,283.77082 H 1.0418969\" id=\"path817\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"M 25.405696,283.77082 H 23.286471\" id=\"path817-3\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"m 13.229167,295.9489 v -2.11763\" id=\"path817-3-6\" inkscape:connector-curvature=\"0\" /> <path style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" d=\"m 13.229167,275.05759 v -3.44507\" id=\"path817-3-6-7\" inkscape:connector-curvature=\"0\" /> <g id=\"g824\" transform=\"matrix(0.63953151,0,0,0.64343684,6.732623,276.71502)\" style=\"fill:#ffcc33\"> <path id=\"path822\" d=\"M 16.07,8 H 15 V 5 C 15,5 15,0 10,0 5,0 5,5 5,5 V 8 H 3.93 A 1.93,1.93 0 0 0 2,9.93 v 8.15 A 1.93,1.93 0 0 0 3.93,20 H 16.07 A 1.93,1.93 0 0 0 18,18.07 V 9.93 A 1.93,1.93 0 0 0 16.07,8 Z M 10,16 a 2,2 0 1 1 2,-2 2,2 0 0 1 -2,2 z M 13,8 H 7 V 5.5 C 7,4 7,2 10,2 c 3,0 3,2 3,3.5 z\" inkscape:connector-curvature=\"0\" /> </g> </g> </svg> "
public static crosshair_locked = " <!-- Created with Inkscape (http://www.inkscape.org/) --> <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\" width=\"100\" height=\"100\" viewBox=\"0 0 26.458333 26.458334\" version=\"1.1\" id=\"svg8\" inkscape:version=\"0.92.5 (2060ec1f9f, 2020-04-08)\" sodipodi:docname=\"crosshair-locked.svg\"> <defs id=\"defs2\" /> <sodipodi:namedview id=\"base\" pagecolor=\"#ffffff\" bordercolor=\"#666666\" borderopacity=\"1.0\" inkscape:pageopacity=\"0.0\" inkscape:pageshadow=\"2\" inkscape:zoom=\"5.6568542\" inkscape:cx=\"27.044982\" inkscape:cy=\"77.667126\" inkscape:document-units=\"px\" inkscape:current-layer=\"layer1\" showgrid=\"false\" units=\"px\" showguides=\"true\" inkscape:guide-bbox=\"true\" inkscape:window-width=\"1920\" inkscape:window-height=\"999\" inkscape:window-x=\"0\" inkscape:window-y=\"0\" inkscape:window-maximized=\"1\" inkscape:snap-global=\"false\"> <sodipodi:guide position=\"13.229167,23.859748\" orientation=\"1,0\" id=\"guide815\" inkscape:locked=\"false\" /> <sodipodi:guide position=\"14.944824,13.229167\" orientation=\"0,1\" id=\"guide817\" inkscape:locked=\"false\" /> </sodipodi:namedview> <metadata id=\"metadata5\"> <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> <g inkscape:label=\"Layer 1\" inkscape:groupmode=\"layer\" id=\"layer1\" transform=\"translate(0,-270.54165)\"> <g id=\"g827\"> <circle r=\"8.8715391\" cy=\"283.77081\" cx=\"13.16302\" id=\"path815\" style=\"fill: none !important;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817\" d=\"M 3.2841366,283.77082 H 1.0418969\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3\" d=\"M 25.405696,283.77082 H 23.286471\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3-6\" d=\"m 13.229167,295.9489 v -2.11763\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> <path inkscape:connector-curvature=\"0\" id=\"path817-3-6-7\" d=\"m 13.229167,275.05759 v -3.44507\" style=\"fill: none !important;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529\" /> </g> <path style=\"fill:#5555ec;fill-opacity:0.98823529;stroke-width:0.6151033\" inkscape:connector-curvature=\"0\" d=\"m 16.850267,281.91543 h -0.65616 v -1.85094 c 0,0 0,-3.08489 -3.066169,-3.08489 -3.066169,0 -3.066169,3.08489 -3.066169,3.08489 v 1.85094 H 9.4056091 a 1.1835412,1.1907685 0 0 0 -1.1835412,1.19077 v 5.02838 a 1.1835412,1.1907685 0 0 0 1.1835412,1.1846 h 7.4446579 a 1.1835412,1.1907685 0 0 0 1.183541,-1.19078 v -5.0222 a 1.1835412,1.1907685 0 0 0 -1.183541,-1.19077 z m -3.722329,4.93583 a 1.2264675,1.233957 0 1 1 1.226468,-1.23395 1.2264675,1.233957 0 0 1 -1.226468,1.23395 z m 1.839702,-4.93583 h -3.679403 v -1.54245 c 0,-0.92546 0,-2.15942 1.839701,-2.15942 1.839702,0 1.839702,1.23396 1.839702,2.15942 z\" id=\"path822\" /> </g> </svg> "
public static crosshair_locked_img = Img.AsImageElement(Svg.crosshair_locked)
public static crosshair_locked_svg() { return new Img(Svg.crosshair_locked, true);}
public static crosshair_locked_ui() { return new FixedUiElement(Svg.crosshair_locked_img);}

View file

@ -82,7 +82,7 @@ export default class Minimap extends BaseUIElement {
doubleClickZoom: this._allowMoving,
keyboard: this._allowMoving,
touchZoom: this._allowMoving,
zoomAnimation: this._allowMoving,
// Disabling this breaks the geojson layer - don't ask me why! zoomAnimation: this._allowMoving,
fadeAnimation: this._allowMoving
});

View file

@ -0,0 +1,21 @@
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import Translations from "../i18n/Translations";
import State from "../../State";
import {FeatureSourceUtils} from "../../Logic/FeatureSource/FeatureSource";
import {Utils} from "../../Utils";
import Combine from "../Base/Combine";
export class ExportDataButton extends Combine {
constructor() {
const t = Translations.t.general.download
const button = new SubtleButton(Svg.floppy_ui(), t.downloadGeojson.Clone().SetClass("font-bold"))
.onClick(() => {
const geojson = FeatureSourceUtils.extractGeoJson(State.state.featurePipeline)
const name = State.state.layoutToUse.data.id;
Utils.offerContentsAsDownloadableFile(JSON.stringify(geojson), `MapComplete_${name}_export_${new Date().toISOString().substr(0,19)}.geojson`);
})
super([button, t.licenseInfo.Clone().SetClass("link-underline")])
}
}

View file

@ -2,11 +2,12 @@ import State from "../../State";
import BackgroundSelector from "./BackgroundSelector";
import LayerSelection from "./LayerSelection";
import Combine from "../Base/Combine";
import {FixedUiElement} from "../Base/FixedUiElement";
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 {ExportDataButton} from "./ExportDataButton";
export default class LayerControlPanel extends ScrollableFullScreen {
@ -14,27 +15,34 @@ export default class LayerControlPanel extends ScrollableFullScreen {
super(LayerControlPanel.GenTitle, LayerControlPanel.GeneratePanel, "layers", isShown);
}
private static GenTitle():BaseUIElement {
private static GenTitle(): BaseUIElement {
return Translations.t.general.layerSelection.title.Clone().SetClass("text-2xl break-words font-bold p-2")
}
private static GeneratePanel() : BaseUIElement {
let layerControlPanel: BaseUIElement = new FixedUiElement("");
private static GeneratePanel(): BaseUIElement {
const elements: BaseUIElement[] = []
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
layerControlPanel = new BackgroundSelector();
layerControlPanel.SetStyle("margin:1em");
layerControlPanel.onClick(() => {
const backgroundSelector = new BackgroundSelector();
backgroundSelector.SetStyle("margin:1em");
backgroundSelector.onClick(() => {
});
elements.push(backgroundSelector)
}
if (State.state.filteredLayers.data.length > 1) {
const layerSelection = new LayerSelection(State.state.filteredLayers);
layerSelection.onClick(() => {
});
layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]);
}
elements.push(new Toggle(
new LayerSelection(State.state.filteredLayers),
undefined,
State.state.filteredLayers.map(layers => layers.length > 1)
))
return layerControlPanel;
elements.push(new Toggle(
new ExportDataButton(),
undefined,
State.state.featureSwitchEnableExport
))
return new Combine(elements).SetClass("flex flex-col")
}
}

View file

@ -74,7 +74,6 @@ export default class LayerSelection extends Combine {
);
}
super(checkboxes)
this.SetStyle("display:flex;flex-direction:column;")

View file

@ -48,7 +48,7 @@ export default class LocationInput extends InputElement<Loc> {
)
})
layer.map(layer => {
this.mapBackground.map(layer => {
const leaflet = map.leafletMap.data
if (leaflet === undefined || layer === undefined) {

View file

@ -36,7 +36,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
.SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2");
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon,
"block w-8 h-8 align-baseline box-content sm:p-0.5")
"block w-8 h-8 align-baseline box-content sm:p-0.5", "width: 2rem;")
))
.SetClass("flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2")

View file

@ -16,31 +16,31 @@ export default class TagRenderingAnswer extends VariableUiElement {
throw "Trying to generate a tagRenderingAnswer without configuration..."
}
super(tagsSource.map(tags => {
if(tags === undefined){
if (tags === undefined) {
return undefined;
}
if(configuration.condition){
if(!configuration.condition.matchesProperties(tags)){
if (configuration.condition) {
if (!configuration.condition.matchesProperties(tags)) {
return undefined;
}
}
const trs = Utils.NoNull(configuration.GetRenderValues(tags));
if(trs.length === 0){
return undefined;
}
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
if(valuesToRender.length === 1){
return valuesToRender[0];
}else if(valuesToRender.length > 1){
return new List(valuesToRender)
}
return undefined;
}).map((element : BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle)))
this.SetClass("flex items-center flex-row text-lg link-underline tag-renering-answer")
const trs = Utils.NoNull(configuration.GetRenderValues(tags));
if (trs.length === 0) {
return undefined;
}
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
if (valuesToRender.length === 1) {
return valuesToRender[0];
} else if (valuesToRender.length > 1) {
return new List(valuesToRender)
}
return undefined;
}).map((element: BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle)))
this.SetClass("flex items-center flex-row text-lg link-underline")
this.SetStyle("word-wrap: anywhere;");
}

View file

@ -146,7 +146,9 @@ export default class ShowDataLayer {
const popup = L.popup({
autoPan: true,
closeOnEscapeKey: true,
closeButton: false
closeButton: false,
autoPanPaddingTopLeft: [15,15],
}, leafletLayer);
leafletLayer.bindPopup(popup);

View file

@ -448,5 +448,12 @@ export class Utils {
b: parseInt(hex.substr(5, 2), 16),
}
}
public static setDefaults(options, defaults){
for (let key in defaults){
if (!(key in options)) options[key] = defaults[key];
}
return options;
}
}

View file

@ -508,7 +508,8 @@
}
}
]
}
},
"level"
],
"icon": {
"render": {

View file

@ -25,9 +25,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8284271"
inkscape:cx="67.47399"
inkscape:cy="29.788021"
inkscape:zoom="5.6568542"
inkscape:cx="27.044982"
inkscape:cy="77.667126"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
@ -68,40 +68,39 @@
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-270.54165)">
<circle
style="fill:none;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529"
id="path815"
cx="13.16302"
cy="283.77081"
r="8.8715391" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="M 3.2841366,283.77082 H 1.0418969"
id="path817"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="M 25.405696,283.77082 H 23.286471"
id="path817-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="m 13.229167,295.9489 v -2.11763"
id="path817-3-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="m 13.229167,275.05759 v -3.44507"
id="path817-3-6-7"
inkscape:connector-curvature="0" />
<g
id="g824"
transform="matrix(0.63953151,0,0,0.64343684,6.732623,276.71502)"
style="fill:#ffcc33">
id="g827">
<circle
r="8.8715391"
cy="283.77081"
cx="13.16302"
id="path815"
style="fill:none;fill-opacity:1;stroke:#5555ec;stroke-width:2.64583335;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529" />
<path
id="path822"
d="M 16.07,8 H 15 V 5 C 15,5 15,0 10,0 5,0 5,5 5,5 V 8 H 3.93 A 1.93,1.93 0 0 0 2,9.93 v 8.15 A 1.93,1.93 0 0 0 3.93,20 H 16.07 A 1.93,1.93 0 0 0 18,18.07 V 9.93 A 1.93,1.93 0 0 0 16.07,8 Z M 10,16 a 2,2 0 1 1 2,-2 2,2 0 0 1 -2,2 z M 13,8 H 7 V 5.5 C 7,4 7,2 10,2 c 3,0 3,2 3,3.5 z"
inkscape:connector-curvature="0" />
inkscape:connector-curvature="0"
id="path817"
d="M 3.2841366,283.77082 H 1.0418969"
style="fill:none;stroke:#5555ec;stroke-width:2.09723878;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3"
d="M 25.405696,283.77082 H 23.286471"
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3-6"
d="m 13.229167,295.9489 v -2.11763"
style="fill:none;stroke:#5555ec;stroke-width:2.11666679;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
<path
inkscape:connector-curvature="0"
id="path817-3-6-7"
d="m 13.229167,275.05759 v -3.44507"
style="fill:none;stroke:#5555ec;stroke-width:2.11666668;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" />
</g>
<path
style="fill:#5555ec;fill-opacity:0.98823529;stroke-width:0.6151033"
inkscape:connector-curvature="0"
d="m 16.850267,281.91543 h -0.65616 v -1.85094 c 0,0 0,-3.08489 -3.066169,-3.08489 -3.066169,0 -3.066169,3.08489 -3.066169,3.08489 v 1.85094 H 9.4056091 a 1.1835412,1.1907685 0 0 0 -1.1835412,1.19077 v 5.02838 a 1.1835412,1.1907685 0 0 0 1.1835412,1.1846 h 7.4446579 a 1.1835412,1.1907685 0 0 0 1.183541,-1.19078 v -5.0222 a 1.1835412,1.1907685 0 0 0 -1.183541,-1.19077 z m -3.722329,4.93583 a 1.2264675,1.233957 0 1 1 1.226468,-1.23395 1.2264675,1.233957 0 0 1 -1.226468,1.23395 z m 1.839702,-4.93583 h -3.679403 v -1.54245 c 0,-0.92546 0,-2.15942 1.839701,-2.15942 1.839702,0 1.839702,1.23396 1.839702,2.15942 z"
id="path822" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -162,6 +162,18 @@
"license": "CC0; trivial",
"sources": []
},
{
"authors": [],
"path": "crosshair-empty.svg",
"license": "CC0; trivial",
"sources": []
},
{
"authors": [],
"path": "crosshair-locked.svg",
"license": "CC0; trivial",
"sources": []
},
{
"authors": [
"Dave Gandy"
@ -204,7 +216,7 @@
"license": "CC0",
"sources": [
"https://commons.wikimedia.org/wiki/File:Media-floppy.svg",
" http://tango.freedesktop.org/Tango_Desktop_Project"
"http://tango.freedesktop.org/Tango_Desktop_Project"
]
},
{
@ -582,5 +594,15 @@
"sources": [
"https://www.mapillary.com/"
]
},
{
"authors": [
"The Tango! Desktop Project"
],
"path": "floppy.svg",
"license": "CC0",
"sources": [
"https://commons.wikimedia.org/wiki/File:Media-floppy.svg"
]
}
]

View file

@ -123,5 +123,43 @@
"all_tags": {
"#": "Prints all the tags",
"render": "{all_tags()}"
},
"level": {
"question": {
"nl": "Op welke verdieping bevindt dit punt zich?",
"en": "On what level is this feature located?"
},
"render": {
"en": "Located on the {level}th floor",
"nl": "Bevindt zich op de {level}de verdieping"
},
"freeform": {
"key": "level",
"type": "float"
},
"mappings": [
{
"if": "location=underground",
"then": {
"en": "Located underground",
"nl": "Bevindt zich ondergronds"
},
"hideInAnswer": true
},
{
"if": "level=0",
"then": {
"en": "Located on the ground floor",
"nl": "Bevindt zich gelijkvloers"
}
},
{
"if": "level=1",
"then": {
"en": "Located on the first floor",
"nl": "Bevindt zich op de eerste verdieping"
}
}
]
}
}

View file

@ -374,7 +374,7 @@
"en": "Is there a website with more information about this artwork?",
"nl": "Op welke website kan men meer informatie vinden over dit kunstwerk?",
"fr": "Sur quel site web pouvons-nous trouver plus d'informations sur cette œuvre d'art?",
"de": "Auf welcher Website gibt es mehr Informationen über dieses Kunstwerk?",
"de": "Gibt es eine Website mit weiteren Informationen über dieses Kunstwerk?",
"it": "Esiste un sito web con maggiori informazioni su questopera?",
"ru": "Есть ли сайт с более подробной информацией об этой работе?",
"ja": "この作品についての詳しい情報はどのウェブサイトにありますか?",

View file

@ -10,7 +10,8 @@
"ja",
"fr",
"zh_Hant",
"nb_NO"
"nb_NO",
"de"
],
"title": {
"en": "Bicycle libraries",
@ -20,7 +21,8 @@
"ja": "自転車ライブラリ",
"fr": "Vélothèques",
"zh_Hant": "單車圖書館",
"nb_NO": "Sykkelbibliotek"
"nb_NO": "Sykkelbibliotek",
"de": "Fahrradbibliothek"
},
"description": {
"nl": "Een fietsbibliotheek is een plaats waar men een fiets kan lenen, vaak voor een klein bedrag per jaar. Een typisch voorbeeld zijn kinderfietsbibliotheken, waar men een fiets op maat van het kind kan lenen. Is het kind de fiets ontgroeid, dan kan het te kleine fietsje omgeruild worden voor een grotere.",

View file

@ -427,7 +427,8 @@
"it": "Sito web ufficiale: <a href='{website}'>{website}</a>",
"ja": "公式Webサイト: <a href='{website}'>{website}</a>",
"nb_NO": "Offisiell nettside: <a href='{website}'>{website}</a>",
"zh_Hant": "官方網站:<a href='{website}'>{website}</a>"
"zh_Hant": "官方網站:<a href='{website}'>{website}</a>",
"nl": "Officiële website: : <a href='{website}'>{website}</a>"
},
"freeform": {
"type": "url",
@ -823,7 +824,8 @@
"then": {
"en": "Anyone can use this dump station",
"ja": "誰でもこのゴミ捨て場を使用できます",
"it": "Chiunque può farne uso"
"it": "Chiunque può farne uso",
"ru": "Любой может воспользоваться этой станцией утилизации"
},
"hideInAnswer": true
},
@ -836,7 +838,8 @@
"then": {
"en": "Anyone can use this dump station",
"ja": "誰でもこのゴミ捨て場を使用できます",
"it": "Chiunque può farne uso"
"it": "Chiunque può farne uso",
"ru": "Любой может воспользоваться этой станцией утилизации"
}
}
]
@ -845,12 +848,14 @@
"render": {
"en": "This station is part of network {network}",
"ja": "このステーションはネットワーク{network}の一部です",
"it": "Questo luogo è parte della rete {network}"
"it": "Questo luogo è parte della rete {network}",
"ru": "Эта станция - часть сети {network}"
},
"question": {
"en": "What network is this place a part of? (skip if none)",
"ja": "ここは何のネットワークの一部ですか?(なければスキップ)",
"it": "Di quale rete fa parte questo luogo? (se non fa parte di nessuna rete, salta)"
"it": "Di quale rete fa parte questo luogo? (se non fa parte di nessuna rete, salta)",
"ru": "К какой сети относится эта станция? (пропустите, если неприменимо)"
},
"freeform": {
"key": "network"

View file

@ -14,7 +14,8 @@
"ru": "Карта зарядных станций по всему миру",
"ja": "充電ステーションの世界地図",
"zh_Hant": "全世界的充電站地圖",
"it": "Una mappa mondiale delle stazioni di ricarica"
"it": "Una mappa mondiale delle stazioni di ricarica",
"nl": "Een wereldwijde kaart van oplaadpunten"
},
"description": {
"en": "On this open map, one can find and mark information about charging stations",
@ -229,11 +230,12 @@
"ja": "{network}",
"zh_Hant": "{network}",
"nb_NO": "{network}",
"it": "{network}"
"it": "{network}",
"nl": "{network}"
},
"question": {
"en": "What network of this charging station under?",
"ru": "К какой сети относится эта станция?",
"ru": "К какой сети относится эта зарядная станция?",
"ja": "この充電ステーションの運営チェーンはどこですか?",
"zh_Hant": "充電站所屬的網路是?",
"it": "A quale rete appartiene questa stazione di ricarica?"
@ -253,7 +255,8 @@
"ru": "Не является частью более крупной сети",
"ja": "大規模な運営チェーンの一部ではない",
"zh_Hant": "不屬於大型網路",
"it": "Non appartiene a una rete"
"it": "Non appartiene a una rete",
"nl": "Maakt geen deel uit van een netwerk"
}
},
{
@ -267,7 +270,8 @@
"ru": "AeroVironment",
"ja": "AeroVironment",
"zh_Hant": "AeroVironment",
"it": "AeroVironment"
"it": "AeroVironment",
"nl": "AeroVironment"
}
},
{
@ -281,7 +285,8 @@
"ru": "Blink",
"ja": "Blink",
"zh_Hant": "Blink",
"it": "Blink"
"it": "Blink",
"nl": "Blink"
}
},
{
@ -295,7 +300,8 @@
"ru": "eVgo",
"ja": "eVgo",
"zh_Hant": "eVgo",
"it": "eVgo"
"it": "eVgo",
"nl": "eVgo"
}
}
]

View file

@ -171,14 +171,16 @@
"en": "Climbing club",
"nl": "Klimclub",
"ja": "クライミングクラブ",
"nb_NO": "Klatreklubb"
"nb_NO": "Klatreklubb",
"ru": "Клуб скалолазания"
},
"description": {
"de": "Ein Kletterverein",
"nl": "Een klimclub",
"en": "A climbing club",
"ja": "クライミングクラブ",
"nb_NO": "En klatreklubb"
"nb_NO": "En klatreklubb",
"ru": "Клуб скалолазания"
}
},
{
@ -241,7 +243,8 @@
"description": {
"de": "Eine Kletterhalle",
"en": "A climbing gym",
"ja": "クライミングジム"
"ja": "クライミングジム",
"nl": "Een klimzaal"
},
"tagRenderings": [
"images",
@ -484,7 +487,8 @@
"presets": [
{
"title": {
"en": "Climbing route"
"en": "Climbing route",
"nl": "Klimroute"
},
"tags": [
"sport=climbing",
@ -790,7 +794,8 @@
"fr": "<strong>{name}</strong>",
"id": "<strong>{name}</strong>",
"ru": "<strong>{name}</strong>",
"ja": "<strong>{name}</strong>"
"ja": "<strong>{name}</strong>",
"nl": "<strong>{name}</strong>"
},
"condition": "name~*"
},
@ -897,7 +902,8 @@
"en": "Is there a (unofficial) website with more informations (e.g. topos)?",
"de": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?",
"ja": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?",
"nl": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
"nl": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?",
"ru": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
},
"condition": {
"and": [
@ -976,7 +982,8 @@
{
"if": "access=members",
"then": {
"en": "Only club members"
"en": "Only club members",
"ru": "Только членам клуба"
}
},
{

View file

@ -27,7 +27,8 @@
"ja",
"zh_Hant",
"nb_NO",
"it"
"it",
"ru"
],
"startLat": 51.2095,
"startZoom": 14,
@ -222,7 +223,8 @@
"en": "All streets",
"ja": "すべての道路",
"nb_NO": "Alle gater",
"it": "Tutte le strade"
"it": "Tutte le strade",
"ru": "Все улицы"
},
"description": {
"nl": "Laag waar je een straat als fietsstraat kan markeren",
@ -247,7 +249,8 @@
"nl": "Straat",
"en": "Street",
"ja": "ストリート",
"it": "Strada"
"it": "Strada",
"ru": "Улица"
},
"mappings": [
{

View file

@ -15,7 +15,8 @@
"fr": "Cette carte affiche les points d'accès public à de l'eau potable, et permet d'en ajouter facilement",
"ja": "この地図には、一般にアクセス可能な飲料水スポットが示されており、簡単に追加することができる",
"zh_Hant": "在這份地圖上,公共可及的飲水點可以顯示出來,也能輕易的增加",
"it": "Questa mappa mostra tutti i luoghi in cui è disponibile acqua potabile ed è possibile aggiungerne di nuovi"
"it": "Questa mappa mostra tutti i luoghi in cui è disponibile acqua potabile ed è possibile aggiungerne di nuovi",
"ru": "На этой карте показываются и могут быть легко добавлены общедоступные точки питьевой воды"
},
"language": [
"en",

View file

@ -165,7 +165,8 @@
"nl": "Ligt de tuin in zon/half schaduw of schaduw?",
"en": "Is the garden shaded or sunny?",
"ja": "庭は日陰ですか、日当たりがいいですか?",
"it": "Il giardino è al sole o in ombra?"
"it": "Il giardino è al sole o in ombra?",
"ru": "Сад расположен на солнечной стороне или в тени?"
}
},
{
@ -185,7 +186,8 @@
"nl": "Er is een regenton",
"en": "There is a rain barrel",
"ja": "雨樽がある",
"it": "C'è un contenitore per raccogliere la pioggia"
"it": "C'è un contenitore per raccogliere la pioggia",
"ru": "Есть бочка с дождевой водой"
}
},
{
@ -198,7 +200,8 @@
"nl": "Er is geen regenton",
"en": "There is no rain barrel",
"ja": "雨樽はありません",
"it": "Non c'è un contenitore per raccogliere la pioggia"
"it": "Non c'è un contenitore per raccogliere la pioggia",
"ru": "Нет бочки с дождевой водой"
}
}
]
@ -263,7 +266,8 @@
"nl": "Wat voor planten staan hier?",
"en": "What kinds of plants grow here?",
"ja": "ここではどんな植物が育つんですか?",
"it": "Che tipi di piante sono presenti qui?"
"it": "Che tipi di piante sono presenti qui?",
"ru": "Какие виды растений обитают здесь?"
},
"mappings": [
{
@ -310,13 +314,15 @@
"nl": "Meer details: {description}",
"en": "More details: {description}",
"ja": "詳細情報: {description}",
"it": "Maggiori dettagli: {description}"
"it": "Maggiori dettagli: {description}",
"ru": "Подробнее: {description}"
},
"question": {
"nl": "Aanvullende omschrijving van de tuin (indien nodig, en voor zover nog niet omschreven hierboven)",
"en": "Extra describing info about the garden (if needed and not yet described above)",
"ja": "庭園に関する追加の説明情報(必要な場合でまだ上記に記載されていない場合)",
"it": "Altre informazioni per descrivere il giardino (se necessarie e non riportate qui sopra)"
"it": "Altre informazioni per descrivere il giardino (se necessarie e non riportate qui sopra)",
"ru": "Дополнительная информация о саде (если требуется или еще не указана выше)"
},
"freeform": {
"key": "description",

View file

@ -118,7 +118,8 @@
"nl": "Wat is de website van deze frituur?",
"fr": "Quel est le site web de cette friterie?",
"ja": "このお店のホームページは何ですか?",
"it": "Qual è il sito web di questo negozio?"
"it": "Qual è il sito web di questo negozio?",
"ru": "Какой веб-сайт у этого магазина?"
},
"freeform": {
"key": "website",
@ -136,7 +137,8 @@
"fr": "Quel est le numéro de téléphone de cette friterie?",
"ja": "電話番号は何番ですか?",
"nb_NO": "Hva er telefonnummeret?",
"it": "Qual è il numero di telefono?"
"it": "Qual è il numero di telefono?",
"ru": "Какой телефон?"
},
"freeform": {
"key": "phone",
@ -275,7 +277,8 @@
"then": {
"nl": "Je mag <b>geen</b> eigen containers meenemen om je bestelling in mee te nemen",
"en": "Bringing your own container is <b>not allowed</b>",
"ja": "独自の容器を持参することは<b>できません</b>"
"ja": "独自の容器を持参することは<b>できません</b>",
"ru": "Приносить свою тару <b>не разрешено</b>"
}
},
{

View file

@ -3,12 +3,14 @@
"title": {
"en": "Hydrants, Extinguishers, Fire stations, and Ambulance stations.",
"ja": "消火栓、消火器、消防署、救急ステーションです。",
"zh_Hant": "消防栓、滅火器、消防隊、以及急救站。"
"zh_Hant": "消防栓、滅火器、消防隊、以及急救站。",
"ru": "Пожарные гидранты, огнетушители, пожарные станции и станции скорой помощи."
},
"shortDescription": {
"en": "Map to show hydrants, extinguishers, fire stations, and ambulance stations.",
"ja": "消火栓、消火器、消防署消火栓、消火器、消防署、および救急ステーションを表示します。",
"zh_Hant": "顯示消防栓、滅火器、消防隊與急救站的地圖。"
"zh_Hant": "顯示消防栓、滅火器、消防隊與急救站的地圖。",
"ru": "Карта пожарных гидрантов, огнетушителей, пожарных станций и станций скорой помощи."
},
"description": {
"en": "On this map you can find and update hydrants, fire stations, ambulance stations, and extinguishers in your favorite neighborhoods. \n\nYou can track your precise location (mobile only) and select layers that are relevant for you in the bottom left corner. You can also use this tool to add or edit pins (points of interest) to the map and provide additional details by answering available questions. \n\nAll changes you make will automatically be saved in the global database of OpenStreetMap and can be freely re-used by others.",
@ -39,7 +41,8 @@
"en": "Map of hydrants",
"ja": "消火栓の地図",
"zh_Hant": "消防栓地圖",
"nb_NO": "Kart over brannhydranter"
"nb_NO": "Kart over brannhydranter",
"ru": "Карта пожарных гидрантов"
},
"minzoom": 14,
"source": {
@ -61,19 +64,22 @@
"en": "Map layer to show fire hydrants.",
"ja": "消火栓を表示するマップレイヤ。",
"zh_Hant": "顯示消防栓的地圖圖層。",
"nb_NO": "Kartlag for å vise brannhydranter."
"nb_NO": "Kartlag for å vise brannhydranter.",
"ru": "Слой карты, отображающий пожарные гидранты."
},
"tagRenderings": [
{
"question": {
"en": "What color is the hydrant?",
"ja": "消火栓の色は何色ですか?",
"nb_NO": "Hvilken farge har brannhydranten?"
"nb_NO": "Hvilken farge har brannhydranten?",
"ru": "Какого цвета гидрант?"
},
"render": {
"en": "The hydrant color is {colour}",
"ja": "消火栓の色は{color}です",
"nb_NO": "Brannhydranter er {colour}"
"nb_NO": "Brannhydranter er {colour}",
"ru": "Цвет гидранта {colour}"
},
"freeform": {
"key": "colour"
@ -87,7 +93,8 @@
},
"then": {
"en": "The hydrant color is unknown.",
"ja": "消火栓の色は不明です。"
"ja": "消火栓の色は不明です。",
"ru": "Цвет гидранта не определён."
},
"hideInAnswer": true
},
@ -99,7 +106,8 @@
},
"then": {
"en": "The hydrant color is yellow.",
"ja": "消火栓の色は黄色です。"
"ja": "消火栓の色は黄色です。",
"ru": "Гидрант жёлтого цвета."
}
},
{
@ -111,7 +119,8 @@
"then": {
"en": "The hydrant color is red.",
"ja": "消火栓の色は赤です。",
"it": "L'idrante è rosso."
"it": "L'idrante è rosso.",
"ru": "Гидрант красного цвета."
}
}
]
@ -120,7 +129,8 @@
"question": {
"en": "What type of hydrant is it?",
"ja": "どんな消火栓なんですか?",
"it": "Di che tipo è questo idrante?"
"it": "Di che tipo è questo idrante?",
"ru": "К какому типу относится этот гидрант?"
},
"freeform": {
"key": "fire_hydrant:type"
@ -141,7 +151,8 @@
"then": {
"en": "The hydrant type is unknown.",
"ja": "消火栓の種類は不明です。",
"it": "Il tipo di idrante è sconosciuto."
"it": "Il tipo di idrante è sconosciuto.",
"ru": "Тип гидранта не определён."
},
"hideInAnswer": true
},
@ -214,7 +225,8 @@
},
"then": {
"en": "The hydrant is (fully or partially) working.",
"ja": "消火栓は(完全にまたは部分的に)機能しています。"
"ja": "消火栓は(完全にまたは部分的に)機能しています。",
"ru": "Гидрант (полностью или частично) в рабочем состоянии."
}
},
{
@ -238,7 +250,8 @@
},
"then": {
"en": "The hydrant has been removed.",
"ja": "消火栓が撤去されました。"
"ja": "消火栓が撤去されました。",
"ru": "Гидрант демонтирован."
}
}
]
@ -282,7 +295,8 @@
"name": {
"en": "Map of fire extinguishers.",
"ja": "消火器の地図です。",
"nb_NO": "Kart over brannhydranter"
"nb_NO": "Kart over brannhydranter",
"ru": "Карта огнетушителей."
},
"minzoom": 14,
"source": {
@ -304,17 +318,20 @@
"en": "Map layer to show fire hydrants.",
"ja": "消火栓を表示するマップレイヤ。",
"zh_Hant": "顯示消防栓的地圖圖層。",
"nb_NO": "Kartlag for å vise brannslokkere."
"nb_NO": "Kartlag for å vise brannslokkere.",
"ru": "Слой карты, отображающий огнетушители."
},
"tagRenderings": [
{
"render": {
"en": "Location: {location}",
"ja": "場所:{location}"
"ja": "場所:{location}",
"ru": "Местоположение: {location}"
},
"question": {
"en": "Where is it positioned?",
"ja": "どこにあるんですか?"
"ja": "どこにあるんですか?",
"ru": "Где это расположено?"
},
"mappings": [
{
@ -325,7 +342,8 @@
},
"then": {
"en": "Found indoors.",
"ja": "屋内にある。"
"ja": "屋内にある。",
"ru": "Внутри."
}
},
{
@ -336,7 +354,8 @@
},
"then": {
"en": "Found outdoors.",
"ja": "屋外にある。"
"ja": "屋外にある。",
"ru": "Снаружи."
}
}
],
@ -367,11 +386,13 @@
"title": {
"en": "Fire extinguisher",
"ja": "消火器",
"nb_NO": "Brannslukker"
"nb_NO": "Brannslukker",
"ru": "Огнетушитель"
},
"description": {
"en": "A fire extinguisher is a small, portable device used to stop a fire",
"ja": "消火器は、火災を止めるために使用される小型で携帯可能な装置である"
"ja": "消火器は、火災を止めるために使用される小型で携帯可能な装置である",
"ru": "Огнетушитель - небольшое переносное устройство для тушения огня"
}
}
],
@ -383,7 +404,8 @@
"en": "Map of fire stations",
"ja": "消防署の地図",
"nb_NO": "Kart over brannstasjoner",
"it": "Mappa delle caserme dei vigili del fuoco"
"it": "Mappa delle caserme dei vigili del fuoco",
"ru": "Карта пожарных частей"
},
"minzoom": 12,
"source": {
@ -406,7 +428,8 @@
"description": {
"en": "Map layer to show fire stations.",
"ja": "消防署を表示するためのマップレイヤ。",
"it": "Livello che mostra le caserme dei vigili del fuoco."
"it": "Livello che mostra le caserme dei vigili del fuoco.",
"ru": "Слой карты, отображающий пожарные части."
},
"tagRenderings": [
{
@ -416,13 +439,14 @@
"question": {
"en": "What is the name of this fire station?",
"ja": "この消防署の名前は何ですか?",
"ru": "Как называется пожарная часть?",
"ru": "Как называется эта пожарная часть?",
"it": "Come si chiama questa caserma dei vigili del fuoco?"
},
"render": {
"en": "This station is called {name}.",
"ja": "このステーションの名前は{name}です。",
"it": "Questa caserma si chiama {name}."
"it": "Questa caserma si chiama {name}.",
"ru": "Эта часть называется {name}."
}
},
{
@ -432,24 +456,28 @@
"question": {
"en": " What is the street name where the station located?",
"ja": " 救急ステーションの所在地はどこですか?",
"it": " Qual è il nome della via in cui si trova la caserma?"
"it": " Qual è il nome della via in cui si trova la caserma?",
"ru": " По какому адресу расположена эта часть?"
},
"render": {
"en": "This station is along a highway called {addr:street}.",
"ja": "{addr:street} 沿いにあります。"
"ja": "{addr:street} 沿いにあります。",
"ru": "Часть расположена вдоль шоссе {addr:street}."
}
},
{
"question": {
"en": "Where is the station located? (e.g. name of neighborhood, villlage, or town)",
"ja": "このステーションの住所は?(例: 地区、村、または町の名称)"
"ja": "このステーションの住所は?(例: 地区、村、または町の名称)",
"ru": "Где расположена часть? (напр., название населённого пункта)"
},
"freeform": {
"key": "addr:place"
},
"render": {
"en": "This station is found within {addr:place}.",
"ja": "このステーションは{addr:place}にあります。"
"ja": "このステーションは{addr:place}にあります。",
"ru": "Эта часть расположена в {addr:place}."
}
},
{
@ -560,7 +588,8 @@
],
"title": {
"en": "Fire station",
"ja": "消防署"
"ja": "消防署",
"ru": "Пожарная часть"
},
"description": {
"en": "A fire station is a place where the fire trucks and firefighters are located when not in operation.",
@ -573,7 +602,8 @@
"id": "ambulancestation",
"name": {
"en": "Map of ambulance stations",
"ja": "救急ステーションの地図"
"ja": "救急ステーションの地図",
"ru": "Карта станций скорой помощи"
},
"minzoom": 12,
"source": {
@ -602,11 +632,12 @@
"question": {
"en": "What is the name of this ambulance station?",
"ja": "この救急ステーションの名前は何ですか?",
"ru": "Как называется станция скорой помощи?"
"ru": "Как называется эта станция скорой помощи?"
},
"render": {
"en": "This station is called {name}.",
"ja": "このステーションの名前は{name}です。"
"ja": "このステーションの名前は{name}です。",
"ru": "Эта станция называется {name}."
}
},
{
@ -615,17 +646,20 @@
},
"question": {
"en": " What is the street name where the station located?",
"ja": " 救急ステーションの所在地はどこですか?"
"ja": " 救急ステーションの所在地はどこですか?",
"ru": " По какому адресу расположена эта станция?"
},
"render": {
"en": "This station is along a highway called {addr:street}.",
"ja": "{addr:street} 沿いにあります。"
"ja": "{addr:street} 沿いにあります。",
"ru": "Эта станция расположена вдоль шоссе {addr:street}."
}
},
{
"question": {
"en": "Where is the station located? (e.g. name of neighborhood, villlage, or town)",
"ja": "このステーションの住所は?(例: 地区、村、または町の名称)"
"ja": "このステーションの住所は?(例: 地区、村、または町の名称)",
"ru": "Где расположена станция? (напр., название населённого пункта)"
},
"freeform": {
"key": "addr:place"
@ -735,7 +769,8 @@
},
"description": {
"en": "Add an ambulance station to the map",
"ja": "救急ステーション(消防署)をマップに追加する"
"ja": "救急ステーション(消防署)をマップに追加する",
"ru": "Добавить станцию скорой помощи на карту"
}
}
],

View file

@ -5,7 +5,8 @@
"nl": "Een kaart met Kaarten",
"fr": "Carte des cartes",
"ja": "マップのマップ",
"zh_Hant": "地圖的地圖"
"zh_Hant": "地圖的地圖",
"ru": "Карта карт"
},
"shortDescription": {
"en": "This theme shows all (touristic) maps that OpenStreetMap knows of",
@ -26,7 +27,8 @@
"nl",
"fr",
"ja",
"zh_Hant"
"zh_Hant",
"ru"
],
"maintainer": "MapComplete",
"icon": "./assets/themes/maps/logo.svg",

View file

@ -56,7 +56,7 @@
"tagRenderings": [
{
"render": {
"en": "The power output of this wind turbine is {canonical(generator:output:electricity)}."
"en": "The power output of this wind turbine is {generator:output:electricity}."
},
"question": {
"en": "What is the power output of this wind turbine? (e.g. 2.3 MW)"
@ -79,7 +79,7 @@
},
{
"render": {
"en": "The total height (including rotor radius) of this wind turbine is {canonical(height)}."
"en": "The total height (including rotor radius) of this wind turbine is {height} metres."
},
"question": {
"en": "What is the total height of this wind turbine (including rotor radius), in metres?"
@ -91,7 +91,7 @@
},
{
"render": {
"en": "The rotor diameter of this wind turbine is {canonical(rotor:diameter)}."
"en": "The rotor diameter of this wind turbine is {rotor:diameter} metres."
},
"question": {
"en": "What is the rotor diameter of this wind turbine, in metres?"
@ -129,7 +129,7 @@
}
],
"units": [
{
{
"appliesToKey": [
"generator:output:electricity"
],
@ -183,7 +183,8 @@
},
{
"appliesToKey": [
"height","rotor:diameter"
"height",
"rotor:diameter"
],
"applicableUnits": [
{

View file

@ -20,7 +20,8 @@
"fr": "Crée un thème personnalisé basé sur toutes les couches disponibles de tous les thèmes",
"de": "Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen",
"ja": "すべてのテーマの使用可能なすべてのレイヤーに基づいて個人用テーマを作成する",
"zh_Hant": "從所有可用的主題圖層創建個人化主題"
"zh_Hant": "從所有可用的主題圖層創建個人化主題",
"ru": "Создать персональную тему на основе доступных слоёв тем"
},
"language": [
"en",
@ -31,7 +32,8 @@
"fr",
"de",
"ja",
"zh_Hant"
"zh_Hant",
"ru"
],
"maintainer": "MapComplete",
"icon": "./assets/svg/addSmall.svg",

View file

@ -5,28 +5,32 @@
"en": "Playgrounds",
"fr": "Aires de jeux",
"ja": "遊び場",
"zh_Hant": "遊樂場"
"zh_Hant": "遊樂場",
"ru": "Игровые площадки"
},
"shortDescription": {
"nl": "Een kaart met speeltuinen",
"en": "A map with playgrounds",
"fr": "Une carte des aires de jeux",
"ja": "遊び場のある地図",
"zh_Hant": "遊樂場的地圖"
"zh_Hant": "遊樂場的地圖",
"ru": "Карта игровых площадок"
},
"description": {
"nl": "Op deze kaart vind je speeltuinen en kan je zelf meer informatie en foto's toevoegen",
"en": "On this map, you find playgrounds and can add more information",
"fr": "Cette carte affiche les aires de jeux et permet d'ajouter plus d'informations",
"ja": "この地図では遊び場を見つけ情報を追加することができます",
"zh_Hant": "在這份地圖上,你可以尋找遊樂場以及其相關資訊"
"zh_Hant": "在這份地圖上,你可以尋找遊樂場以及其相關資訊",
"ru": "На этой карте можно найти игровые площадки и добавить дополнительную информацию"
},
"language": [
"nl",
"en",
"fr",
"ja",
"zh_Hant"
"zh_Hant",
"ru"
],
"maintainer": "",
"icon": "./assets/themes/playgrounds/playground.svg",

View file

@ -4,7 +4,8 @@
"en": "Open Shop Map",
"fr": "Carte des magasins",
"ja": "オープン ショップ マップ",
"zh_Hant": "開放商店地圖"
"zh_Hant": "開放商店地圖",
"ru": "Открыть карту магазинов"
},
"shortDescription": {
"en": "An editable map with basic shop information",
@ -94,7 +95,8 @@
"en": "A shop",
"fr": "Un magasin",
"ja": "ショップ",
"nl": "Een winkel"
"nl": "Een winkel",
"ru": "Магазин"
},
"tagRenderings": [
"images",
@ -102,7 +104,7 @@
"question": {
"en": "What is the name of this shop?",
"fr": "Qu'est-ce que le nom de ce magasin?",
"ru": "Как называется магазин?",
"ru": "Как называется этот магазин?",
"ja": "このお店の名前は何ですか?",
"nl": "Wat is de naam van deze winkel?"
},
@ -120,7 +122,8 @@
"question": {
"en": "What does this shop sell?",
"fr": "Que vends ce magasin ?",
"ja": "このお店では何を売っていますか?"
"ja": "このお店では何を売っていますか?",
"ru": "Что продаётся в этом магазине?"
},
"freeform": {
"key": "shop"
@ -232,7 +235,8 @@
"en": "What is the phone number?",
"fr": "Quel est le numéro de téléphone ?",
"ja": "電話番号は何番ですか?",
"nl": "Wat is het telefoonnummer?"
"nl": "Wat is het telefoonnummer?",
"ru": "Какой телефон?"
},
"freeform": {
"key": "phone",
@ -252,7 +256,8 @@
"en": "What is the website of this shop?",
"fr": "Quel est le site internet de ce magasin ?",
"ja": "このお店のホームページは何ですか?",
"nl": "Wat is de website van deze winkel?"
"nl": "Wat is de website van deze winkel?",
"ru": "Какой веб-сайт у этого магазина?"
},
"freeform": {
"key": "website",
@ -270,7 +275,8 @@
"question": {
"en": "What is the email address of this shop?",
"fr": "Quelle est l'adresse électronique de ce magasin ?",
"ja": "このお店のメールアドレスは何ですか?"
"ja": "このお店のメールアドレスは何ですか?",
"ru": "Каков адрес электронной почты этого магазина?"
},
"freeform": {
"key": "email",
@ -288,7 +294,8 @@
"en": "What are the opening hours of this shop?",
"fr": "Quels sont les horaires d'ouverture de ce magasin ?",
"ja": "この店の営業時間は何時から何時までですか?",
"nl": "Wat zijn de openingsuren van deze winkel?"
"nl": "Wat zijn de openingsuren van deze winkel?",
"ru": "Каковы часы работы этого магазина?"
},
"freeform": {
"key": "opening_hours",

View file

@ -28,7 +28,7 @@
"builtin": "play_forest",
"override": {
"source": {
"geoJson": "https://pietervdvn.github.io/speelplekken_cache/speelplekken_{z}_{x}_{y}.geojson",
"geoJson": "https://pietervdvn.github.io/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
"geoJsonZoomLevel": 14,
"isOsmCache": true
},
@ -105,11 +105,31 @@
{
"builtin": "slow_roads",
"override": {
"+tagRenderings": [
{
"question": "Is dit een publiek toegankelijk pad?",
"mappings": [
{
"if": "access=private",
"then": "Dit is een privaat pad"
},
{
"if": "access=no",
"then": "Dit is een privaat pad",
"hideInAnswer": true
},
{
"if": "access=permissive",
"then": "Dit pad is duidelijk in private eigendom, maar er hangen geen verbodsborden dus mag men erover"
}
]
}
],
"calculatedTags": [
"_part_of_walking_routes=Array.from(new Set(feat.memberships().map(r => \"<a href='#relation/\"+r.relation.id+\"'>\" + r.relation.tags.name + \"</a>\"))).join(', ')",
"_is_shadowed=feat.overlapWith('shadow').length > 0 ? 'yes': ''"
],
"minzoom": 9,
"minzoom": 18,
"source": {
"geoJsonLocal": "http://127.0.0.1:8080/speelplekken_{layer}_{z}_{x}_{y}.geojson",
"geoJson": "https://pietervdvn.github.io/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
@ -235,21 +255,21 @@
"maxZoom": 16,
"minNeededElements": 100
},
"roamingRenderings": [
{
"render": "Maakt deel uit van {_part_of_walking_routes}",
"condition": "_part_of_walking_routes~*"
},
{
"render": "<a href='{video}' target='blank'>Een kinder-reportage vinden jullie hier<a/>",
"freeform": {
"key": "video",
"type": "url"
},
"question": "Wat is de link naar de video-reportage?"
}
],
"overrideAll": {
"+tagRenderings": [
{
"render": "Maakt deel uit van {_part_of_walking_routes}",
"condition": "_part_of_walking_routes~*"
},
{
"render": "<a href='{video}' target='blank'>Een kinder-reportage vinden jullie hier<a/>",
"freeform": {
"key": "video",
"type": "url"
},
"question": "Wat is de link naar de video-reportage?"
}
],
"isShown": {
"render": "yes",
"mappings": [

View file

@ -5,14 +5,16 @@
"fr": "Terrains de sport",
"en": "Sport pitches",
"ja": "スポーツ競技場",
"zh_Hant": "運動場地"
"zh_Hant": "運動場地",
"ru": "Спортивные площадки"
},
"shortDescription": {
"nl": "Deze kaart toont sportvelden",
"fr": "Une carte montrant les terrains de sport",
"en": "A map showing sport pitches",
"ja": "スポーツ競技場を示す地図",
"zh_Hant": "顯示運動場地的地圖"
"zh_Hant": "顯示運動場地的地圖",
"ru": "Карта, отображающая спортивные площадки"
},
"description": {
"nl": "Een sportveld is een ingerichte plaats met infrastructuur om een sport te beoefenen",
@ -26,7 +28,8 @@
"fr",
"en",
"ja",
"zh_Hant"
"zh_Hant",
"ru"
],
"maintainer": "",
"icon": "./assets/layers/sport_pitch/table_tennis.svg",

View file

@ -15,7 +15,8 @@
"fr": "Carte des arbres",
"it": "Mappa tutti gli alberi",
"ja": "すべての樹木をマッピングする",
"zh_Hant": "所有樹木的地圖"
"zh_Hant": "所有樹木的地圖",
"ru": "Карта деревьев"
},
"description": {
"nl": "Breng bomen in kaart!",
@ -23,7 +24,8 @@
"fr": "Cartographions tous les arbres !",
"it": "Mappa tutti gli alberi!",
"ja": "すべての樹木をマッピングします!",
"zh_Hant": "繪製所有樹木!"
"zh_Hant": "繪製所有樹木!",
"ru": "Нанесите все деревья на карту!"
},
"language": [
"nl",

View file

@ -149,6 +149,10 @@
"zoomInToSeeThisLayer": "Zoom in to see this layer",
"title": "Select layers"
},
"download": {
"downloadGeojson": "Download visible data as geojson",
"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></li><li>Any change to this data must be republished under the same license</li></ul>. Please see the full <a href='https://www.openstreetmap.org/copyright' target='_blank'>copyright notice</a> for details"
},
"weekdays": {
"abbreviations": {
"monday": "Mon",

View file

@ -15,6 +15,21 @@
"opening_hours": {
"question": "What are the opening hours of {name}?",
"render": "<h3>Opening hours</h3>{opening_hours_table(opening_hours)}"
},
"level": {
"question": "On what level is this feature located?",
"render": "Located on the {level}th floor",
"mappings": {
"0": {
"then": "Located underground"
},
"1": {
"then": "Located on the ground floor"
},
"2": {
"then": "Located on the first floor"
}
}
}
}
}

View file

@ -15,6 +15,21 @@
"opening_hours": {
"question": "Wat zijn de openingsuren van {name}?",
"render": "<h3>Openingsuren</h3>{opening_hours_table(opening_hours)}"
},
"level": {
"question": "Op welke verdieping bevindt dit punt zich?",
"render": "Bevindt zich op de {level}de verdieping",
"mappings": {
"0": {
"then": "Bevindt zich ondergronds"
},
"1": {
"then": "Bevindt zich gelijkvloers"
},
"2": {
"then": "Bevindt zich op de eerste verdieping"
}
}
}
}
}

View file

@ -87,6 +87,9 @@
"shortDescription": "Eine Karte aller Sitzbänke",
"description": "Diese Karte zeigt alle Sitzbänke, die in OpenStreetMap eingetragen sind: Einzeln stehende Bänke und Bänke, die zu Haltestellen oder Unterständen gehören. Mit einem OpenStreetMap-Account können Sie neue Bänke eintragen oder Detailinformationen existierender Bänke bearbeiten."
},
"bicyclelib": {
"title": "Fahrradbibliothek"
},
"bookcases": {
"title": "Öffentliche Bücherschränke Karte",
"description": "Ein öffentlicher Bücherschrank ist ein kleiner Bücherschrank am Straßenrand, ein Kasten, eine alte Telefonzelle oder andere Gegenstände, in denen Bücher aufbewahrt werden. Jeder kann ein Buch hinstellen oder mitnehmen. Diese Karte zielt darauf ab, all diese Bücherschränke zu sammeln. Sie können neue Bücherschränke in der Nähe entdecken und mit einem kostenlosen OpenStreetMap-Account schnell Ihre Lieblingsbücherschränke hinzufügen."
@ -327,8 +330,5 @@
"toilets": {
"title": "Offene Toilette Karte",
"description": "Eine Karte der öffentlichen Toiletten"
},
"bicyclelib": {
"title": "Fahrradbibliothek"
}
}
}

View file

@ -1143,6 +1143,13 @@
"human": " gigawatts"
}
}
},
"1": {
"applicableUnits": {
"0": {
"human": " meter"
}
}
}
}
},

View file

@ -273,6 +273,9 @@
"3": {
"render": "Deze plaats vraagt {charge}",
"question": "Hoeveel kost deze plaats?"
},
"9": {
"render": "Officiële website: : <a href='{website}'>{website}</a>"
}
}
}
@ -280,13 +283,33 @@
},
"charging_stations": {
"title": "Oplaadpunten",
"shortDescription": "Een wereldwijde kaart van oplaadpunten",
"layers": {
"0": {
"name": "Oplaadpunten",
"title": {
"render": "Oplaadpunt"
},
"description": "Een oplaadpunt"
"description": "Een oplaadpunt",
"tagRenderings": {
"6": {
"render": "{network}",
"mappings": {
"0": {
"then": "Maakt geen deel uit van een netwerk"
},
"1": {
"then": "AeroVironment"
},
"2": {
"then": "Blink"
},
"3": {
"then": "eVgo"
}
}
}
}
}
}
},
@ -333,6 +356,7 @@
}
}
},
"description": "Een klimzaal",
"tagRenderings": {
"3": {
"render": "<strong>{name}</strong>",
@ -368,6 +392,11 @@
"question": "Hoe moeilijk is deze klimroute volgens het Franse/Belgische systeem?",
"render": "De klimmoeilijkheid is {climbing:grade:french} volgens het Franse/Belgische systeem"
}
},
"presets": {
"0": {
"title": "Klimroute"
}
}
},
"3": {
@ -419,6 +448,9 @@
},
"description": "Een klimgelegenheid?",
"tagRenderings": {
"1": {
"render": "<strong>{name}</strong>"
},
"2": {
"mappings": {
"0": {
@ -919,6 +951,13 @@
"human": " gigawatt"
}
}
},
"1": {
"applicableUnits": {
"0": {
"human": " meter"
}
}
}
}
},

View file

@ -278,7 +278,19 @@
}
},
"6": {
"question": "Кто может использовать эту станцию утилизации?"
"question": "Кто может использовать эту станцию утилизации?",
"mappings": {
"2": {
"then": "Любой может воспользоваться этой станцией утилизации"
},
"3": {
"then": "Любой может воспользоваться этой станцией утилизации"
}
}
},
"7": {
"render": "Эта станция - часть сети {network}",
"question": "К какой сети относится эта станция? (пропустите, если неприменимо)"
}
}
}
@ -301,7 +313,7 @@
},
"6": {
"render": "{network}",
"question": "К какой сети относится эта станция?",
"question": "К какой сети относится эта зарядная станция?",
"mappings": {
"0": {
"then": "Не является частью более крупной сети"
@ -335,6 +347,12 @@
"0": {
"render": "<strong>{name}</strong>"
}
},
"presets": {
"0": {
"title": "Клуб скалолазания",
"description": "Клуб скалолазания"
}
}
},
"1": {
@ -367,6 +385,16 @@
}
},
"roamingRenderings": {
"0": {
"question": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
},
"2": {
"mappings": {
"3": {
"then": "Только членам клуба"
}
}
},
"9": {
"mappings": {
"0": {
@ -379,18 +407,49 @@
}
}
},
"fietsstraten": {
"layers": {
"2": {
"name": "Все улицы",
"title": {
"render": "Улица"
}
}
}
},
"cyclofix": {
"title": "Cyclofix - открытая карта для велосипедистов"
},
"drinking_water": {
"title": "Питьевая вода"
"title": "Питьевая вода",
"description": "На этой карте показываются и могут быть легко добавлены общедоступные точки питьевой воды"
},
"facadegardens": {
"layers": {
"0": {
"tagRenderings": {
"2": {
"question": "Сад расположен на солнечной стороне или в тени?"
},
"3": {
"mappings": {
"0": {
"then": "Есть бочка с дождевой водой"
},
"1": {
"then": "Нет бочки с дождевой водой"
}
}
},
"4": {
"render": "Дата строительства сада: {start_date}"
},
"6": {
"question": "Какие виды растений обитают здесь?"
},
"7": {
"render": "Подробнее: {description}",
"question": "Дополнительная информация о саде (если требуется или еще не указана выше)"
}
}
}
@ -401,26 +460,70 @@
"0": {
"tagRenderings": {
"3": {
"render": "<a href='{website}'>{website}</a>"
"render": "<a href='{website}'>{website}</a>",
"question": "Какой веб-сайт у этого магазина?"
},
"4": {
"question": "Какой телефон?"
},
"8": {
"mappings": {
"1": {
"then": "Приносить свою тару <b>не разрешено</b>"
}
}
}
}
}
}
},
"hailhydrant": {
"title": "Пожарные гидранты, огнетушители, пожарные станции и станции скорой помощи.",
"shortDescription": "Карта пожарных гидрантов, огнетушителей, пожарных станций и станций скорой помощи.",
"layers": {
"0": {
"name": "Карта пожарных гидрантов",
"title": {
"render": "Гидрант"
},
"description": "Слой карты, отображающий пожарные гидранты.",
"tagRenderings": {
"0": {
"question": "Какого цвета гидрант?",
"render": "Цвет гидранта {colour}",
"mappings": {
"0": {
"then": "Цвет гидранта не определён."
},
"1": {
"then": "Гидрант жёлтого цвета."
},
"2": {
"then": "Гидрант красного цвета."
}
}
},
"1": {
"question": "К какому типу относится этот гидрант?",
"render": " Тип гидранта: {fire_hydrant:type}",
"mappings": {
"0": {
"then": "Тип гидранта не определён."
},
"3": {
"then": "<img style=\"width:15px\" src=\"./assets/themes/hailhydrant/hydrant_unknown.svg\" /> Тип стены."
}
}
},
"2": {
"mappings": {
"0": {
"then": "Гидрант (полностью или частично) в рабочем состоянии."
},
"2": {
"then": "Гидрант демонтирован."
}
}
}
},
"presets": {
@ -430,38 +533,98 @@
}
},
"1": {
"name": "Карта огнетушителей.",
"title": {
"render": "Огнетушители"
},
"description": "Слой карты, отображающий огнетушители.",
"tagRenderings": {
"0": {
"render": "Местоположение: {location}",
"question": "Где это расположено?",
"mappings": {
"0": {
"then": "Внутри."
},
"1": {
"then": "Снаружи."
}
}
}
},
"presets": {
"0": {
"title": "Огнетушитель",
"description": "Огнетушитель - небольшое переносное устройство для тушения огня"
}
}
},
"2": {
"name": "Карта пожарных частей",
"title": {
"render": "Пожарная часть"
},
"description": "Слой карты, отображающий пожарные части.",
"tagRenderings": {
"0": {
"question": "Как называется пожарная часть?"
"question": "Как называется эта пожарная часть?",
"render": "Эта часть называется {name}."
},
"1": {
"question": " По какому адресу расположена эта часть?",
"render": "Часть расположена вдоль шоссе {addr:street}."
},
"2": {
"question": "Где расположена часть? (напр., название населённого пункта)",
"render": "Эта часть расположена в {addr:place}."
}
},
"presets": {
"0": {
"title": "Пожарная часть"
}
}
},
"3": {
"name": "Карта станций скорой помощи",
"title": {
"render": "Станция скорой помощи"
},
"tagRenderings": {
"0": {
"question": "Как называется станция скорой помощи?"
"question": "Как называется эта станция скорой помощи?",
"render": "Эта станция называется {name}."
},
"1": {
"question": " По какому адресу расположена эта станция?",
"render": "Эта станция расположена вдоль шоссе {addr:street}."
},
"2": {
"question": "Где расположена станция? (напр., название населённого пункта)"
}
},
"presets": {
"0": {
"title": "Станция скорой помощи"
"title": "Станция скорой помощи",
"description": "Добавить станцию скорой помощи на карту"
}
}
}
}
},
"maps": {
"title": "Карта карт"
},
"personal": {
"description": "Создать персональную тему на основе доступных слоёв тем"
},
"playgrounds": {
"title": "Игровые площадки",
"shortDescription": "Карта игровых площадок",
"description": "На этой карте можно найти игровые площадки и добавить дополнительную информацию"
},
"shops": {
"title": "Открыть карту магазинов",
"layers": {
"0": {
"name": "Магазин",
@ -476,11 +639,13 @@
}
}
},
"description": "Магазин",
"tagRenderings": {
"1": {
"question": "Как называется магазин?"
"question": "Как называется этот магазин?"
},
"2": {
"question": "Что продаётся в этом магазине?",
"mappings": {
"1": {
"then": "Супермаркет"
@ -497,16 +662,20 @@
}
},
"3": {
"render": "<a href='tel:{phone}'>{phone}</a>"
"render": "<a href='tel:{phone}'>{phone}</a>",
"question": "Какой телефон?"
},
"4": {
"render": "<a href='{website}'>{website}</a>"
"render": "<a href='{website}'>{website}</a>",
"question": "Какой веб-сайт у этого магазина?"
},
"5": {
"render": "<a href='mailto:{email}'>{email}</a>"
"render": "<a href='mailto:{email}'>{email}</a>",
"question": "Каков адрес электронной почты этого магазина?"
},
"6": {
"render": "{opening_hours_table(opening_hours)}"
"render": "{opening_hours_table(opening_hours)}",
"question": "Каковы часы работы этого магазина?"
}
},
"presets": {
@ -518,11 +687,17 @@
}
}
},
"sport_pitches": {
"title": "Спортивные площадки",
"shortDescription": "Карта, отображающая спортивные площадки"
},
"toilets": {
"title": "Открытая карта туалетов",
"description": "Карта общественных туалетов"
},
"trees": {
"title": "Деревья"
"title": "Деревья",
"shortDescription": "Карта деревьев",
"description": "Нанесите все деревья на карту!"
}
}

View file

@ -12,7 +12,7 @@ import BaseUIElement from "./UI/BaseUIElement";
import Table from "./UI/Base/Table";
const connection = new OsmConnection(false, new UIEventSource<string>(undefined), "");
const connection = new OsmConnection(false, false, new UIEventSource<string>(undefined), "");
let rendered = false;

View file

@ -56,7 +56,8 @@ export default class ScriptUtils {
const urlObj = new URL(url)
https.get({
host: urlObj.host,
path: urlObj.pathname,
path: urlObj.pathname + urlObj.search,
port: urlObj.port,
headers: {
"accept": "application/json"
@ -74,6 +75,7 @@ export default class ScriptUtils {
try {
resolve(JSON.parse(result))
} catch (e) {
console.error("Could not parse the following as JSON:", result)
reject(e)
}
});

View file

@ -94,7 +94,8 @@ async function downloadRaw(targetdir: string, r: TileRange, overpass: Overpass)/
}
)
.catch(err => {
console.log("Could not download - probably hit the rate limit; waiting a bit")
console.log(url)
console.log("Could not download - probably hit the rate limit; waiting a bit. ("+err+")")
failed++;
return ScriptUtils.sleep(60000).then(() => console.log("Waiting is done"))
})

View file

@ -212,6 +212,7 @@ function generateTranslationsObjectFrom(objects: { path: string, parsed: { id: s
}
function MergeTranslation(source: any, target: any, language: string, context: string = "") {
for (const key in source) {
if (!source.hasOwnProperty(key)) {
continue
@ -220,6 +221,9 @@ function MergeTranslation(source: any, target: any, language: string, context: s
const targetV = target[key]
if (typeof sourceV === "string") {
if(targetV === undefined){
if(typeof target === "string"){
throw "Trying to merge a translation into a fixed string at "+context+" for key "+key;
}
target[key] = source[key];
continue;
}

View file

@ -15,7 +15,7 @@ export default class OsmConnectionSpec extends T {
super("OsmConnectionSpec-test", [
["login on dev",
() => {
const osmConn = new OsmConnection(false,
const osmConn = new OsmConnection(false,false,
new UIEventSource<string>(undefined),
"Unit test",
true,