Merge branch 'feature/move-flox' into develop

This commit is contained in:
Pieter Vander Vennet 2021-10-14 17:39:16 +02:00
commit 43f73947c9
47 changed files with 2139 additions and 786 deletions

View file

@ -16,7 +16,7 @@ export interface ChangeDescription {
/**
* The type of the change
*/
changeType: "answer" | "create" | "split" | "delete" | string
changeType: "answer" | "create" | "split" | "delete" | "move" | string
/**
* THe motivation for the change, e.g. 'deleted because does not exist anymore'
*/

View file

@ -0,0 +1,41 @@
import {ChangeDescription} from "./ChangeDescription";
import OsmChangeAction from "./OsmChangeAction";
import {Changes} from "../Changes";
export default class ChangeLocationAction extends OsmChangeAction {
private readonly _id: number;
private readonly _newLonLat: [number, number];
private readonly _meta: { theme: string; reason: string };
constructor(id: string, newLonLat: [number, number], meta: {
theme: string,
reason: string
}) {
super();
if (!id.startsWith("node/")) {
throw "Invalid ID: only 'node/number' is accepted"
}
this._id = Number(id.substring("node/".length))
this._newLonLat = newLonLat;
this._meta = meta;
}
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const d: ChangeDescription = {
changes: {
lat: this._newLonLat[1],
lon: this._newLonLat[0]
},
type: "node",
id: this._id, meta: {
changeType: "move",
theme: this._meta.theme,
specialMotivation: this._meta.reason
}
}
return [d]
}
}

View file

