diff --git a/Customizations/JSON/LayoutConfig.ts b/Customizations/JSON/LayoutConfig.ts index 12b9d5f766..e76c68ac88 100644 --- a/Customizations/JSON/LayoutConfig.ts +++ b/Customizations/JSON/LayoutConfig.ts @@ -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) diff --git a/Customizations/JSON/LayoutConfigJson.ts b/Customizations/JSON/LayoutConfigJson.ts index 374de70e07..d36a8463d6 100644 --- a/Customizations/JSON/LayoutConfigJson.ts +++ b/Customizations/JSON/LayoutConfigJson.ts @@ -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; } diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index 6f299adcf9..5c3158fd7e 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -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- ------------------ diff --git a/InitUiElements.ts b/InitUiElements.ts index 0dbc7eaac7..bc804a2294 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -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); diff --git a/Logic/Actors/AvailableBaseLayers.ts b/Logic/Actors/AvailableBaseLayers.ts index eceadde898..e84ecc6356 100644 --- a/Logic/Actors/AvailableBaseLayers.ts +++ b/Logic/Actors/AvailableBaseLayers.ts @@ -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 } diff --git a/Logic/Actors/StrayClickHandler.ts b/Logic/Actors/StrayClickHandler.ts index b4d6300700..3e7609fd41 100644 --- a/Logic/Actors/StrayClickHandler.ts +++ b/Logic/Actors/StrayClickHandler.ts @@ -47,7 +47,12 @@ export default class StrayClickHandler { popupAnchor: [0, -45] }) }); - const popup = L.popup().setContent("
"); + const popup = L.popup({ + autoPan: true, + autoPanPaddingTopLeft: [15,15], + closeOnEscapeKey: true, + autoClose: true + }).setContent("
"); self._lastMarker.addTo(leafletMap.data); self._lastMarker.bindPopup(popup); diff --git a/Logic/FeatureSource/FeatureSource.ts b/Logic/FeatureSource/FeatureSource.ts index ba568271ea..171db39f6a 100644 --- a/Logic/FeatureSource/FeatureSource.ts +++ b/Logic/FeatureSource/FeatureSource.ts @@ -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} + + + } + + } \ No newline at end of file diff --git a/Logic/Osm/OsmConnection.ts b/Logic/Osm/OsmConnection.ts index 37c8fa1d2e..92a0823f65 100644 --- a/Logic/Osm/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -47,6 +47,7 @@ export class OsmConnection { public auth; public userDetails: UIEventSource; public isLoggedIn: UIEventSource + 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, + constructor(dryRun: boolean, + fakeUser: boolean, + oauth_token: UIEventSource, // 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(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(); diff --git a/State.ts b/State.ts index 8e4322d654..4186572668 100644 --- a/State.ts +++ b/State.ts @@ -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; 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>(undefined, "Relation memberships") + public readonly knownRelations = new UIEventSource>(undefined, "Relation memberships") public readonly featureSwitchUserbadge: UIEventSource; public readonly featureSwitchSearch: UIEventSource; @@ -95,6 +96,11 @@ export default class State { public readonly featureSwitchIsDebugging: UIEventSource; public readonly featureSwitchShowAllQuestions: UIEventSource; public readonly featureSwitchApiURL: UIEventSource; + public readonly featureSwitchEnableExport: UIEventSource; + public readonly featureSwitchFakeUser: UIEventSource; + + + 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( 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( diff --git a/Svg.ts b/Svg.ts index 8b1e293075..26c5505ed3 100644 --- a/Svg.ts +++ b/Svg.ts @@ -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 = " image/svg+xml " + public static crosshair_locked = " image/svg+xml " 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);} diff --git a/UI/Base/Minimap.ts b/UI/Base/Minimap.ts index 2c38e8b74b..a7066c9eed 100644 --- a/UI/Base/Minimap.ts +++ b/UI/Base/Minimap.ts @@ -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 }); diff --git a/UI/BigComponents/ExportDataButton.ts b/UI/BigComponents/ExportDataButton.ts new file mode 100644 index 0000000000..9a161de9fd --- /dev/null +++ b/UI/BigComponents/ExportDataButton.ts @@ -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")]) + } +} \ No newline at end of file diff --git a/UI/BigComponents/LayerControlPanel.ts b/UI/BigComponents/LayerControlPanel.ts index 42a3eda125..c8837fbccc 100644 --- a/UI/BigComponents/LayerControlPanel.ts +++ b/UI/BigComponents/LayerControlPanel.ts @@ -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, "
", 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") } } \ No newline at end of file diff --git a/UI/BigComponents/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts index e282947096..3c7f108e8c 100644 --- a/UI/BigComponents/LayerSelection.ts +++ b/UI/BigComponents/LayerSelection.ts @@ -74,7 +74,6 @@ export default class LayerSelection extends Combine { ); } - super(checkboxes) this.SetStyle("display:flex;flex-direction:column;") diff --git a/UI/Input/LocationInput.ts b/UI/Input/LocationInput.ts index c306153e32..d568e4443f 100644 --- a/UI/Input/LocationInput.ts +++ b/UI/Input/LocationInput.ts @@ -48,7 +48,7 @@ export default class LocationInput extends InputElement { ) }) - layer.map(layer => { + this.mapBackground.map(layer => { const leaflet = map.leafletMap.data if (leaflet === undefined || layer === undefined) { diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index f35f73ceb8..b456c0ab96 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -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") diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 6c8fd257ec..c8953dd011 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -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;"); } diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts index df45af45e2..59225640f2 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer.ts @@ -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); diff --git a/Utils.ts b/Utils.ts index cb88356569..f16f987af2 100644 --- a/Utils.ts +++ b/Utils.ts @@ -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; + } } diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index ef3adb308a..6f59d8a5cf 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -508,7 +508,8 @@ } } ] - } + }, + "level" ], "icon": { "render": { diff --git a/assets/svg/crosshair-locked.svg b/assets/svg/crosshair-locked.svg index b1a741c287..d8d04340cd 100644 --- a/assets/svg/crosshair-locked.svg +++ b/assets/svg/crosshair-locked.svg @@ -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)"> - - - - - + id="g827"> + + 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" /> + + + + diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 1ef8f94c8e..2d0848c432 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -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" + ] } ] \ No newline at end of file diff --git a/assets/tagRenderings/questions.json b/assets/tagRenderings/questions.json index 7bff3270a3..66d034cdc3 100644 --- a/assets/tagRenderings/questions.json +++ b/assets/tagRenderings/questions.json @@ -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" + } + } + ] } } \ No newline at end of file diff --git a/assets/themes/artwork/artwork.json b/assets/themes/artwork/artwork.json index 7b8cc876c1..c3b7b7c552 100644 --- a/assets/themes/artwork/artwork.json +++ b/assets/themes/artwork/artwork.json @@ -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 quest’opera?", "ru": "Есть ли сайт с более подробной информацией об этой работе?", "ja": "この作品についての詳しい情報はどのウェブサイトにありますか?", diff --git a/assets/themes/bicycle_library/bicycle_library.json b/assets/themes/bicycle_library/bicycle_library.json index 437020a3fc..6d0cf61e2b 100644 --- a/assets/themes/bicycle_library/bicycle_library.json +++ b/assets/themes/bicycle_library/bicycle_library.json @@ -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.", diff --git a/assets/themes/campersites/campersites.json b/assets/themes/campersites/campersites.json index 3c580c5fd5..cc248f2780 100644 --- a/assets/themes/campersites/campersites.json +++ b/assets/themes/campersites/campersites.json @@ -427,7 +427,8 @@ "it": "Sito web ufficiale: {website}", "ja": "公式Webサイト: {website}", "nb_NO": "Offisiell nettside: {website}", - "zh_Hant": "官方網站:{website}" + "zh_Hant": "官方網站:{website}", + "nl": "Officiële website: : {website}" }, "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" diff --git a/assets/themes/charging_stations/charging_stations.json b/assets/themes/charging_stations/charging_stations.json index 159cd5819f..16bc127864 100644 --- a/assets/themes/charging_stations/charging_stations.json +++ b/assets/themes/charging_stations/charging_stations.json @@ -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" } } ] diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 342888881a..fbf13310c3 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -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": "{name}", "id": "{name}", "ru": "{name}", - "ja": "{name}" + "ja": "{name}", + "nl": "{name}" }, "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": "Только членам клуба" } }, { diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index 8a5956cd1a..b3b3abbf4c 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -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": [ { diff --git a/assets/themes/drinking_water/drinking_water.json b/assets/themes/drinking_water/drinking_water.json index da21f5296b..9dee5a8a36 100644 --- a/assets/themes/drinking_water/drinking_water.json +++ b/assets/themes/drinking_water/drinking_water.json @@ -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", diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index b472fdf051..8cc9369815 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -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", diff --git a/assets/themes/fritures/fritures.json b/assets/themes/fritures/fritures.json index 36cef08a01..3f3c51f425 100644 --- a/assets/themes/fritures/fritures.json +++ b/assets/themes/fritures/fritures.json @@ -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 geen eigen containers meenemen om je bestelling in mee te nemen", "en": "Bringing your own container is not allowed", - "ja": "独自の容器を持参することはできません" + "ja": "独自の容器を持参することはできません", + "ru": "Приносить свою тару не разрешено" } }, { diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index fef5c8ab28..d465b72eb2 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -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": "Добавить станцию скорой помощи на карту" } } ], diff --git a/assets/themes/maps/maps.json b/assets/themes/maps/maps.json index cc0d114e3f..e34cdce30b 100644 --- a/assets/themes/maps/maps.json +++ b/assets/themes/maps/maps.json @@ -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", diff --git a/assets/themes/openwindpowermap/openwindpowermap.json b/assets/themes/openwindpowermap/openwindpowermap.json index b1e4ecaa7c..22bba27ef9 100644 --- a/assets/themes/openwindpowermap/openwindpowermap.json +++ b/assets/themes/openwindpowermap/openwindpowermap.json @@ -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": [ { diff --git a/assets/themes/personalLayout/personalLayout.json b/assets/themes/personalLayout/personalLayout.json index b834fbdc3c..02e26cdbd2 100644 --- a/assets/themes/personalLayout/personalLayout.json +++ b/assets/themes/personalLayout/personalLayout.json @@ -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", diff --git a/assets/themes/playgrounds/playgrounds.json b/assets/themes/playgrounds/playgrounds.json index ce3a2b398b..418aa36bc4 100644 --- a/assets/themes/playgrounds/playgrounds.json +++ b/assets/themes/playgrounds/playgrounds.json @@ -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", diff --git a/assets/themes/shops/shops.json b/assets/themes/shops/shops.json index 3883a9ef83..571d0e1b6d 100644 --- a/assets/themes/shops/shops.json +++ b/assets/themes/shops/shops.json @@ -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", diff --git a/assets/themes/speelplekken/speelplekken_temp.json b/assets/themes/speelplekken/speelplekken_temp.json index f18fbbad10..bf3975dc36 100644 --- a/assets/themes/speelplekken/speelplekken_temp.json +++ b/assets/themes/speelplekken/speelplekken_temp.json @@ -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 => \"\" + r.relation.tags.name + \"\"))).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": "Een kinder-reportage vinden jullie hier", - "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": "Een kinder-reportage vinden jullie hier", + "freeform": { + "key": "video", + "type": "url" + }, + "question": "Wat is de link naar de video-reportage?" + } + ], "isShown": { "render": "yes", "mappings": [ diff --git a/assets/themes/sport_pitches/sport_pitches.json b/assets/themes/sport_pitches/sport_pitches.json index 5d872c8dca..7f1fdeebda 100644 --- a/assets/themes/sport_pitches/sport_pitches.json +++ b/assets/themes/sport_pitches/sport_pitches.json @@ -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", diff --git a/assets/themes/trees/trees.json b/assets/themes/trees/trees.json index daf26fe5b7..8545caa9a9 100644 --- a/assets/themes/trees/trees.json +++ b/assets/themes/trees/trees.json @@ -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", diff --git a/langs/en.json b/langs/en.json index 7f327653c5..aac35335c1 100644 --- a/langs/en.json +++ b/langs/en.json @@ -149,6 +149,10 @@ "zoomInToSeeThisLayer": "Zoom in to see this layer", "title": "Select layers" }, + "download": { + "downloadGeojson": "Download visible data as geojson", + "licenseInfo": "

Copyright notice

The provided is available under ODbL. Reusing this data is free for any purpose, but
  • the attribution © OpenStreetMap contributors
  • Any change to this data must be republished under the same license
. Please see the full
copyright notice for details" + }, "weekdays": { "abbreviations": { "monday": "Mon", diff --git a/langs/shared-questions/en.json b/langs/shared-questions/en.json index a8b983d9e6..d11785526e 100644 --- a/langs/shared-questions/en.json +++ b/langs/shared-questions/en.json @@ -15,6 +15,21 @@ "opening_hours": { "question": "What are the opening hours of {name}?", "render": "

Opening hours

{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" + } + } } } } \ No newline at end of file diff --git a/langs/shared-questions/nl.json b/langs/shared-questions/nl.json index 2e50d77b1e..fd5a2a9b69 100644 --- a/langs/shared-questions/nl.json +++ b/langs/shared-questions/nl.json @@ -15,6 +15,21 @@ "opening_hours": { "question": "Wat zijn de openingsuren van {name}?", "render": "

Openingsuren

{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" + } + } } } } \ No newline at end of file diff --git a/langs/themes/de.json b/langs/themes/de.json index 95abb60b17..6a0b29fad1 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -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" } -} +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index 48852d5a4c..f5db488b97 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1143,6 +1143,13 @@ "human": " gigawatts" } } + }, + "1": { + "applicableUnits": { + "0": { + "human": " meter" + } + } } } }, diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 7966e9e2d1..e1ab2203fd 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -273,6 +273,9 @@ "3": { "render": "Deze plaats vraagt {charge}", "question": "Hoeveel kost deze plaats?" + }, + "9": { + "render": "Officiële website: : {website}" } } } @@ -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": "{name}", @@ -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": "{name}" + }, "2": { "mappings": { "0": { @@ -919,6 +951,13 @@ "human": " gigawatt" } } + }, + "1": { + "applicableUnits": { + "0": { + "human": " meter" + } + } } } }, diff --git a/langs/themes/ru.json b/langs/themes/ru.json index cb0a7dbe71..74f15b8753 100644 --- a/langs/themes/ru.json +++ b/langs/themes/ru.json @@ -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": "{name}" } + }, + "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": "{website}" + "render": "{website}", + "question": "Какой веб-сайт у этого магазина?" + }, + "4": { + "question": "Какой телефон?" + }, + "8": { + "mappings": { + "1": { + "then": "Приносить свою тару не разрешено" + } + } } } } } }, "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": " Тип стены." } } + }, + "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": "{phone}" + "render": "{phone}", + "question": "Какой телефон?" }, "4": { - "render": "{website}" + "render": "{website}", + "question": "Какой веб-сайт у этого магазина?" }, "5": { - "render": "{email}" + "render": "{email}", + "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": "Нанесите все деревья на карту!" } } \ No newline at end of file diff --git a/preferences.ts b/preferences.ts index 1c1773a143..a7ae07dedc 100644 --- a/preferences.ts +++ b/preferences.ts @@ -12,7 +12,7 @@ import BaseUIElement from "./UI/BaseUIElement"; import Table from "./UI/Base/Table"; -const connection = new OsmConnection(false, new UIEventSource(undefined), ""); +const connection = new OsmConnection(false, false, new UIEventSource(undefined), ""); let rendered = false; diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index ccf230275e..174b1d3df0 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -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) } }); diff --git a/scripts/generateCache.ts b/scripts/generateCache.ts index f7651c4b41..733e798fc1 100644 --- a/scripts/generateCache.ts +++ b/scripts/generateCache.ts @@ -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")) }) diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 492a669e98..9f957553ec 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -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; } diff --git a/test/OsmConnection.spec.ts b/test/OsmConnection.spec.ts index ffcb4840ca..2253e56c38 100644 --- a/test/OsmConnection.spec.ts +++ b/test/OsmConnection.spec.ts @@ -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(undefined), "Unit test", true,