Add move option, enable move and delete option on most layers
This commit is contained in:
parent
0a3eb966c1
commit
7e2b73ac5d
33 changed files with 454 additions and 104 deletions
|
@ -18,7 +18,6 @@ export default class ChangeLocationAction extends OsmChangeAction {
|
||||||
this._id = Number(id.substring("node/".length))
|
this._id = Number(id.substring("node/".length))
|
||||||
this._newLonLat = newLonLat;
|
this._newLonLat = newLonLat;
|
||||||
this._meta = meta;
|
this._meta = meta;
|
||||||
throw "TODO"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
protected async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
|
||||||
|
|
|
@ -59,14 +59,13 @@ export abstract class OsmObject {
|
||||||
|
|
||||||
static async DownloadPropertiesOf(id: string): Promise<any> {
|
static async DownloadPropertiesOf(id: string): Promise<any> {
|
||||||
const splitted = id.split("/");
|
const splitted = id.split("/");
|
||||||
const type = splitted[0];
|
|
||||||
const idN = Number(splitted[1]);
|
const idN = Number(splitted[1]);
|
||||||
if (idN < 0) {
|
if (idN < 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${OsmObject.backendURL}api/0.6/${id}`;
|
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
|
return rawData.elements[0].tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +79,7 @@ export abstract class OsmObject {
|
||||||
|
|
||||||
const full = (id.startsWith("way")) ? "/full" : "";
|
const full = (id.startsWith("way")) ? "/full" : "";
|
||||||
const url = `${OsmObject.backendURL}api/0.6/${id}${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)
|
// 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);
|
const parsed = OsmObject.ParseObjects(rawData.elements);
|
||||||
// Lets fetch the object we need
|
// Lets fetch the object we need
|
||||||
|
@ -105,7 +104,7 @@ export abstract class OsmObject {
|
||||||
* Beware: their geometry will be incomplete!
|
* Beware: their geometry will be incomplete!
|
||||||
*/
|
*/
|
||||||
public static DownloadReferencingWays(id: string): Promise<OsmWay[]> {
|
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 => {
|
data => {
|
||||||
return data.elements.map(wayInfo => {
|
return data.elements.map(wayInfo => {
|
||||||
const way = new OsmWay(wayInfo.id)
|
const way = new OsmWay(wayInfo.id)
|
||||||
|
@ -121,7 +120,7 @@ export abstract class OsmObject {
|
||||||
* Beware: their geometry will be incomplete!
|
* Beware: their geometry will be incomplete!
|
||||||
*/
|
*/
|
||||||
public static async DownloadReferencingRelations(id: string): Promise<OsmRelation[]> {
|
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 => {
|
return data.elements.map(wayInfo => {
|
||||||
const rel = new OsmRelation(wayInfo.id)
|
const rel = new OsmRelation(wayInfo.id)
|
||||||
rel.LoadData(wayInfo)
|
rel.LoadData(wayInfo)
|
||||||
|
@ -139,7 +138,7 @@ export abstract class OsmObject {
|
||||||
const idN = Number(splitted[1]);
|
const idN = Number(splitted[1]);
|
||||||
const src = new UIEventSource<OsmObject[]>([]);
|
const src = new UIEventSource<OsmObject[]>([]);
|
||||||
OsmObject.historyCache.set(id, src);
|
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 elements: any[] = data.elements;
|
||||||
const osmObjects: OsmObject[] = []
|
const osmObjects: OsmObject[] = []
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||||
import FilterConfigJson from "./FilterConfigJson";
|
import FilterConfigJson from "./FilterConfigJson";
|
||||||
import {DeleteConfigJson} from "./DeleteConfigJson";
|
import {DeleteConfigJson} from "./DeleteConfigJson";
|
||||||
import UnitConfigJson from "./UnitConfigJson";
|
import UnitConfigJson from "./UnitConfigJson";
|
||||||
|
import MoveConfigJson from "./MoveConfigJson";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for a single layer
|
* Configuration for a single layer
|
||||||
|
@ -314,6 +315,18 @@ export interface LayerConfigJson {
|
||||||
*/
|
*/
|
||||||
deletion?: boolean | DeleteConfigJson
|
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
|
* IF set, a 'split this road' button is shown
|
||||||
*/
|
*/
|
||||||
|
|
12
Models/ThemeConfig/Json/MoveConfigJson.ts
Normal file
12
Models/ThemeConfig/Json/MoveConfigJson.ts
Normal 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
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import {Unit} from "../Unit";
|
||||||
import DeleteConfig from "./DeleteConfig";
|
import DeleteConfig from "./DeleteConfig";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import Img from "../../UI/Base/Img";
|
import Img from "../../UI/Base/Img";
|
||||||
|
import MoveConfig from "./MoveConfig";
|
||||||
|
|
||||||
export default class LayerConfig {
|
export default class LayerConfig {
|
||||||
static WAYHANDLING_DEFAULT = 0;
|
static WAYHANDLING_DEFAULT = 0;
|
||||||
|
@ -49,6 +50,7 @@ export default class LayerConfig {
|
||||||
wayHandling: number;
|
wayHandling: number;
|
||||||
public readonly units: Unit[];
|
public readonly units: Unit[];
|
||||||
public readonly deletion: DeleteConfig | null;
|
public readonly deletion: DeleteConfig | null;
|
||||||
|
public readonly allowMove: MoveConfig | null
|
||||||
public readonly allowSplit: boolean
|
public readonly allowSplit: boolean
|
||||||
|
|
||||||
presets: PresetConfig[];
|
presets: PresetConfig[];
|
||||||
|
@ -138,10 +140,10 @@ export default class LayerConfig {
|
||||||
const key = kv.substring(0, index);
|
const key = kv.substring(0, index);
|
||||||
const code = kv.substring(index + 1);
|
const code = kv.substring(index + 1);
|
||||||
|
|
||||||
try{
|
try {
|
||||||
|
|
||||||
new Function("feat", "return " + code + ";");
|
new Function("feat", "return " + code + ";");
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})`
|
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.minzoom = json.minzoom ?? 0;
|
||||||
this.minzoomVisible = json.minzoomVisible ?? this.minzoom;
|
this.minzoomVisible = json.minzoomVisible ?? this.minzoom;
|
||||||
this.wayHandling = json.wayHandling ?? 0;
|
this.wayHandling = json.wayHandling ?? 0;
|
||||||
if(json.presets !== undefined && json.presets?.map === undefined){
|
if (json.presets !== undefined && json.presets?.map === undefined) {
|
||||||
throw "Presets should be a list of items (at "+context+")"
|
throw "Presets should be a list of items (at " + context + ")"
|
||||||
}
|
}
|
||||||
this.presets = (json.presets ?? []).map((pr, i) => {
|
this.presets = (json.presets ?? []).map((pr, i) => {
|
||||||
|
|
||||||
|
@ -294,7 +296,7 @@ export default class LayerConfig {
|
||||||
|
|
||||||
const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
|
const missingIds = json.tagRenderings?.filter(tr => typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined) ?? [];
|
||||||
|
|
||||||
if(missingIds.length > 0 && official){
|
if (missingIds.length > 0 && official) {
|
||||||
console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds)
|
console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds)
|
||||||
throw "Missing ids in tagrenderings"
|
throw "Missing ids in tagrenderings"
|
||||||
}
|
}
|
||||||
|
@ -303,8 +305,8 @@ export default class LayerConfig {
|
||||||
return new FilterConfig(option, `${context}.filter-[${i}]`)
|
return new FilterConfig(option, `${context}.filter-[${i}]`)
|
||||||
});
|
});
|
||||||
|
|
||||||
if(json["filters"] !== undefined){
|
if (json["filters"] !== undefined) {
|
||||||
throw "Error in "+context+": use 'filter' instead of 'filters'"
|
throw "Error in " + context + ": use 'filter' instead of 'filters'"
|
||||||
}
|
}
|
||||||
|
|
||||||
const titleIcons = [];
|
const titleIcons = [];
|
||||||
|
@ -369,6 +371,16 @@ export default class LayerConfig {
|
||||||
this.deletion = new DeleteConfig(json.deletion, `${context}.deletion`);
|
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) {
|
if (json["showIf"] !== undefined) {
|
||||||
throw (
|
throw (
|
||||||
"Invalid key on layerconfig " +
|
"Invalid key on layerconfig " +
|
||||||
|
|
17
Models/ThemeConfig/MoveConfig.ts
Normal file
17
Models/ThemeConfig/MoveConfig.ts
Normal 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 + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import {BBox} from "../../Logic/BBox";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import Toggle from "./Toggle";
|
||||||
|
|
||||||
export default class LocationInput extends InputElement<Loc> implements MinimapObj {
|
export default class LocationInput extends InputElement<Loc> implements MinimapObj {
|
||||||
|
|
||||||
|
@ -161,6 +162,11 @@ export default class LocationInput extends InputElement<Loc> implements MinimapO
|
||||||
protected InnerConstructElement(): HTMLElement {
|
protected InnerConstructElement(): HTMLElement {
|
||||||
try {
|
try {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
const hasMoved = new UIEventSource(false)
|
||||||
|
this.GetValue().addCallbackAndRunD(_ => {
|
||||||
|
hasMoved.setData(true)
|
||||||
|
return true;
|
||||||
|
})
|
||||||
this.clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location))
|
this.clickLocation.addCallbackAndRunD(location => this._centerLocation.setData(location))
|
||||||
if (this._snapTo !== undefined) {
|
if (this._snapTo !== undefined) {
|
||||||
|
|
||||||
|
@ -213,8 +219,8 @@ export default class LocationInput extends InputElement<Loc> implements MinimapO
|
||||||
]).SetClass("block w-0 h-0 z-10 relative")
|
]).SetClass("block w-0 h-0 z-10 relative")
|
||||||
.SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"),
|
.SetStyle("background: rgba(255, 128, 128, 0.21); left: 50%; top: 50%; opacity: 0.5"),
|
||||||
|
|
||||||
new Combine([
|
new Toggle(undefined,
|
||||||
animatedHand])
|
animatedHand, hasMoved)
|
||||||
.SetClass("block w-0 h-0 z-10 relative")
|
.SetClass("block w-0 h-0 z-10 relative")
|
||||||
.SetStyle("left: calc(50% + 3rem); top: calc(50% + 2rem); opacity: 0.7"),
|
.SetStyle("left: calc(50% + 3rem); top: calc(50% + 2rem); opacity: 0.7"),
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||||
import {Translation} from "../i18n/Translation";
|
import {Translation} from "../i18n/Translation";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
||||||
|
import MoveWizard from "./MoveWizard";
|
||||||
|
|
||||||
export default class FeatureInfoBox extends ScrollableFullScreen {
|
export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
|
|
||||||
|
@ -72,6 +73,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
editElements.push(questionBox);
|
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) {
|
if (layerConfig.deletion) {
|
||||||
editElements.push(
|
editElements.push(
|
||||||
|
|
|
@ -15,13 +15,17 @@ import {OsmObject} from "../../Logic/Osm/OsmObject";
|
||||||
import {Changes} from "../../Logic/Osm/Changes";
|
import {Changes} from "../../Logic/Osm/Changes";
|
||||||
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction";
|
import ChangeLocationAction from "../../Logic/Osm/Actions/ChangeLocationAction";
|
||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
|
||||||
|
import MoveConfig from "../../Models/ThemeConfig/MoveConfig";
|
||||||
|
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers";
|
||||||
|
import {ElementStorage} from "../../Logic/ElementStorage";
|
||||||
|
|
||||||
interface MoveReason {
|
interface MoveReason {
|
||||||
text: Translation | string,
|
text: Translation | string,
|
||||||
|
invitingText: Translation | string,
|
||||||
icon: string | BaseUIElement,
|
icon: string | BaseUIElement,
|
||||||
changesetCommentValue: string,
|
changesetCommentValue: string,
|
||||||
lockBounds: true | boolean,
|
lockBounds: true | boolean,
|
||||||
background: undefined | "map" | "photo",
|
background: undefined | "map" | "photo" | string | string[],
|
||||||
startZoom: number,
|
startZoom: number,
|
||||||
minZoom: number
|
minZoom: number
|
||||||
}
|
}
|
||||||
|
@ -36,13 +40,9 @@ export default class MoveWizard extends Toggle {
|
||||||
osmConnection: OsmConnection,
|
osmConnection: OsmConnection,
|
||||||
featureSwitchUserbadge: UIEventSource<boolean>,
|
featureSwitchUserbadge: UIEventSource<boolean>,
|
||||||
changes: Changes,
|
changes: Changes,
|
||||||
layoutToUse: LayoutConfig
|
layoutToUse: LayoutConfig,
|
||||||
}, options?: {
|
allElements: ElementStorage
|
||||||
reasons?: MoveReason[]
|
}, options : MoveConfig) {
|
||||||
disableDefaultReasons?: false | boolean
|
|
||||||
|
|
||||||
}) {
|
|
||||||
options = options ?? {}
|
|
||||||
|
|
||||||
const t = Translations.t.move
|
const t = Translations.t.move
|
||||||
const loginButton = new Toggle(
|
const loginButton = new Toggle(
|
||||||
|
@ -51,14 +51,53 @@ export default class MoveWizard extends Toggle {
|
||||||
state.featureSwitchUserbadge
|
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 currentStep = new UIEventSource<"start" | "reason" | "pick_location" | "moved">("start")
|
||||||
const moveReason = new UIEventSource<MoveReason>(undefined)
|
const moveReason = new UIEventSource<MoveReason>(undefined)
|
||||||
const moveButton = new SubtleButton(
|
let moveButton : BaseUIElement;
|
||||||
|
if(reasons.length === 1){
|
||||||
|
const reason = reasons[0]
|
||||||
|
moveReason.setData(reason)
|
||||||
|
moveButton = new SubtleButton(
|
||||||
|
reason.icon,
|
||||||
|
Translations.WT(reason.invitingText).Clone()
|
||||||
|
).onClick(() => {
|
||||||
|
currentStep.setData("pick_location")
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
moveButton = new SubtleButton(
|
||||||
Svg.move_ui(),
|
Svg.move_ui(),
|
||||||
t.inviteToMove.Clone()
|
t.inviteToMove.generic.Clone()
|
||||||
).onClick(() => {
|
).onClick(() => {
|
||||||
currentStep.setData("reason")
|
currentStep.setData("reason")
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const moveAgainButton = new SubtleButton(
|
const moveAgainButton = new SubtleButton(
|
||||||
Svg.move_ui(),
|
Svg.move_ui(),
|
||||||
|
@ -68,39 +107,7 @@ export default class MoveWizard extends Toggle {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const reasons: MoveReason[] = []
|
|
||||||
if (!options.disableDefaultReasons) {
|
|
||||||
reasons.push({
|
|
||||||
text: t.reasonRelocation.Clone(),
|
|
||||||
icon: Svg.relocation_svg(),
|
|
||||||
changesetCommentValue: "relocated",
|
|
||||||
lockBounds: false,
|
|
||||||
background: undefined,
|
|
||||||
startZoom: 12,
|
|
||||||
minZoom: 6
|
|
||||||
})
|
|
||||||
|
|
||||||
reasons.push({
|
|
||||||
text: t.reasonInaccurate.Clone(),
|
|
||||||
icon: Svg.crosshair_svg(),
|
|
||||||
changesetCommentValue: "improve_accuracy",
|
|
||||||
lockBounds: true,
|
|
||||||
background: "photo",
|
|
||||||
startZoom: 17,
|
|
||||||
minZoom: 16
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for (const reason of options.reasons ?? []) {
|
|
||||||
reasons.push({
|
|
||||||
text: reason.text,
|
|
||||||
icon: reason.icon,
|
|
||||||
changesetCommentValue: reason.changesetCommentValue,
|
|
||||||
lockBounds: reason.lockBounds ?? true,
|
|
||||||
background: reason.background,
|
|
||||||
startZoom: reason.startZoom ?? 15,
|
|
||||||
minZoom: reason.minZoom
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectReason = new Combine(reasons.map(r => new SubtleButton(r.icon, r.text).onClick(() => {
|
const selectReason = new Combine(reasons.map(r => new SubtleButton(r.icon, r.text).onClick(() => {
|
||||||
moveReason.setData(r)
|
moveReason.setData(r)
|
||||||
|
@ -124,7 +131,8 @@ export default class MoveWizard extends Toggle {
|
||||||
|
|
||||||
const locationInput = new LocationInput({
|
const locationInput = new LocationInput({
|
||||||
minZoom: reason.minZoom,
|
minZoom: reason.minZoom,
|
||||||
centerLocation: loc
|
centerLocation: loc,
|
||||||
|
mapBackground: AvailableBaseLayers.SelectBestLayerAccordingTo(loc, new UIEventSource(reason.background))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (reason.lockBounds) {
|
if (reason.lockBounds) {
|
||||||
|
@ -135,10 +143,14 @@ export default class MoveWizard extends Toggle {
|
||||||
|
|
||||||
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
const confirmMove = new SubtleButton(Svg.move_confirm_svg(), t.confirmMove)
|
||||||
confirmMove.onClick(() => {
|
confirmMove.onClick(() => {
|
||||||
state.changes.applyAction(new ChangeLocationAction(featureToMove.properties.id, [locationInput.GetValue().data.lon, locationInput.GetValue().data.lat], {
|
const loc = locationInput.GetValue().data
|
||||||
|
state.changes.applyAction(new ChangeLocationAction(featureToMove.properties.id, [loc.lon, loc.lat], {
|
||||||
reason: Translations.WT(reason.text).textFor("en"),
|
reason: Translations.WT(reason.text).textFor("en"),
|
||||||
theme: state.layoutToUse.icon
|
theme: state.layoutToUse.id
|
||||||
}))
|
}))
|
||||||
|
featureToMove.properties._lat = loc.lat
|
||||||
|
featureToMove.properties._lon = loc.lon
|
||||||
|
state.allElements.getEventSourceById(id).ping()
|
||||||
currentStep.setData("moved")
|
currentStep.setData("moved")
|
||||||
})
|
})
|
||||||
const zoomInFurhter = t.zoomInFurther.Clone().SetClass("alert block m-6")
|
const zoomInFurhter = t.zoomInFurther.Clone().SetClass("alert block m-6")
|
||||||
|
|
13
Utils.ts
13
Utils.ts
|
@ -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> {
|
public static async downloadJson(url: string, headers?: any): Promise<any> {
|
||||||
const injected = Utils.injectedDownloads[url]
|
const injected = Utils.injectedDownloads[url]
|
||||||
if (injected !== undefined) {
|
if (injected !== undefined) {
|
||||||
|
|
|
@ -600,5 +600,18 @@
|
||||||
"preferredBackground": "photo"
|
"preferredBackground": "photo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity=bench",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -255,5 +255,18 @@
|
||||||
],
|
],
|
||||||
"multiAnswer": true
|
"multiAnswer": true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -145,5 +145,18 @@
|
||||||
"roaming": false,
|
"roaming": false,
|
||||||
"id": "bike_cleaning-charge"
|
"id": "bike_cleaning-charge"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -508,5 +508,18 @@
|
||||||
},
|
},
|
||||||
"id": "Cargo bike capacity?"
|
"id": "Cargo bike capacity?"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -731,5 +731,18 @@
|
||||||
"service:bicycle:pump=no"
|
"service:bicycle:pump=no"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -100,5 +100,18 @@
|
||||||
"amenity=binoculars"
|
"amenity=binoculars"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -296,5 +296,17 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -181,5 +181,11 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": ["amenity=","disused:amenity:={amenity}"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allowMove": true
|
||||||
}
|
}
|
|
@ -3655,5 +3655,18 @@
|
||||||
],
|
],
|
||||||
"eraseInvalidValues": true
|
"eraseInvalidValues": true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -539,5 +539,18 @@
|
||||||
},
|
},
|
||||||
"id": "defibrillator-fixme"
|
"id": "defibrillator-fixme"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:emergency:=defibrillator}",
|
||||||
|
"emergency="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 5
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -166,5 +166,18 @@
|
||||||
},
|
},
|
||||||
"condition": "_closest_other_drinking_water_id~*"
|
"condition": "_closest_other_drinking_water_id~*"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -624,5 +624,11 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": ["amenity=","disused:amenity:={amenity}"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allowMove": true
|
||||||
}
|
}
|
|
@ -187,5 +187,18 @@
|
||||||
},
|
},
|
||||||
"id": "ghost_bike-start_date"
|
"id": "ghost_bike-start_date"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"razed:memorial:=ghost_bike",
|
||||||
|
"memorial="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 50
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -53,5 +53,20 @@
|
||||||
"ru": "Информационный щит"
|
"ru": "Информационный щит"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:tourism:=information",
|
||||||
|
"tourism=" ,
|
||||||
|
"razed:information=board",
|
||||||
|
"information="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -227,5 +227,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"wayHandling": 2
|
"wayHandling": 2,
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"razed:tourism:=information",
|
||||||
|
"tourism="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -180,5 +180,9 @@
|
||||||
],
|
],
|
||||||
"eraseInvalidValues": true
|
"eraseInvalidValues": true
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -81,5 +81,18 @@
|
||||||
"nl": "Voeg hier een parking voor auto's toe"
|
"nl": "Voeg hier een parking voor auto's toe"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -101,5 +101,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"wayHandling": 1
|
"wayHandling": 1,
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -498,5 +498,18 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -115,5 +115,18 @@
|
||||||
"preferredBackground": "photo"
|
"preferredBackground": "photo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"deletion": {
|
||||||
|
"softDeletionTags": {
|
||||||
|
"and": [
|
||||||
|
"disused:amenity:={amenity}",
|
||||||
|
"amenity="
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"neededChangesets": 1
|
||||||
|
},
|
||||||
|
"allowMove": {
|
||||||
|
"enableRelocation": false,
|
||||||
|
"enableImproveAccuraccy": true
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -256,7 +256,6 @@
|
||||||
},
|
},
|
||||||
"move": {
|
"move": {
|
||||||
"loginToMove": "You must be logged in to move a point",
|
"loginToMove": "You must be logged in to move a point",
|
||||||
"inviteToMove": "Move this point",
|
|
||||||
"inviteToMoveAgain": "Move this point again",
|
"inviteToMoveAgain": "Move this point again",
|
||||||
"moveTitle": "Move this point",
|
"moveTitle": "Move this point",
|
||||||
"whyMove": "Why do you want to move this point?",
|
"whyMove": "Why do you want to move this point?",
|
||||||
|
@ -264,8 +263,15 @@
|
||||||
"pointIsMoved": "The point has been moved",
|
"pointIsMoved": "The point has been moved",
|
||||||
"zoomInFurther": "Zoom in further to confirm this move",
|
"zoomInFurther": "Zoom in further to confirm this move",
|
||||||
"selectReason": "Why do you move this object?",
|
"selectReason": "Why do you move this object?",
|
||||||
|
"reasons": {
|
||||||
"reasonRelocation": "The object has been relocated to a totally different location",
|
"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",
|
"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.",
|
"cannotBeMoved": "This feature cannot be moved.",
|
||||||
"isWay": "This feature is a way. Use another OpenStreetMap editor to move it.",
|
"isWay": "This feature is a way. Use another OpenStreetMap editor to move it.",
|
||||||
"isRelation": "This feature is a relation and can not be moved",
|
"isRelation": "This feature is a relation and can not be moved",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"homepage": "https://mapcomplete.osm.be",
|
"homepage": "https://mapcomplete.osm.be",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"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:*",
|
"start": "npm run start:prepare && npm-run-all --parallel start:parallel:*",
|
||||||
"strt": "npm run start:prepare && npm run start:parallel:parcel",
|
"strt": "npm run start:prepare && npm run start:parallel:parcel",
|
||||||
"start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory",
|
"start:prepare": "ts-node scripts/generateLayerOverview.ts --no-fail && npm run increase-memory",
|
||||||
|
|
11
test.ts
11
test.ts
|
@ -2,6 +2,9 @@ import MoveWizard from "./UI/Popup/MoveWizard";
|
||||||
import State from "./State";
|
import State from "./State";
|
||||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
|
||||||
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
import MinimapImplementation from "./UI/Base/MinimapImplementation";
|
||||||
|
import MoveConfig from "./Models/ThemeConfig/MoveConfig";
|
||||||
|
import {FixedUiElement} from "./UI/Base/FixedUiElement";
|
||||||
|
import Combine from "./UI/Base/Combine";
|
||||||
|
|
||||||
|
|
||||||
State.state = new State(AllKnownLayouts.allKnownLayouts.get("bookcases"))
|
State.state = new State(AllKnownLayouts.allKnownLayouts.get("bookcases"))
|
||||||
|
@ -18,8 +21,14 @@ const feature = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
MinimapImplementation.initialize()
|
MinimapImplementation.initialize()
|
||||||
new MoveWizard(
|
new MoveWizard(
|
||||||
feature,
|
feature,
|
||||||
State.state).AttachTo("maindiv")
|
State.state,
|
||||||
|
new MoveConfig({
|
||||||
|
enableRelocation: false,
|
||||||
|
enableImproveAccuracy: true
|
||||||
|
}, "test")).AttachTo("maindiv")
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
Loading…
Reference in a new issue