@ -59,14 +59,13 @@ export abstract class OsmObject {
static async DownloadPropertiesOf(id: string): Promise<any> {
const splitted = id.split("/");
const type = splitted[0];
const idN = Number(splitted[1]);
if (idN < 0) {
return undefined;
}
const url = `${OsmObject.backendURL}api/0.6/${id}`;
const rawData = await Utils.downloadJson(url)
const rawData = await Utils.downloadJsonCached(url, 1000)
return rawData.elements[0].tags
}
@ -80,7 +79,7 @@ export abstract class OsmObject {
const full = (id.startsWith("way")) ? "/full" : "";
const url = `${OsmObject.backendURL}api/0.6/${id}${full}`;
const rawData = await Utils.downloadJson(url)
const rawData = await Utils.downloadJsonCached(url, 1000)
// A full query might contain more then just the requested object (e.g. nodes that are part of a way, where we only want the way)
const parsed = OsmObject.ParseObjects(rawData.elements);
// Lets fetch the object we need
@ -105,7 +104,7 @@ export abstract class OsmObject {
* Beware: their geometry will be incomplete!
*/
public static DownloadReferencingWays(id: string): Promise<OsmWay[]> {
return Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/ways`).then(
return Utils.downloadJsonCached(`${OsmObject.backendURL}api/0.6/${id}/ways`, 60 * 1000).then(
data => {
return data.elements.map(wayInfo => {
const way = new OsmWay(wayInfo.id)
@ -121,7 +120,7 @@ export abstract class OsmObject {
* Beware: their geometry will be incomplete!
*/
public static async DownloadReferencingRelations(id: string): Promise<OsmRelation[]> {
const data = await Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${id}/relations`)
const data = await Utils.downloadJsonCached(`${OsmObject.backendURL}api/0.6/${id}/relations`, 60 * 1000)
return data.elements.map(wayInfo => {
const rel = new OsmRelation(wayInfo.id)
rel.LoadData(wayInfo)
@ -139,7 +138,7 @@ export abstract class OsmObject {
const idN = Number(splitted[1]);
const src = new UIEventSource<OsmObject[]>([]);
OsmObject.historyCache.set(id, src);
Utils.downloadJson(`${OsmObject.backendURL}api/0.6/${type}/${idN}/history`).then(data => {
Utils.downloadJsonCached(`${OsmObject.backendURL}api/0.6/${type}/${idN}/history`, 10 * 60 * 1000).then(data => {
const elements: any[] = data.elements;
const osmObjects: OsmObject[] = []
for (const element of elements) {

View file

@ -3,6 +3,7 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
import FilterConfigJson from "./FilterConfigJson";
import {DeleteConfigJson} from "./DeleteConfigJson";
import UnitConfigJson from "./UnitConfigJson";
import MoveConfigJson from "./MoveConfigJson";
/**
* Configuration for a single layer
@ -314,6 +315,18 @@ export interface LayerConfigJson {
*/
deletion?: boolean | DeleteConfigJson
/**
* Indicates if a point can be moved and configures the modalities.
*
* A feature can be moved by MapComplete if:
*
* - It is a point
* - The point is _not_ part of a way or a a relation.
*
* Off by default. Can be enabled by setting this flag or by configuring.
*/
allowMove?: boolean | MoveConfigJson
/**
* IF set, a 'split this road' button is shown
*/

View file

@ -0,0 +1,12 @@
export default interface MoveConfigJson {
/**
* One default reason to move a point is to improve accuracy.
* Set to false to disable this reason
*/
enableImproveAccuracy?: true | boolean
/**
* One default reason to move a point is because it has relocated
* Set to false to disable this reason
*/
enableRelocation?: true | boolean
}

View file

@ -19,6 +19,7 @@ import {Unit} from "../Unit";
import DeleteConfig from "./DeleteConfig";
import Svg from "../../Svg";
import Img from "../../UI/Base/Img";
import MoveConfig from "./MoveConfig";
export default class LayerConfig {
static WAYHANDLING_DEFAULT = 0;
@ -49,6 +50,7 @@ export default class LayerConfig {
wayHandling: number;
public readonly units: Unit[];
public readonly deletion: DeleteConfig | null;
public readonly allowMove: MoveConfig | null
public readonly allowSplit: boolean
presets: PresetConfig[];
@ -67,7 +69,7 @@ export default class LayerConfig {
this.id = json.id;
this.allowSplit = json.allowSplit ?? false;
this.name = Translations.T(json.name, context + ".name");
this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
this.units = (json.units ?? []).map(((unitJson, i) => Unit.fromJson(unitJson, `${context}.unit[${i}]`)))
if (json.description !== undefined) {
if (Object.keys(json.description).length === 0) {
@ -138,11 +140,11 @@ export default class LayerConfig {
const key = kv.substring(0, index);
const code = kv.substring(index + 1);
try{
new Function("feat", "return " + code + ";");
}catch(e){
throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})`
try {
new Function("feat", "return " + code + ";");
} catch (e) {
throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})`
}
@ -155,8 +157,8 @@ export default class LayerConfig {
this.minzoom = json.minzoom ?? 0;
this.minzoomVisible = json.minzoomVisible ?? this.minzoom;
this.wayHandling = json.wayHandling ?? 0;
if(json.presets !== undefined && json.presets?.map === undefined){
throw "Presets should be a list of items (at "+context+")"
if (json.presets !== undefined && json.presets?.map === undefined) {
throw "Presets should be a list of items (at " + context + ")"
}
this.presets = (json.presets ?? []).map((pr, i) => {
@ -291,21 +293,21 @@ export default class LayerConfig {
}
this.tagRenderings = trs(json.tagRenderings, false);
const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
if(missingIds.length > 0 && official){
console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds)
throw "Missing ids in tagrenderings"
}
const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
if (missingIds.length > 0 && official) {
console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds)
throw "Missing ids in tagrenderings"
}
this.filters = (json.filter ?? []).map((option, i) => {
return new FilterConfig(option, `${context}.filter-[${i}]`)
});
if(json["filters"] !== undefined){
throw "Error in "+context+": use 'filter' instead of 'filters'"
}
if (json["filters"] !== undefined) {
throw "Error in " + context + ": use 'filter' instead of 'filters'"
}
const titleIcons = [];
const defaultIcons = [
@ -369,6 +371,16 @@ export default class LayerConfig {
this.deletion = new DeleteConfig(json.deletion, `${context}.deletion`);
}
this.allowMove = null
if (json.allowMove === false) {
this.allowMove = null;
} else if (json.allowMove === true) {
this.allowMove = new MoveConfig({}, context + ".allowMove")
} else if (json.allowMove !== undefined && json.allowMove !== false) {
this.allowMove = new MoveConfig(json.allowMove, context + ".allowMove")
}
if (json["showIf"] !== undefined) {
throw (
"Invalid key on layerconfig " +

View file

@ -0,0 +1,17 @@
import MoveConfigJson from "./Json/MoveConfigJson";
export default class MoveConfig {
public readonly enableImproveAccuracy: boolean
public readonly enableRelocation: boolean
constructor(json: MoveConfigJson, context: string) {
this.enableImproveAccuracy = json.enableImproveAccuracy ?? true
this.enableRelocation = json.enableRelocation ?? true
if (!(this.enableRelocation || this.enableImproveAccuracy)) {
throw "At least one default move reason should be allowed (at " + context + ")"
}
}
}

View file

@ -153,6 +153,7 @@ export default class SimpleAddUI extends Toggle {
maxSnapDistance: preset.preciseInput.maxSnapDistance,
bounds: mapBounds
})
preciseInput.installBounds(0.15, true)
preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;")

View file

@ -1,7 +1,7 @@
import {InputElement} from "./InputElement";
import Loc from "../../Models/Loc";
import {UIEventSource} from "../../Logic/UIEventSource";
import Minimap from "../Base/Minimap";
import Minimap, {MinimapObj} from "../Base/Minimap";
import BaseLayer from "../../Models/BaseLayer";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
@ -14,8 +14,10 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import {BBox} from "../../Logic/BBox";
import {FixedUiElement} from "../Base/FixedUiElement";
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
import BaseUIElement from "../BaseUIElement";
import Toggle from "./Toggle";
export default class LocationInput extends InputElement<Loc> {
export default class LocationInput extends InputElement<Loc> implements MinimapObj {
private static readonly matchLayer = new LayerConfig(
{
@ -41,8 +43,14 @@ export default class LocationInput extends InputElement<Loc> {
private readonly _snappedPointTags: any;
private readonly _bounds: UIEventSource<BBox>;
public readonly _matching_layer: LayerConfig;
private readonly map: BaseUIElement & MinimapObj;
public readonly leafletMap: UIEventSource<any>
private readonly clickLocation: UIEventSource<Loc>;
private readonly _minZoom: number;
constructor(options: {
minZoom?: number,
mapBackground?: UIEventSource<BaseLayer>,
snapTo?: UIEventSource<{ feature: any }[]>,
maxSnapDistance?: number,
@ -57,6 +65,7 @@ export default class LocationInput extends InputElement<Loc> {
this._centerLocation = options.centerLocation;
this._snappedPointTags = options.snappedPointTags
this._bounds = options.bounds;
this._minZoom = options.minZoom
if (this._snapTo === undefined) {
this._value = this._centerLocation;
} else {
@ -127,6 +136,19 @@ export default class LocationInput extends InputElement<Loc> {
}
this.mapBackground = options.mapBackground ?? State.state?.backgroundLayer ?? new UIEventSource(AvailableBaseLayers.osmCarto)
this.SetClass("block h-full")
this.clickLocation = new UIEventSource<Loc>(undefined);
this.map = Minimap.createMiniMap(
{
location: this._centerLocation,
background: this.mapBackground,
attribution: this.mapBackground !== State.state?.backgroundLayer,
lastClickLocation: this.clickLocation,
bounds: this._bounds
}
)
this.leafletMap = this.map.leafletMap
}
GetValue(): UIEventSource<Loc> {
@ -136,31 +158,24 @@ export default class LocationInput extends InputElement<Loc> {
IsValid(t: Loc): boolean {
return t !== undefined;
}
protected InnerConstructElement(): HTMLElement {
try {
const clickLocation = new UIEventSource<Loc>(undefined);
const map = Minimap.createMiniMap(
{
location: this._centerLocation,
background: this.mapBackground,
attribution: this.mapBackground !== State.state?.backgroundLayer,
lastClickLocation: clickLocation,
bounds: this._bounds
}
)
clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location))
map.installBounds(0.15, true);
if (this._snapTo !== undefined) {
const self = this;
const hasMoved = new UIEventSource(false)
this.GetValue().addCallbackAndRunD(_ => {
hasMoved.setData(true)
return true;
})
this.clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location))
if (this._snapTo !== undefined) {
// Show the lines to snap to
new ShowDataMultiLayer({
features: new StaticFeatureSource(this._snapTo, true),
enablePopups: false,
zoomToFeatures: false,
leafletMap: map.leafletMap,
leafletMap: this.map.leafletMap,
layers: State.state.filteredLayers
}
)
@ -175,22 +190,22 @@ export default class LocationInput extends InputElement<Loc> {
features: new StaticFeatureSource(matchPoint, true),
enablePopups: false,
zoomToFeatures: false,
leafletMap: map.leafletMap,
leafletMap: this.map.leafletMap,
layerToShow: this._matching_layer
})
}
this.mapBackground.map(layer => {
const leaflet = map.leafletMap.data
const leaflet = this.map.leafletMap.data
if (leaflet === undefined || layer === undefined) {
return;
}
leaflet.setMaxZoom(layer.max_zoom)
leaflet.setMinZoom(layer.max_zoom - 2)
leaflet.setMinZoom(self._minZoom ?? layer.max_zoom - 2)
leaflet.setZoom(layer.max_zoom - 1)
}, [map.leafletMap])
}, [this.map.leafletMap])
const animatedHand = Svg.hand_ui()
.SetStyle("width: 2rem; height: unset;")
@ -204,12 +219,12 @@ export default class LocationInput extends InputElement<Loc> {
]).SetClass("block w-0 h-0 z-10 relative")
.SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"),
new Combine([
animatedHand])
new Toggle(undefined,
animatedHand, hasMoved)
.SetClass("block w-0 h-0 z-10 relative")
.SetStyle("left: calc(50% + 3rem); top: calc(50% + 2rem); opacity: 0.7"),
map
this.map
.SetClass("z-0 relative block w-full h-full bg-gray-100")
]).ConstructElement();
@ -219,4 +234,9 @@ export default class LocationInput extends InputElement<Loc> {
}
}
installBounds(factor: number | BBox, showRange?: boolean): void {
this.map.installBounds(factor, showRange)
}
}

View file

@ -93,12 +93,12 @@ export default class DeleteWizard extends Toggle {
* The button which is shown first. Opening it will trigger the check for deletions
*/
const deleteButton = new SubtleButton(
Svg.delete_icon_ui().SetStyle("width: 2rem; height: 2rem;"), t.delete.Clone()).onClick(
Svg.delete_icon_ui().SetStyle("width: auto; height: 1.5rem;"), t.delete.Clone()).onClick(
() => {
deleteAbility.CheckDeleteability(true)
confirm.setData(true);
}
).SetClass("w-1/2 float-right");
)
const isShown = new UIEventSource<boolean>(id.indexOf("-") < 0)

View file

@ -17,6 +17,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
import {Translation} from "../i18n/Translation";
import {Utils} from "../../Utils";
import {SubstitutedTranslation} from "../SubstitutedTranslation";
import MoveWizard from "./MoveWizard";
export default class FeatureInfoBox extends ScrollableFullScreen {
@ -72,6 +73,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
editElements.push(questionBox);
}
if(layerConfig.allowMove) {
editElements.push(
new VariableUiElement(tags.map(tags => tags.id).map(id => {
const feature = State.state.allElements.ContainingFeatures.get(id)
return new MoveWizard(
feature,
State.state,
layerConfig.allowMove
);
})
)
);
}
if (layerConfig.deletion) {
editElements.push(

221
UI/Popup/MoveWizard.ts Normal file
View file

@ -0,0 +1,221 @@
import {SubtleButton} from "../Base/SubtleButton";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
import Toggle from "../Input/Toggle";
import {UIEventSource} from "../../Logic/UIEventSource";
import Translations from "../i18n/Translations";
import {VariableUiElement} from "../Base/VariableUIElement";
import {Translation} from "../i18n/Translation";
import BaseUIElement from "../BaseUIElement";
import LocationInput from "../Input/LocationInput";
import Loc from "../../Models/Loc";
import {GeoOperations} from "../../Logic/GeoOperations";
import {OsmObject} from "../../Logic/Osm/OsmObject";
import {Changes} from "../../Logic/Osm/Changes";
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction";
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
import MoveConfig from "../../Models/ThemeConfig/MoveConfig";
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
import {ElementStorage} from "../../Logic/ElementStorage";
import Img from "../Base/Img";
interface MoveReason {
text: Translation | string,
invitingText: Translation | string,
icon: BaseUIElement,
changesetCommentValue: string,
lockBounds: true | boolean,
background: undefined | "map" | "photo" | string | string[],
startZoom: number,
minZoom: number
}
export default class MoveWizard extends Toggle {
/**
* The UI-element which helps moving a point
*/
constructor(
featureToMove: any,
state: {
osmConnection: OsmConnection,
featureSwitchUserbadge: UIEventSource<boolean>,
changes: Changes,
layoutToUse: LayoutConfig,
allElements: ElementStorage
}, options : MoveConfig) {
const t = Translations.t.move
const loginButton = new Toggle(
t.loginToMove.Clone().SetClass("btn").onClick(() => state.osmConnection.AttemptLogin()),
undefined,
state.featureSwitchUserbadge
)
const reasons: MoveReason[] = []
if (options.enableRelocation) {
reasons.push({
text: t.reasons.reasonRelocation.Clone(),
invitingText: t.inviteToMove.reasonRelocation.Clone(),
icon: Svg.relocation_svg(),
changesetCommentValue: "relocated",
lockBounds: false,
background: undefined,
startZoom: 12,
minZoom: 6
})
}
if(options.enableImproveAccuracy){
reasons.push({
text: t.reasons.reasonInaccurate.Clone(),
invitingText: t.inviteToMove.reasonInaccurate,
icon: Svg.crosshair_svg(),
changesetCommentValue: "improve_accuracy",
lockBounds: true,
background: "photo",
startZoom: 17,
minZoom: 16
})
}
const currentStep = new UIEventSource<"start" | "reason" | "pick_location" | "moved">("start")
const moveReason = new UIEventSource<MoveReason>(undefined)
let moveButton : BaseUIElement;
if(reasons.length === 1){
const reason = reasons[0]
moveReason.setData(reason)
moveButton = new SubtleButton(
reason.icon.SetStyle("height: 1.5rem; width: auto;"),
Translations.WT(reason.invitingText).Clone()
).onClick(() => {
currentStep.setData("pick_location")
})
}else{
moveButton = new SubtleButton(
Svg.move_ui().SetStyle("height: 1.5rem; width: auto"),
t.inviteToMove.generic.Clone()
).onClick(() => {
currentStep.setData("reason")
})
}
const moveAgainButton = new SubtleButton(
Svg.move_ui(),
t.inviteToMoveAgain.Clone()
).onClick(() => {
currentStep.setData("reason")
})
const selectReason = new Combine(reasons.map(r => new SubtleButton(r.icon, r.text).onClick(() => {
moveReason.setData(r)
currentStep.setData("pick_location")
})))
const cancelButton = new SubtleButton(Svg.close_svg(), t.cancel).onClick(() => currentStep.setData("start"))
const [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove)
const locationInput = moveReason.map(reason => {
if (reason === undefined) {
return undefined
}
const loc = new UIEventSource<Loc>({
lon: lon,
lat: lat,
zoom: reason?.startZoom ?? 16
})
const locationInput = new LocationInput({
minZoom: reason.minZoom,
centerLocation: loc,
mapBackground: AvailableBaseLayers.SelectBestLayerAccordingTo(loc, new UIEventSource(reason.background))
})
if (reason.lockBounds) {
locationInput.installBounds(0.05, true)
}
locationInput.SetStyle("height: 17.5rem")
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
confirmMove.onClick(() => {
const loc = locationInput.GetValue().data
state.changes.applyAction(new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
reason: Translations.WT(reason.text).textFor("en"),
theme: state.layoutToUse.id
}))
featureToMove.properties._lat = loc.lat
featureToMove.properties._lon = loc.lon
state.allElements.getEventSourceById(id).ping()
currentStep.setData("moved")
})
const zoomInFurhter = t.zoomInFurther.Clone().SetClass("alert block m-6")
return new Combine([
locationInput,
new Toggle(confirmMove, zoomInFurhter, locationInput.GetValue().map(l => l.zoom >= 19))
]).SetClass("flex flex-col")
});
const dialogClasses = "p-2 md:p-4 m-2 border border-gray-400 rounded-xl flex flex-col"
const moveFlow = new Toggle(
new VariableUiElement(currentStep.map(currentStep => {
switch (currentStep) {
case "start":
return moveButton;
case "reason":
return new Combine([t.whyMove.Clone().SetClass("text-lg font-bold"), selectReason, cancelButton]).SetClass(dialogClasses);
case "pick_location":
return new Combine([t.moveTitle.Clone().SetClass("text-lg font-bold"), new VariableUiElement(locationInput), cancelButton]).SetClass(dialogClasses)
case "moved":
return new Combine([t.pointIsMoved.Clone().SetClass("thanks"), moveAgainButton]).SetClass("flex flex-col");
}
})),
loginButton,
state.osmConnection.isLoggedIn
)
let id = featureToMove.properties.id
const backend = state.osmConnection._oauth_config.url
if (id.startsWith(backend)) {
id = id.substring(backend.length)
}
const moveDisallowedReason = new UIEventSource<BaseUIElement>(undefined)
if (id.startsWith("way")) {
moveDisallowedReason.setData(t.isWay.Clone())
} else if (id.startsWith("relation")) {
moveDisallowedReason.setData(t.isRelation.Clone())
} else {
OsmObject.DownloadReferencingWays(id).then(referencing => {
if (referencing.length > 0) {
console.log("Got a referencing way, move not allowed")
moveDisallowedReason.setData(t.partOfAWay.Clone())
}
})
OsmObject.DownloadReferencingRelations(id).then(partOf => {
if(partOf.length > 0){
moveDisallowedReason.setData(t.partOfRelation.Clone())
}
})
}
super(
moveFlow,
new Combine([
Svg.move_not_allowed_svg().SetStyle("height: 2rem"),
new Combine([t.cannotBeMoved.Clone(),
new VariableUiElement(moveDisallowedReason).SetClass("subtle")
]).SetClass("flex flex-col")
]).SetClass("flex"),
moveDisallowedReason.map(r => r === undefined)
)
}
}

View file

@ -120,7 +120,7 @@ export default class SplitRoadWizard extends Toggle {
}))
// Toggle between splitmap
const splitButton = new SubtleButton(Svg.scissors_ui(), t.inviteToSplit.Clone().SetClass("text-lg font-bold"));
const splitButton = new SubtleButton(Svg.scissors_ui().SetStyle("height: 1.5rem; width: auto"), t.inviteToSplit.Clone().SetClass("text-lg font-bold"));
splitButton.onClick(
() => {
splitClicked.setData(true)

View file

@ -320,6 +320,19 @@ export class Utils {
)
}
private static _download_cache = new Map<string, {promise: Promise<any>, timestamp: number}>()
public static async downloadJsonCached(url: string, maxCacheTimeMs: number, headers?: any): Promise<any> {
const cached = Utils._download_cache.get(url)
if(cached !== undefined){
if((new Date().getTime() - cached.timestamp) <= maxCacheTimeMs){
return cached.promise
}
}
const promise = Utils.downloadJson(url, headers)
Utils._download_cache.set(url, {promise, timestamp: new Date().getTime()})
return await promise
}
public static async downloadJson(url: string, headers?: any): Promise<any> {
const injected = Utils.injectedDownloads[url]
if (injected !== undefined) {

View file

@ -0,0 +1,398 @@
{
"id": "artwork",
"name": {
"en": "Artworks",
"nl": "Kunstwerken",
"fr": "Œuvres d'art",
"de": "Kunstwerke",
"id": "Karya seni",
"it": "Opere darte",
"ru": "Произведения искусства",
"es": "Obras de arte",
"ja": "美術品",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
},
"source": {
"osmTags": "tourism=artwork"
},
"title": {
"render": {
"en": "Artwork",
"nl": "Kunstwerk",
"fr": "Œuvre d'art",
"de": "Kunstwerk",
"id": "Karya seni",
"it": "Opera darte",
"ru": "Художественная работа",
"es": "Obra de arte",
"ja": "アートワーク",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
},
"mappings": [
{
"if": "name~*",
"then": {
"en": "Artwork <i>{name}</i>",
"nl": "Kunstwerk <i>{name}</i>",
"fr": "Œuvre d'art <i>{name}</i>",
"de": "Kunstwerk <i>{name}</i>",
"id": "Karya seni <i>{name}</i>",
"it": "Opera <i>{name}</i>",
"ru": "Художественная работа <i>{name}</i>",
"es": "Obra de arte <i>{nombre}</i>",
"ja": "アートワーク <i>{name}</i>",
"zh_Hant": "藝術品<i>{name}</i>"
}
}
]
},
"icon": {
"render": "./assets/themes/artwork/artwork.svg"
},
"color": {
"render": "#0000ff"
},
"width": {
"render": "10"
},
"description": {
"en": "Diverse pieces of artwork",
"nl": "Verschillende soorten kunstwerken",
"fr": "Diverses œuvres d'art",
"de": "Verschiedene Kunstwerke",
"it": "Diverse opere darte",
"ru": "Разнообразные произведения искусства",
"es": "Diversas piezas de obras de arte",
"ja": "多様な作品",
"zh_Hant": "不同類型的藝術品"
},
"minzoom": 12,
"wayHandling": 2,
"presets": [
{
"tags": [
"tourism=artwork"
],
"title": {
"en": "Artwork",
"nl": "Kunstwerk",
"fr": "Œuvre d'art",
"de": "Kunstwerk",
"it": "Opera darte",
"ru": "Художественная работа",
"es": "Obra de arte",
"ja": "アートワーク",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
}
}
],
"tagRenderings": [
"images",
{
"render": {
"en": "This is a {artwork_type}",
"nl": "Dit is een {artwork_type}",
"fr": "Type d'œuvre : {artwork_type}",
"de": "Dies ist ein {artwork_type}",
"it": "Si tratta di un {artwork_type}",
"ru": "Это {artwork_type}",
"es": "Esta es un {artwork_type}",
"ja": "これは{artwork_type}です",
"zh_Hant": "這是 {artwork_type}",
"nb_NO": "Dette er et kunstverk av typen {artwork_type}"
},
"question": {
"en": "What is the type of this artwork?",
"nl": "Wat voor soort kunstwerk is dit?",
"fr": "Quel est le type de cette œuvre d'art?",
"de": "Was ist die Art dieses Kunstwerks?",
"it": "Che tipo di opera darte è questo?",
"ru": "К какому типу относится эта работа?",
"es": "Cuál es el tipo de esta obra de arte?",
"ja": "この作品の種類は何ですか?",
"zh_Hant": "這是什麼類型的藝術品?",
"nb_NO": "Hvilken type kunstverk er dette?"
},
"freeform": {
"key": "artwork_type",
"addExtraTags": [
"fixme=Artowrk type was added with the freeform, might need another check"
]
},
"mappings": [
{
"if": "artwork_type=architecture",
"then": {
"en": "Architecture",
"nl": "Architectuur",
"fr": "Architecture",
"de": "Architektur",
"it": "Architettura",
"ru": "Архитектура",
"ja": "建物",
"zh_Hant": "建築物",
"nb_NO": "Arkitektur"
}
},
{
"if": "artwork_type=mural",
"then": {
"en": "Mural",
"nl": "Muurschildering",
"fr": "Peinture murale",
"de": "Wandbild",
"it": "Murale",
"ru": "Фреска",
"ja": "壁画",
"zh_Hant": "壁畫",
"nb_NO": "Veggmaleri"
}
},
{
"if": "artwork_type=painting",
"then": {
"en": "Painting",
"nl": "Schilderij",
"fr": "Peinture",
"de": "Malerei",
"it": "Dipinto",
"ru": "Живопись",
"ja": "絵画",
"zh_Hant": "繪畫",
"nb_NO": "Maleri"
}
},
{
"if": "artwork_type=sculpture",
"then": {
"en": "Sculpture",
"nl": "Beeldhouwwerk",
"fr": "Sculpture",
"de": "Skulptur",
"it": "Scultura",
"ru": "Скульптура",
"ja": "彫刻",
"zh_Hant": "雕塑",
"nb_NO": "Skulptur"
}
},
{
"if": "artwork_type=statue",
"then": {
"en": "Statue",
"nl": "Standbeeld",
"fr": "Statue",
"de": "Statue",
"it": "Statua",
"ru": "Статуя",
"ja": "彫像",
"zh_Hant": "雕像",
"nb_NO": "Statue"
}
},
{
"if": "artwork_type=bust",
"then": {
"en": "Bust",
"nl": "Buste",
"fr": "Buste",
"de": "Büste",
"it": "Busto",
"ru": "Бюст",
"ja": "胸像",
"zh_Hant": "半身像",
"nb_NO": "Byste"
}
},
{
"if": "artwork_type=stone",
"then": {
"en": "Stone",
"nl": "Steen",
"fr": "Rocher",
"de": "Stein",
"it": "Masso",
"ru": "Камень",
"ja": "石",
"zh_Hant": "石頭",
"nb_NO": "Stein"
}
},
{
"if": "artwork_type=installation",
"then": {
"en": "Installation",
"nl": "Installatie",
"fr": "Installation",
"de": "Installation",
"it": "Istallazione",
"ru": "Инсталляция",
"ja": "インスタレーション",
"zh_Hant": "安裝",
"nb_NO": "Installasjon"
}
},
{
"if": "artwork_type=graffiti",
"then": {
"en": "Graffiti",
"nl": "Graffiti",
"fr": "Graffiti",
"de": "Graffiti",
"it": "Graffiti",
"ru": "Граффити",
"ja": "落書き",
"zh_Hant": "塗鴨",
"nb_NO": "Graffiti"
}
},
{
"if": "artwork_type=relief",
"then": {
"en": "Relief",
"nl": "Reliëf",
"fr": "Relief",
"de": "Relief",
"it": "Rilievo",
"ru": "Рельеф",
"ja": "レリーフ",
"zh_Hant": "寬慰",
"nb_NO": "Relieff"
}
},
{
"if": "artwork_type=azulejo",
"then": {
"en": "Azulejo (Spanish decorative tilework)",
"nl": "Azulejo (Spaanse siertegels)",
"fr": "Azulejo (faïence latine)",
"de": "Azulejo (spanische dekorative Fliesenarbeit)",
"it": "Azulejo (ornamento decorativo piastrellato spagnolo)",
"ru": "Азуле́жу (испанская роспись глазурованной керамической плитки)",
"ja": "Azulejo (スペインの装飾タイル)",
"zh_Hant": "Azulejo (西班牙雕塑作品名稱)",
"nb_NO": "Azulejo (Spansk dekorativt flisverk)"
}
},
{
"if": "artwork_type=tilework",
"then": {
"en": "Tilework",
"nl": "Tegelwerk",
"fr": "Carrelage",
"de": "Fliesenarbeit",
"it": "Mosaico di piastrelle",
"ru": "Плитка (мозаика)",
"ja": "タイルワーク",
"zh_Hant": "瓷磚",
"nb_NO": "Flisarbeid"
}
}
],
"id": "artwork-artwork_type"
},
{
"question": {
"en": "Which artist created this?",
"nl": "Welke kunstenaar creëerde dit kunstwerk?",
"fr": "Quel artiste a créé cette œuvre ?",
"de": "Welcher Künstler hat das geschaffen?",
"it": "Quale artista ha creato questopera?",
"ru": "Какой художник создал это?",
"ja": "どのアーティストが作ったんですか?",
"zh_Hant": "創造這個的藝術家是誰?",
"nb_NO": "Hvilken artist lagde dette?"
},
"render": {
"en": "Created by {artist_name}",
"nl": "Gecreëerd door {artist_name}",
"fr": "Créé par {artist_name}",
"de": "Erstellt von {artist_name}",
"it": "Creato da {artist_name}",
"ru": "Создано {artist_name}",
"ja": "作成者:{artist_name}",
"zh_Hant": "{artist_name} 創作",
"nb_NO": "Laget av {artist_name}"
},
"freeform": {
"key": "artist_name"
},
"id": "artwork-artist_name"
},
{
"question": {
"en": "Is there a website with more information about this artwork?",
"nl": "Is er een website met meer informatie over dit kunstwerk?",
"fr": "Existe-t-il un site web où trouver plus d'informations sur cette œuvre d'art ?",
"de": "Gibt es eine Website mit weiteren Informationen über dieses Kunstwerk?",
"it": "Esiste un sito web con maggiori informazioni su questopera?",
"ru": "Есть ли сайт с более подробной информацией об этой работе?",
"ja": "この作品についての詳しい情報はどのウェブサイトにありますか?",
"zh_Hant": "在那個網站能夠找到更多藝術品的資訊?",
"nb_NO": "Finnes det en nettside med mer info om dette kunstverket?"
},
"render": {
"en": "More information on <a href='{website}' target='_blank'>this website</a>",
"nl": "Meer informatie op <a href='{website}' target='_blank'>deze website</a>",
"fr": "Plus d'info <a href='{website}' target='_blank'>sûr ce site web</a>",
"de": "Weitere Informationen auf <a href='{website}' target='_blank'>dieser Webseite</a>",
"id": "Info lanjut tersedia di <a href='{website}' target='_blank'>laman web</a> ini.",
"it": "Ulteriori informazioni su <a href='{website}' target='_blank'>questo sito web</a>",
"ru": "Больше информации на <a href='{website}' target='_blank'>этом сайте</a>",
"ja": "<a href='{website}' target='_blank'>Webサイト</a>に詳細情報がある",
"zh_Hant": "<a href='{website}' target='_blank'>這個網站</a>有更多資訊",
"nb_NO": "Mer info er å finne på <a href='{website}' target='_blank'>denne nettsiden</a>"
},
"freeform": {
"key": "website",
"type": "url"
},
"id": "artwork-website"
},
{
"question": {
"en": "Which Wikidata-entry corresponds with <b>this artwork</b>?",
"nl": "Welk Wikidata-item beschrijft <b>dit kunstwerk</b>?",
"fr": "Quelle entrée Wikidata correspond à <b>cette œuvre d'art</b> ?",
"de": "Welcher Wikidata-Eintrag entspricht <b>diesem Kunstwerk</b>?",
"it": "Quale elemento Wikidata corrisponde a <b>questopera darte</b>?",
"ru": "Какая запись в Wikidata соответсвует <b>этой работе</b>?",
"ja": "<b>このアートワーク</b>に関するWikidataのエントリーはどれですか?",
"zh_Hant": "<b>這個藝術品</b>有那個對應的 Wikidata 項目?",
"nb_NO": "Hvilken Wikipedia-oppføring samsvarer med <b>dette kunstverket</b>?"
},
"render": {
"en": "Corresponds with <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"nl": "Komt overeen met <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"fr": "Correspond à <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"de": "Entspricht <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"it": "Corrisponde a <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"ru": "Запись об этой работе в wikidata: <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"ja": "<a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>に関連する",
"zh_Hant": "與 <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>對應",
"nb_NO": "Samsvarer med <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>"
},
"freeform": {
"key": "wikidata",
"type": "wikidata"
},
"id": "artwork-wikidata"
}
],
"deletion": {
"softDeletionTags": {
"and": [
"razed:tourism=artwork",
"tourism="
]
},
"neededChangesets": 5
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -600,5 +600,18 @@
"preferredBackground": "photo"
}
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity=bench",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -255,5 +255,18 @@
],
"multiAnswer": true
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -145,5 +145,18 @@
"roaming": false,
"id": "bike_cleaning-charge"
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -508,5 +508,18 @@
},
"id": "Cargo bike capacity?"
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -731,5 +731,18 @@
"service:bicycle:pump=no"
]
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -100,5 +100,18 @@
"amenity=binoculars"
]
}
},
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -296,5 +296,17 @@
}
]
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
}
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -181,5 +181,11 @@
}
]
}
]
],
"deletion": {
"softDeletionTags": {
"and": ["amenity=","disused:amenity:={amenity}"]
}
},
"allowMove": true
}

View file

@ -3655,5 +3655,18 @@
],
"eraseInvalidValues": true
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -539,5 +539,18 @@
},
"id": "defibrillator-fixme"
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:emergency:=defibrillator}",
"emergency="
]
},
"neededChangesets": 5
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -166,5 +166,18 @@
},
"condition": "_closest_other_drinking_water_id~*"
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -624,5 +624,11 @@
}
]
}
]
],
"deletion": {
"softDeletionTags": {
"and": ["amenity=","disused:amenity:={amenity}"]
}
},
"allowMove": true
}

View file

@ -187,5 +187,18 @@
},
"id": "ghost_bike-start_date"
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"razed:memorial:=ghost_bike",
"memorial="
]
},
"neededChangesets": 50
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -53,5 +53,20 @@
"ru": "Информационный щит"
}
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:tourism:=information",
"tourism=" ,
"razed:information=board",
"information="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -227,5 +227,18 @@
}
}
],
"wayHandling": 2
"wayHandling": 2,
"deletion": {
"softDeletionTags": {
"and": [
"razed:tourism:=information",
"tourism="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -180,5 +180,9 @@
],
"eraseInvalidValues": true
}
]
],
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -81,5 +81,18 @@
"nl": "Voeg hier een parking voor auto's toe"
}
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -101,5 +101,18 @@
}
}
],
"wayHandling": 1
"wayHandling": 1,
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -0,0 +1,323 @@
{
"id": "shops",
"name": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"minzoom": 16,
"source": {
"osmTags": {
"and": [
"shop~*"
]
}
},
"title": {
"render": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"mappings": [
{
"if": {
"and": [
"name~*"
]
},
"then": {
"en": "{name}",
"fr": "{name}",
"ru": "{name}",
"ja": "{name}"
}
},
{
"if": {
"and": [
"shop!~yes"
]
},
"then": {
"en": "{shop}",
"fr": "{shop}",
"ru": "{shop}",
"ja": "{shop}"
}
}
]
},
"description": {
"en": "A shop",
"fr": "Un magasin",
"ja": "ショップ",
"nl": "Een winkel",
"ru": "Магазин"
},
"tagRenderings": [
"images",
{
"question": {
"en": "What is the name of this shop?",
"fr": "Qu'est-ce que le nom de ce magasin?",
"ru": "Как называется этот магазин?",
"ja": "このお店の名前は何ですか?",
"nl": "Wat is de naam van deze winkel?"
},
"render": "This shop is called <i>{name}</i>",
"freeform": {
"key": "name"
},
"id": "shops-name"
},
{
"render": {
"en": "This shop sells {shop}",
"fr": "Ce magasin vends {shop}",
"ja": "こちらのお店では{shop}を販売しております"
},
"question": {
"en": "What does this shop sell?",
"fr": "Que vends ce magasin ?",
"ja": "このお店では何を売っていますか?",
"ru": "Что продаётся в этом магазине?"
},
"freeform": {
"key": "shop"
},
"mappings": [
{
"if": {
"and": [
"shop=convenience"
]
},
"then": {
"en": "Convenience store",
"fr": "Épicerie/superette",
"ja": "コンビニエンスストア"
}
},
{
"if": {
"and": [
"shop=supermarket"
]
},
"then": {
"en": "Supermarket",
"fr": "Supermarché",
"ru": "Супермаркет",
"ja": "スーパーマーケット",
"nl": "Supermarkt"
}
},
{
"if": {
"and": [
"shop=clothes"
]
},
"then": {
"en": "Clothing store",
"fr": "Magasin de vêtements",
"ru": "Магазин одежды",
"ja": "衣料品店"
}
},
{
"if": {
"and": [
"shop=hairdresser"
]
},
"then": {
"en": "Hairdresser",
"fr": "Coiffeur",
"ru": "Парикмахерская",
"ja": "理容師",
"nl": "Kapper"
}
},
{
"if": {
"and": [
"shop=bakery"
]
},
"then": {
"en": "Bakery",
"fr": "Boulangerie",
"ja": "ベーカリー",
"nl": "Bakkerij"
}
},
{
"if": {
"and": [
"shop=car_repair"
]
},
"then": {
"en": "Car repair (garage)",
"fr": "Garagiste",
"ja": "自動車修理(ガレージ)"
}
},
{
"if": {
"and": [
"shop=car"
]
},
"then": {
"en": "Car dealer",
"fr": "Concessionnaire",
"ru": "Автосалон",
"ja": "自動車ディーラー"
}
}
],
"id": "shops-shop"
},
{
"render": {
"en": "<a href='tel:{phone}'>{phone}</a>",
"fr": "<a href='tel:{phone}'>{phone}</a>",
"ca": "<a href='tel:{phone}'>{phone}</a>",
"id": "<a href='tel:{phone}'>{phone}</a>",
"ru": "<a href='tel:{phone}'>{phone}</a>",
"ja": "<a href='tel:{phone}'>{phone}</a>"
},
"question": {
"en": "What is the phone number?",
"fr": "Quel est le numéro de téléphone ?",
"ja": "電話番号は何番ですか?",
"nl": "Wat is het telefoonnummer?",
"ru": "Какой телефон?"
},
"freeform": {
"key": "phone",
"type": "phone"
},
"id": "shops-phone"
},
{
"render": {
"en": "<a href='{website}'>{website}</a>",
"fr": "<a href='{website}'>{website}</a>",
"ca": "<a href='{website}'>{website}</a>",
"id": "<a href='{website}'>{website}</a>",
"ru": "<a href='{website}'>{website}</a>",
"ja": "<a href='{website}'>{website}</a>"
},
"question": {
"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?",
"ru": "Какой веб-сайт у этого магазина?"
},
"freeform": {
"key": "website",
"type": "url"
},
"id": "shops-website"
},
{
"render": {
"en": "<a href='mailto:{email}'>{email}</a>",
"fr": "<a href='mailto:{email}'>{email}</a>",
"id": "<a href='mailto:{email}'>{email}</a>",
"ru": "<a href='mailto:{email}'>{email}</a>",
"ja": "<a href='mailto:{email}'>{email}</a>"
},
"question": {
"en": "What is the email address of this shop?",
"fr": "Quelle est l'adresse électronique de ce magasin ?",
"ja": "このお店のメールアドレスは何ですか?",
"ru": "Каков адрес электронной почты этого магазина?",
"nl": "Wat is het e-mailadres van deze winkel?"
},
"freeform": {
"key": "email",
"type": "email"
},
"id": "shops-email"
},
{
"render": {
"en": "{opening_hours_table(opening_hours)}",
"fr": "{opening_hours_table(opening_hours)}",
"ru": "{opening_hours_table(opening_hours)}",
"ja": "{opening_hours_table(opening_hours)}"
},
"question": {
"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?",
"ru": "Каковы часы работы этого магазина?"
},
"freeform": {
"key": "opening_hours",
"type": "opening_hours"
},
"id": "shops-opening_hours"
},
"questions",
"reviews"
],
"icon": {
"render": "./assets/themes/shops/shop.svg"
},
"iconOverlays": [
{
"if": "opening_hours~*",
"then": "isOpen",
"badge": true
}
],
"width": {
"render": "8"
},
"iconSize": {
"render": "40,40,center"
},
"color": {
"render": "#00f"
},
"presets": [
{
"tags": [
"shop=yes"
],
"title": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"description": {
"en": "Add a new shop",
"fr": "Ajouter un nouveau magasin",
"ru": "Добавить новый магазин",
"ja": "新しい店を追加する",
"nl": "Voeg een nieuwe winkel toe"
}
}
],
"wayHandling": 2,
"deletion": {
"softDeletionTags": {
"and": ["amenity=","disused:amenity:={amenity}"]
}
},
"allowMove": true
}

View file

@ -498,5 +498,18 @@
}
]
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -115,5 +115,18 @@
"preferredBackground": "photo"
}
}
]
],
"deletion": {
"softDeletionTags": {
"and": [
"disused:amenity:={amenity}",
"amenity="
]
},
"neededChangesets": 1
},
"allowMove": {
"enableRelocation": false,
"enableImproveAccuraccy": true
}
}

View file

@ -955,6 +955,32 @@
],
"sources": []
},
{
"path": "move.svg",
"license": "CC-BY-SA 3.0 Unported",
"authors": [
"MGalloway (WMF)"
],
"sources": [
"https://commons.wikimedia.org/wiki/File:Move_icon.svg"
]
},
{
"path": "move_confirm.svg",
"license": "CC0",
"authors": [
"Pieter Vander Vennet"
],
"sources": []
},
{
"path": "move_not_allowed.svg",
"license": "CC0",
"authors": [
"Pieter Vander Vennet"
],
"sources": []
},
{
"path": "no_checkmark.svg",
"license": "CC0; trivial",
@ -1137,6 +1163,14 @@
"authors": [],
"sources": []
},
{
"path": "relocation.svg",
"license": "CC0",
"authors": [
"Pieter Vander Vennet"
],
"sources": []
},
{
"path": "ring.svg",
"license": "CC0; trivial",

7
assets/svg/move.svg Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
move
</title>
<path d="M19 10l-4-3v2h-4V5h2l-3-4-3 4h2v4H5V7l-4 3 4 3v-2h4v4H7l3 4 3-4h-2v-4h4v2l4-3z"/>
</svg>

After

Width:  |  Height:  |  Size: 260 B

281
assets/svg/move_confirm.svg Normal file
View file

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 20 20"
version="1.1"
id="svg6"
sodipodi:docname="move_confirm.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10">
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="17.436001"
width="25.4126"
y="52.703999"
x="58.84"
id="filter0_d">
<feFlood
id="feFlood52"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix54"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset56"
dy="4" />
<feGaussianBlur
id="feGaussianBlur58"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix60"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend62"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend64"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="38"
width="38.000099"
y="15"
x="14"
id="filter1_d">
<feFlood
id="feFlood67"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix69"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset71"
dy="4" />
<feGaussianBlur
id="feGaussianBlur73"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix75"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend77"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend79"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="53"
width="53"
y="7"
x="39.5"
id="filter2_d">
<feFlood
id="feFlood82"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix84"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset86"
dy="4" />
<feGaussianBlur
id="feGaussianBlur88"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix90"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend92"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend94"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="38.142899"
width="54.766701"
y="54"
x="11"
id="filter3_d">
<feFlood
id="feFlood97"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix99"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset101"
dy="4" />
<feGaussianBlur
id="feGaussianBlur103"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix105"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend107"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend109"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="29"
width="28"
y="64"
x="41"
id="filter4_d">
<feFlood
id="feFlood112"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix114"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset116"
dy="4" />
<feGaussianBlur
id="feGaussianBlur118"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix120"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend122"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend124"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<clipPath
id="clip0">
<rect
style="fill:#ffffff"
y="0"
x="0"
id="rect127"
transform="rotate(-45,57.35965,-37.759145)"
height="31.819799"
width="31.819799" />
</clipPath>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="960"
inkscape:window-height="1003"
id="namedview8"
showgrid="false"
inkscape:zoom="11.8"
inkscape:cx="14.211286"
inkscape:cy="-5.2054641"
inkscape:window-x="960"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<title
id="title2">
move
</title>
<path
d="M19 10l-4-3v2h-4V5h2l-3-4-3 4h2v4H5V7l-4 3 4 3v-2h4v4H7l3 4 3-4h-2v-4h4v2l4-3z"
id="path4" />
<g
id="g942"
transform="matrix(0.42132349,0,0,0.42132349,8.3654416,8.6801476)">
<circle
style="fill:#37d649;fill-opacity:1;stroke:none;stroke-width:0.20323335;stroke-opacity:1"
id="circle4"
r="9.9584341"
cy="15.401706"
cx="16.186441" />
<path
inkscape:transform-center-y="40.988227"
inkscape:transform-center-x="14.392136"
id="path924"
d="m 11,15 4.5,5 6,-8"
inkscape:connector-curvature="0"
style="fill:none;stroke:#ffffff;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View file

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="20"
height="20"
viewBox="0 0 20 20"
version="1.1"
id="svg6"
sodipodi:docname="move_not_allowed.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10">
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="17.436001"
width="25.4126"
y="52.703999"
x="58.84"
id="filter0_d">
<feFlood
id="feFlood52"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix54"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset56"
dy="4" />
<feGaussianBlur
id="feGaussianBlur58"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix60"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend62"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend64"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="38"
width="38.000099"
y="15"
x="14"
id="filter1_d">
<feFlood
id="feFlood67"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix69"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset71"
dy="4" />
<feGaussianBlur
id="feGaussianBlur73"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix75"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend77"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend79"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="53"
width="53"
y="7"
x="39.5"
id="filter2_d">
<feFlood
id="feFlood82"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix84"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset86"
dy="4" />
<feGaussianBlur
id="feGaussianBlur88"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix90"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend92"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend94"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="38.142899"
width="54.766701"
y="54"
x="11"
id="filter3_d">
<feFlood
id="feFlood97"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix99"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset101"
dy="4" />
<feGaussianBlur
id="feGaussianBlur103"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix105"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend107"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend109"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
filterUnits="userSpaceOnUse"
height="29"
width="28"
y="64"
x="41"
id="filter4_d">
<feFlood
id="feFlood112"
result="BackgroundImageFix"
flood-opacity="0" />
<feColorMatrix
id="feColorMatrix114"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
type="matrix"
in="SourceAlpha" />
<feOffset
id="feOffset116"
dy="4" />
<feGaussianBlur
id="feGaussianBlur118"
stdDeviation="2" />
<feColorMatrix
id="feColorMatrix120"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
type="matrix" />
<feBlend
id="feBlend122"
result="effect1_dropShadow"
in2="BackgroundImageFix"
mode="normal" />
<feBlend
id="feBlend124"
result="shape"
in2="effect1_dropShadow"
in="SourceGraphic"
mode="normal" />
</filter>
<clipPath
id="clip0">
<rect
style="fill:#ffffff"
y="0"
x="0"
id="rect127"
transform="rotate(-45,57.35965,-37.759145)"
height="31.819799"
width="31.819799" />
</clipPath>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1003"
id="namedview8"
showgrid="false"
inkscape:zoom="11.8"
inkscape:cx="-2.6072629"
inkscape:cy="5.7824568"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<title
id="title2">
move
</title>
<path
d="M19 10l-4-3v2h-4V5h2l-3-4-3 4h2v4H5V7l-4 3 4 3v-2h4v4H7l3 4 3-4h-2v-4h4v2l4-3z"
id="path4" />
<circle
cx="14.76144"
cy="14.745519"
id="circle4"
style="fill:#e72c44;fill-opacity:1;stroke:none;stroke-width:0.09427451;stroke-opacity:1"
r="4.619451" />
<g
transform="matrix(0.20049776,0,0,0.20049776,12.17322,-42.226005)"
id="layer1"
inkscape:label="Layer 1">
<path
inkscape:connector-curvature="0"
id="path815"
d="M 22.551907,293.60329 2.927466,273.97885 v 0"
style="fill:none;stroke:#ffffff;stroke-width:6.0128417;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path815-3"
d="M 22.581499,273.88422 2.9570535,293.50867 v 0"
style="fill:none;stroke:#fcffff;stroke-width:6.0128417;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

61
assets/svg/relocation.svg Normal file
View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="16px"
id="Layer_1"
style="enable-background:new 0 0 16 16;fill: #000000;"
version="1.1"
viewBox="0 0 16 16"
width="16px"
xml:space="preserve"
sodipodi:docname="relocation.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1003"
id="namedview5"
showgrid="false"
inkscape:zoom="20.85965"
inkscape:cx="10.143178"
inkscape:cy="10.590388"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
d="M 15.968433,9.4957631 14.991526,8.5195302 V 6.1271188 c 0,-0.3705509 -0.303177,-0.6737288 -0.673729,-0.6737288 h -0.673728 c -0.370553,0 -0.67373,0.3031779 -0.67373,0.6737288 v 0.372572 L 11.622881,5.1535806 C 11.438954,4.9797587 11.270522,4.7796611 10.949152,4.7796611 c -0.321369,0 -0.489801,0.2000976 -0.67373,0.3739195 L 5.9298726,9.4957631 c -0.2102033,0.21896 -0.3705508,0.378636 -0.3705508,0.6737279 0,0.379309 0.2910508,0.673729 0.6737289,0.673729 h 0.6737287 v 4.042372 c 0,0.370553 0.303178,0.67373 0.6737289,0.67373 h 2.0211871 v -3.368645 c 0,-0.37055 0.303178,-0.673727 0.6737266,-0.673727 h 1.347459 c 0.37055,0 0.673728,0.303177 0.673728,0.673727 v 3.368645 h 2.021188 c 0.370552,0 0.673729,-0.303177 0.673729,-0.67373 V 10.84322 h 0.673729 c 0.382678,0 0.673729,-0.29442 0.673729,-0.673729 0,-0.2950919 -0.160347,-0.4547679 -0.370551,-0.6737279 z"
id="path2"
inkscape:connector-curvature="0"
style="stroke-width:0.67372882" />
<path
d="M 7.5468103,3.8654038 6.9092727,3.2283064 V 1.6669989 c 0,-0.2418245 -0.197856,-0.4396809 -0.4396814,-0.4396809 H 6.0299107 c -0.2418254,0 -0.4396815,0.1978564 -0.4396815,0.4396809 V 1.9101425 L 4.7108672,1.0316599 C 4.5908347,0.91822221 4.4809147,0.78763691 4.2711857,0.78763691 c -0.2097281,0 -0.319648,0.1305853 -0.4396814,0.24402299 L 0.99556255,3.8654038 c -0.1371804,0.1428956 -0.2418245,0.2471013 -0.2418245,0.4396805 0,0.2475406 0.1899421,0.4396815 0.43968095,0.4396815 H 1.6331 v 2.6380853 c 0,0.2418254 0.1978565,0.4396815 0.439681,0.4396815 H 3.3918246 V 5.624127 c 0,-0.2418237 0.1978561,-0.4396797 0.4396797,-0.4396797 h 0.8793629 c 0.2418236,0 0.4396805,0.197856 0.4396805,0.4396797 v 2.1984056 h 1.3190436 c 0.2418254,0 0.4396814,-0.1978561 0.4396814,-0.4396815 V 4.7447658 h 0.4396806 c 0.2497393,0 0.4396815,-0.1921409 0.4396815,-0.4396815 0,-0.1925792 -0.104644,-0.2967849 -0.2418245,-0.4396805 z"
id="path2-3"
inkscape:connector-curvature="0"
style="fill:none;stroke:#808080;stroke-width:0.43968096;stroke-opacity:1" /><path
style="fill:none;stroke:#000000;stroke-width:0.86116266;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 4.2635907,11.093613 1.7780944,1.509272 -1.7780944,1.504628"
id="path821"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" /><path
style="fill:none;stroke:#000000;stroke-width:0.79756224;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 5.5446079,12.650872 C 4.7207387,12.633302 2.1674541,13.155351 2.3608287,10.176205"
id="path815"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" /></svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -49,390 +49,6 @@
"startLat": 0,
"startLon": 0,
"layers": [
{
"id": "artwork",
"name": {
"en": "Artworks",
"nl": "Kunstwerken",
"fr": "Œuvres d'art",
"de": "Kunstwerke",
"id": "Karya seni",
"it": "Opere darte",
"ru": "Произведения искусства",
"es": "Obras de arte",
"ja": "美術品",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
},
"source": {
"osmTags": "tourism=artwork"
},
"title": {
"render": {
"en": "Artwork",
"nl": "Kunstwerk",
"fr": "Œuvre d'art",
"de": "Kunstwerk",
"id": "Karya seni",
"it": "Opera darte",
"ru": "Художественная работа",
"es": "Obra de arte",
"ja": "アートワーク",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
},
"mappings": [
{
"if": "name~*",
"then": {
"en": "Artwork <i>{name}</i>",
"nl": "Kunstwerk <i>{name}</i>",
"fr": "Œuvre d'art <i>{name}</i>",
"de": "Kunstwerk <i>{name}</i>",
"id": "Karya seni <i>{name}</i>",
"it": "Opera <i>{name}</i>",
"ru": "Художественная работа <i>{name}</i>",
"es": "Obra de arte <i>{nombre}</i>",
"ja": "アートワーク <i>{name}</i>",
"zh_Hant": "藝術品<i>{name}</i>"
}
}
]
},
"icon": {
"render": "./assets/themes/artwork/artwork.svg"
},
"color": {
"render": "#0000ff"
},
"width": {
"render": "10"
},
"description": {
"en": "Diverse pieces of artwork",
"nl": "Verschillende soorten kunstwerken",
"fr": "Diverses œuvres d'art",
"de": "Verschiedene Kunstwerke",
"it": "Diverse opere darte",
"ru": "Разнообразные произведения искусства",
"es": "Diversas piezas de obras de arte",
"ja": "多様な作品",
"zh_Hant": "不同類型的藝術品"
},
"minzoom": 12,
"wayHandling": 2,
"presets": [
{
"tags": [
"tourism=artwork"
],
"title": {
"en": "Artwork",
"nl": "Kunstwerk",
"fr": "Œuvre d'art",
"de": "Kunstwerk",
"it": "Opera darte",
"ru": "Художественная работа",
"es": "Obra de arte",
"ja": "アートワーク",
"zh_Hant": "藝術品",
"nb_NO": "Kunstverk"
}
}
],
"tagRenderings": [
"images",
{
"render": {
"en": "This is a {artwork_type}",
"nl": "Dit is een {artwork_type}",
"fr": "Type d'œuvre : {artwork_type}",
"de": "Dies ist ein {artwork_type}",
"it": "Si tratta di un {artwork_type}",
"ru": "Это {artwork_type}",
"es": "Esta es un {artwork_type}",
"ja": "これは{artwork_type}です",
"zh_Hant": "這是 {artwork_type}",
"nb_NO": "Dette er et kunstverk av typen {artwork_type}"
},
"question": {
"en": "What is the type of this artwork?",
"nl": "Wat voor soort kunstwerk is dit?",
"fr": "Quel est le type de cette œuvre d'art?",
"de": "Was ist die Art dieses Kunstwerks?",
"it": "Che tipo di opera darte è questo?",
"ru": "К какому типу относится эта работа?",
"es": "Cuál es el tipo de esta obra de arte?",
"ja": "この作品の種類は何ですか?",
"zh_Hant": "這是什麼類型的藝術品?",
"nb_NO": "Hvilken type kunstverk er dette?"
},
"freeform": {
"key": "artwork_type",
"addExtraTags": [
"fixme=Artowrk type was added with the freeform, might need another check"
]
},
"mappings": [
{
"if": "artwork_type=architecture",
"then": {
"en": "Architecture",
"nl": "Architectuur",
"fr": "Architecture",
"de": "Architektur",
"it": "Architettura",
"ru": "Архитектура",
"ja": "建物",
"zh_Hant": "建築物",
"nb_NO": "Arkitektur"
}
},
{
"if": "artwork_type=mural",
"then": {
"en": "Mural",
"nl": "Muurschildering",
"fr": "Peinture murale",
"de": "Wandbild",
"it": "Murale",
"ru": "Фреска",
"ja": "壁画",
"zh_Hant": "壁畫",
"nb_NO": "Veggmaleri"
}
},
{
"if": "artwork_type=painting",
"then": {
"en": "Painting",
"nl": "Schilderij",
"fr": "Peinture",
"de": "Malerei",
"it": "Dipinto",
"ru": "Живопись",
"ja": "絵画",
"zh_Hant": "繪畫",
"nb_NO": "Maleri"
}
},
{
"if": "artwork_type=sculpture",
"then": {
"en": "Sculpture",
"nl": "Beeldhouwwerk",
"fr": "Sculpture",
"de": "Skulptur",
"it": "Scultura",
"ru": "Скульптура",
"ja": "彫刻",
"zh_Hant": "雕塑",
"nb_NO": "Skulptur"
}
},
{
"if": "artwork_type=statue",
"then": {
"en": "Statue",
"nl": "Standbeeld",
"fr": "Statue",
"de": "Statue",
"it": "Statua",
"ru": "Статуя",
"ja": "彫像",
"zh_Hant": "雕像",
"nb_NO": "Statue"
}
},
{
"if": "artwork_type=bust",
"then": {
"en": "Bust",
"nl": "Buste",
"fr": "Buste",
"de": "Büste",
"it": "Busto",
"ru": "Бюст",
"ja": "胸像",
"zh_Hant": "半身像",
"nb_NO": "Byste"
}
},
{
"if": "artwork_type=stone",
"then": {
"en": "Stone",
"nl": "Steen",
"fr": "Rocher",
"de": "Stein",
"it": "Masso",
"ru": "Камень",
"ja": "石",
"zh_Hant": "石頭",
"nb_NO": "Stein"
}
},
{
"if": "artwork_type=installation",
"then": {
"en": "Installation",
"nl": "Installatie",
"fr": "Installation",
"de": "Installation",
"it": "Istallazione",
"ru": "Инсталляция",
"ja": "インスタレーション",
"zh_Hant": "安裝",
"nb_NO": "Installasjon"
}
},
{
"if": "artwork_type=graffiti",
"then": {
"en": "Graffiti",
"nl": "Graffiti",
"fr": "Graffiti",
"de": "Graffiti",
"it": "Graffiti",
"ru": "Граффити",
"ja": "落書き",
"zh_Hant": "塗鴨",
"nb_NO": "Graffiti"
}
},
{
"if": "artwork_type=relief",
"then": {
"en": "Relief",
"nl": "Reliëf",
"fr": "Relief",
"de": "Relief",
"it": "Rilievo",
"ru": "Рельеф",
"ja": "レリーフ",
"zh_Hant": "寬慰",
"nb_NO": "Relieff"
}
},
{
"if": "artwork_type=azulejo",
"then": {
"en": "Azulejo (Spanish decorative tilework)",
"nl": "Azulejo (Spaanse siertegels)",
"fr": "Azulejo (faïence latine)",
"de": "Azulejo (spanische dekorative Fliesenarbeit)",
"it": "Azulejo (ornamento decorativo piastrellato spagnolo)",
"ru": "Азуле́жу (испанская роспись глазурованной керамической плитки)",
"ja": "Azulejo (スペインの装飾タイル)",
"zh_Hant": "Azulejo (西班牙雕塑作品名稱)",
"nb_NO": "Azulejo (Spansk dekorativt flisverk)"
}
},
{
"if": "artwork_type=tilework",
"then": {
"en": "Tilework",
"nl": "Tegelwerk",
"fr": "Carrelage",
"de": "Fliesenarbeit",
"it": "Mosaico di piastrelle",
"ru": "Плитка (мозаика)",
"ja": "タイルワーク",
"zh_Hant": "瓷磚",
"nb_NO": "Flisarbeid"
}
}
],
"id": "artwork-artwork_type"
},
{
"question": {
"en": "Which artist created this?",
"nl": "Welke kunstenaar creëerde dit kunstwerk?",
"fr": "Quel artiste a créé cette œuvre ?",
"de": "Welcher Künstler hat das geschaffen?",
"it": "Quale artista ha creato questopera?",
"ru": "Какой художник создал это?",
"ja": "どのアーティストが作ったんですか?",
"zh_Hant": "創造這個的藝術家是誰?",
"nb_NO": "Hvilken artist lagde dette?"
},
"render": {
"en": "Created by {artist_name}",
"nl": "Gecreëerd door {artist_name}",
"fr": "Créé par {artist_name}",
"de": "Erstellt von {artist_name}",
"it": "Creato da {artist_name}",
"ru": "Создано {artist_name}",
"ja": "作成者:{artist_name}",
"zh_Hant": "{artist_name} 創作",
"nb_NO": "Laget av {artist_name}"
},
"freeform": {
"key": "artist_name"
},
"id": "artwork-artist_name"
},
{
"question": {
"en": "Is there a website with more information about this artwork?",
"nl": "Is er een website met meer informatie over dit kunstwerk?",
"fr": "Existe-t-il un site web où trouver plus d'informations sur cette œuvre d'art ?",
"de": "Gibt es eine Website mit weiteren Informationen über dieses Kunstwerk?",
"it": "Esiste un sito web con maggiori informazioni su questopera?",
"ru": "Есть ли сайт с более подробной информацией об этой работе?",
"ja": "この作品についての詳しい情報はどのウェブサイトにありますか?",
"zh_Hant": "在那個網站能夠找到更多藝術品的資訊?",
"nb_NO": "Finnes det en nettside med mer info om dette kunstverket?"
},
"render": {
"en": "More information on <a href='{website}' target='_blank'>this website</a>",
"nl": "Meer informatie op <a href='{website}' target='_blank'>deze website</a>",
"fr": "Plus d'info <a href='{website}' target='_blank'>sûr ce site web</a>",
"de": "Weitere Informationen auf <a href='{website}' target='_blank'>dieser Webseite</a>",
"id": "Info lanjut tersedia di <a href='{website}' target='_blank'>laman web</a> ini.",
"it": "Ulteriori informazioni su <a href='{website}' target='_blank'>questo sito web</a>",
"ru": "Больше информации на <a href='{website}' target='_blank'>этом сайте</a>",
"ja": "<a href='{website}' target='_blank'>Webサイト</a>に詳細情報がある",
"zh_Hant": "<a href='{website}' target='_blank'>這個網站</a>有更多資訊",
"nb_NO": "Mer info er å finne på <a href='{website}' target='_blank'>denne nettsiden</a>"
},
"freeform": {
"key": "website",
"type": "url"
},
"id": "artwork-website"
},
{
"question": {
"en": "Which Wikidata-entry corresponds with <b>this artwork</b>?",
"nl": "Welk Wikidata-item beschrijft <b>dit kunstwerk</b>?",
"fr": "Quelle entrée Wikidata correspond à <b>cette œuvre d'art</b> ?",
"de": "Welcher Wikidata-Eintrag entspricht <b>diesem Kunstwerk</b>?",
"it": "Quale elemento Wikidata corrisponde a <b>questopera darte</b>?",
"ru": "Какая запись в Wikidata соответсвует <b>этой работе</b>?",
"ja": "<b>このアートワーク</b>に関するWikidataのエントリーはどれですか?",
"zh_Hant": "<b>這個藝術品</b>有那個對應的 Wikidata 項目?",
"nb_NO": "Hvilken Wikipedia-oppføring samsvarer med <b>dette kunstverket</b>?"
},
"render": {
"en": "Corresponds with <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"nl": "Komt overeen met <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"fr": "Correspond à <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"de": "Entspricht <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"it": "Corrisponde a <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"ru": "Запись об этой работе в wikidata: <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>",
"ja": "<a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>に関連する",
"zh_Hant": "與 <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>對應",
"nb_NO": "Samsvarer med <a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank'>{wikidata}</a>"
},
"freeform": {
"key": "wikidata",
"type": "wikidata"
},
"id": "artwork-wikidata"
}
]
}
"artwork"
]
}

View file

@ -37,323 +37,6 @@
"widenFactor": 3,
"socialImage": "",
"layers": [
{
"id": "shops",
"name": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"minzoom": 16,
"source": {
"osmTags": {
"and": [
"shop~*"
]
}
},
"title": {
"render": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"mappings": [
{
"if": {
"and": [
"name~*"
]
},
"then": {
"en": "{name}",
"fr": "{name}",
"ru": "{name}",
"ja": "{name}"
}
},
{
"if": {
"and": [
"shop!~yes"
]
},
"then": {
"en": "{shop}",
"fr": "{shop}",
"ru": "{shop}",
"ja": "{shop}"
}
}
]
},
"description": {
"en": "A shop",
"fr": "Un magasin",
"ja": "ショップ",
"nl": "Een winkel",
"ru": "Магазин"
},
"tagRenderings": [
"images",
{
"question": {
"en": "What is the name of this shop?",
"fr": "Qu'est-ce que le nom de ce magasin?",
"ru": "Как называется этот магазин?",
"ja": "このお店の名前は何ですか?",
"nl": "Wat is de naam van deze winkel?"
},
"render": "This shop is called <i>{name}</i>",
"freeform": {
"key": "name"
},
"id": "shops-name"
},
{
"render": {
"en": "This shop sells {shop}",
"fr": "Ce magasin vends {shop}",
"ja": "こちらのお店では{shop}を販売しております"
},
"question": {
"en": "What does this shop sell?",
"fr": "Que vends ce magasin ?",
"ja": "このお店では何を売っていますか?",
"ru": "Что продаётся в этом магазине?"
},
"freeform": {
"key": "shop"
},
"mappings": [
{
"if": {
"and": [
"shop=convenience"
]
},
"then": {
"en": "Convenience store",
"fr": "Épicerie/superette",
"ja": "コンビニエンスストア"
}
},
{
"if": {
"and": [
"shop=supermarket"
]
},
"then": {
"en": "Supermarket",
"fr": "Supermarché",
"ru": "Супермаркет",
"ja": "スーパーマーケット",
"nl": "Supermarkt"
}
},
{
"if": {
"and": [
"shop=clothes"
]
},
"then": {
"en": "Clothing store",
"fr": "Magasin de vêtements",
"ru": "Магазин одежды",
"ja": "衣料品店"
}
},
{
"if": {
"and": [
"shop=hairdresser"
]
},
"then": {
"en": "Hairdresser",
"fr": "Coiffeur",
"ru": "Парикмахерская",
"ja": "理容師",
"nl": "Kapper"
}
},
{
"if": {
"and": [
"shop=bakery"
]
},
"then": {
"en": "Bakery",
"fr": "Boulangerie",
"ja": "ベーカリー",
"nl": "Bakkerij"
}
},
{
"if": {
"and": [
"shop=car_repair"
]
},
"then": {
"en": "Car repair (garage)",
"fr": "Garagiste",
"ja": "自動車修理(ガレージ)"
}
},
{
"if": {
"and": [
"shop=car"
]
},
"then": {
"en": "Car dealer",
"fr": "Concessionnaire",
"ru": "Автосалон",
"ja": "自動車ディーラー"
}
}
],
"id": "shops-shop"
},
{
"render": {
"en": "<a href='tel:{phone}'>{phone}</a>",
"fr": "<a href='tel:{phone}'>{phone}</a>",
"ca": "<a href='tel:{phone}'>{phone}</a>",
"id": "<a href='tel:{phone}'>{phone}</a>",
"ru": "<a href='tel:{phone}'>{phone}</a>",
"ja": "<a href='tel:{phone}'>{phone}</a>"
},
"question": {
"en": "What is the phone number?",
"fr": "Quel est le numéro de téléphone ?",
"ja": "電話番号は何番ですか?",
"nl": "Wat is het telefoonnummer?",
"ru": "Какой телефон?"
},
"freeform": {
"key": "phone",
"type": "phone"
},
"id": "shops-phone"
},
{
"render": {
"en": "<a href='{website}'>{website}</a>",
"fr": "<a href='{website}'>{website}</a>",
"ca": "<a href='{website}'>{website}</a>",
"id": "<a href='{website}'>{website}</a>",
"ru": "<a href='{website}'>{website}</a>",
"ja": "<a href='{website}'>{website}</a>"
},
"question": {
"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?",
"ru": "Какой веб-сайт у этого магазина?"
},
"freeform": {
"key": "website",
"type": "url"
},
"id": "shops-website"
},
{
"render": {
"en": "<a href='mailto:{email}'>{email}</a>",
"fr": "<a href='mailto:{email}'>{email}</a>",
"id": "<a href='mailto:{email}'>{email}</a>",
"ru": "<a href='mailto:{email}'>{email}</a>",
"ja": "<a href='mailto:{email}'>{email}</a>"
},
"question": {
"en": "What is the email address of this shop?",
"fr": "Quelle est l'adresse électronique de ce magasin ?",
"ja": "このお店のメールアドレスは何ですか?",
"ru": "Каков адрес электронной почты этого магазина?",
"nl": "Wat is het e-mailadres van deze winkel?"
},
"freeform": {
"key": "email",
"type": "email"
},
"id": "shops-email"
},
{
"render": {
"en": "{opening_hours_table(opening_hours)}",
"fr": "{opening_hours_table(opening_hours)}",
"ru": "{opening_hours_table(opening_hours)}",
"ja": "{opening_hours_table(opening_hours)}"
},
"question": {
"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?",
"ru": "Каковы часы работы этого магазина?"
},
"freeform": {
"key": "opening_hours",
"type": "opening_hours"
},
"id": "shops-opening_hours"
},
"questions",
"reviews"
],
"icon": {
"render": "./assets/themes/shops/shop.svg"
},
"iconOverlays": [
{
"if": "opening_hours~*",
"then": "isOpen",
"badge": true
}
],
"width": {
"render": "8"
},
"iconSize": {
"render": "40,40,center"
},
"color": {
"render": "#00f"
},
"presets": [
{
"tags": [
"shop=yes"
],
"title": {
"en": "Shop",
"fr": "Magasin",
"ru": "Магазин",
"ja": "店",
"nl": "Winkel"
},
"description": {
"en": "Add a new shop",
"fr": "Ajouter un nouveau magasin",
"ru": "Добавить новый магазин",
"ja": "新しい店を追加する",
"nl": "Voeg een nieuwe winkel toe"
}
}
],
"wayHandling": 2
}
],
"roamingRenderings": []
"shops"
]
}

View file

@ -820,6 +820,10 @@ video {
margin: 0.75rem;
}
.m-6 {
margin: 1.5rem;
}
.m-4 {
margin: 1rem;
}
@ -892,6 +896,10 @@ video {
margin-left: 0.5rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mt-4 {
margin-top: 1rem;
}
@ -916,10 +924,6 @@ video {
margin-right: 0.75rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.box-border {
box-sizing: border-box;
}
@ -2426,6 +2430,10 @@ li::marker {
padding: 0.5rem;
}
.md\:p-4 {
padding: 1rem;
}
.md\:p-3 {
padding: 0.75rem;
}

View file

@ -254,5 +254,30 @@
},
"multi_apply": {
"autoApply": "When changing the attributes {attr_names}, these attributes will automatically be changed on {count} other objects too"
},
"move": {
"loginToMove": "You must be logged in to move a point",
"inviteToMoveAgain": "Move this point again",
"moveTitle": "Move this point",
"whyMove": "Why do you want to move this point?",
"confirmMove": "Move here",
"pointIsMoved": "The point has been moved",
"zoomInFurther": "Zoom in further to confirm this move",
"selectReason": "Why do you move this object?",
"reasons": {
"reasonRelocation": "The object has been relocated to a totally different location",
"reasonInaccurate": "The location of this object is inaccurate and should be moved a few meter"
},
"inviteToMove": {
"generic": "Move this point",
"reasonInaccurate": "Improve the accuracy of this point",
"reasonRelocation": "Move this object to a another place because it has relocated"
},
"cannotBeMoved": "This feature cannot be moved.",
"isWay": "This feature is a way. Use another OpenStreetMap editor to move it.",
"isRelation": "This feature is a relation and can not be moved",
"partOfAWay": "This feature is part of another way. Use another editor to move it",
"partOfRelation": "This feature is part of a relation. Use another editor to move it",
"cancel": "Cancel move"
}
}

View file

@ -7,11 +7,11 @@
"homepage": "https://mapcomplete.osm.be",
"main": "index.js",
"scripts": {
"increase-memory": "export NODE_OPTIONS=--max_old_space_size=6182",
"increase-memory": "export NODE_OPTIONS=--max_old_space_size=8364",
"start": "npm run start:prepare && npm-run-all --parallel start:parallel:*",
"strt": "npm run start:prepare && npm run start:parallel:parcel",
"start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory",
"start:parallel:parcel": "parcel *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*",
"start:parallel:parcel": "node --max_old_space_size=8000 $(which parcel) serve *.html UI/** Logic/** assets/*.json assets/svg/* assets/generated/* assets/layers/*/*.svg assets/layers/*/*.jpg assets/layers/*/*.png assets/tagRenderings/*.json assets/themes/*/*.svg assets/themes/*/*.jpg assets/themes/*/*.png vendor/* vendor/*/*",
"start:parallel:tailwindcli": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch",
"generate:css": "tailwindcss -i index.css -o css/index-tailwind-output.css",
"test": "ts-node test/TestAll.ts",

35
test.ts
View file

@ -1,3 +1,34 @@
import WikipediaBox from "./UI/Wikipedia/WikipediaBox";
import MoveWizard from "./UI/Popup/MoveWizard";
import State from "./State";
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import MinimapImplementation from "./UI/Base/MinimapImplementation";
import MoveConfig from "./Models/ThemeConfig/MoveConfig";
import {FixedUiElement} from "./UI/Base/FixedUiElement";
import Combine from "./UI/Base/Combine";
new WikipediaBox(["L614072"]).AttachTo("maindiv")
State.state = new State(AllKnownLayouts.allKnownLayouts.get("bookcases"))
const feature = {
"type": "Feature",
"properties": {
id: "node/14925464"
},
"geometry": {
"type": "Point",
"coordinates": [
4.21875,
50.958426723359935
]
}
}
/*
MinimapImplementation.initialize()
new MoveWizard(
feature,
State.state,
new MoveConfig({
enableRelocation: false,
enableImproveAccuracy: true
}, "test")).AttachTo("maindiv")
*/