forked from MapComplete/MapComplete
Update to the import viewers
This commit is contained in:
parent
fa179af601
commit
f09134c3be
26 changed files with 303 additions and 413 deletions
|
@ -10,6 +10,9 @@ import IndexText from "./BigComponents/IndexText";
|
|||
import FeaturedMessage from "./BigComponents/FeaturedMessage";
|
||||
import Toggle from "./Input/Toggle";
|
||||
import {SubtleButton} from "./Base/SubtleButton";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Title from "./Base/Title";
|
||||
import Svg from "../Svg";
|
||||
|
||||
export default class AllThemesGui {
|
||||
constructor() {
|
||||
|
@ -26,12 +29,21 @@ export default class AllThemesGui {
|
|||
]);
|
||||
new Combine([
|
||||
intro,
|
||||
new FeaturedMessage(),
|
||||
new FeaturedMessage().SetClass("mb-4 block"),
|
||||
new MoreScreen(state, true),
|
||||
new Toggle(
|
||||
undefined,
|
||||
new SubtleButton(undefined, Translations.t.index.logIn).SetStyle("height:min-content").onClick(() => state.osmConnection.AttemptLogin()),
|
||||
state.osmConnection.isLoggedIn),
|
||||
new VariableUiElement(state.osmConnection.userDetails.map(ud => {
|
||||
if(ud.csCount < Constants.userJourney.importHelperUnlock){
|
||||
return undefined;
|
||||
}
|
||||
return new Combine([
|
||||
new SubtleButton( undefined, Translations.t.importHelper.title, {url: "import_helper.html"}),
|
||||
new SubtleButton( Svg.note_svg(), Translations.t.importInspector.title, {url: "import_viewer.html"})
|
||||
]).SetClass("p-4 border-2 border-gray-500 m-4 block")
|
||||
})),
|
||||
Translations.t.general.aboutMapcomplete
|
||||
.Subs({"osmcha_link": Utils.OsmChaLinkFor(7)})
|
||||
.SetClass("link-underline"),
|
||||
|
|
|
@ -8,7 +8,9 @@ export default class Loading extends Combine {
|
|||
const t = Translations.W(msg) ?? Translations.t.general.loading;
|
||||
t.SetClass("pl-2")
|
||||
super([
|
||||
Svg.loading_svg().SetClass("animate-spin").SetStyle("width: 1.5rem; height: 1.5rem;"),
|
||||
Svg.loading_svg()
|
||||
.SetClass("animate-spin self-center")
|
||||
.SetStyle("width: 1.5rem; height: 1.5rem; min-width: 1.5rem;"),
|
||||
t
|
||||
])
|
||||
this.SetClass("flex p-1")
|
||||
|
|
|
@ -55,8 +55,8 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
|
|||
this.leafletMap.addCallbackD(leaflet => {
|
||||
let bounds;
|
||||
if (typeof factor === "number") {
|
||||
bounds = leaflet.getBounds()
|
||||
leaflet.setMaxBounds(bounds.pad(factor))
|
||||
bounds = leaflet.getBounds().pad(factor)
|
||||
leaflet.setMaxBounds(bounds)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
leaflet.setMaxBounds(factor.toLeaflet())
|
||||
|
@ -99,9 +99,9 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
|
|||
// @ts-ignore
|
||||
L.geoJSON(data, {
|
||||
style: {
|
||||
color: "#f00",
|
||||
weight: 2,
|
||||
opacity: 0.4
|
||||
color: "#f44",
|
||||
weight: 4,
|
||||
opacity: 0.7
|
||||
}
|
||||
}).addTo(leaflet)
|
||||
}
|
||||
|
|
|
@ -18,22 +18,26 @@ export default class TableOfContents extends Combine {
|
|||
}) {
|
||||
let titles: Title[]
|
||||
if (elements instanceof Combine) {
|
||||
titles = TableOfContents.getTitles(elements.getElements())
|
||||
titles = TableOfContents.getTitles(elements.getElements()) ?? []
|
||||
} else {
|
||||
titles = elements
|
||||
titles = elements ?? []
|
||||
}
|
||||
|
||||
|
||||
let els: { level: number, content: BaseUIElement }[] = []
|
||||
for (const title of titles) {
|
||||
let content: BaseUIElement
|
||||
console.log("Constructing content for ", title)
|
||||
if (title.title instanceof Translation) {
|
||||
content = title.title.Clone()
|
||||
} else if (title.title instanceof FixedUiElement) {
|
||||
content = title.title
|
||||
content = new FixedUiElement(title.title.content)
|
||||
} else if (Utils.runningFromConsole) {
|
||||
content = new FixedUiElement(title.AsMarkdown())
|
||||
} else {
|
||||
} else if(title["title"] !== undefined) {
|
||||
content = new FixedUiElement(title.title.ConstructElement().innerText)
|
||||
}else{
|
||||
console.log("Not generating a title for ", title)
|
||||
continue
|
||||
}
|
||||
|
||||
const vis = new Link(content, "#" + title.id)
|
||||
|
|
|
@ -25,17 +25,36 @@ export class Accordeon extends Combine {
|
|||
export default class Toggleable extends Combine {
|
||||
public readonly isVisible = new UIEventSource(false)
|
||||
|
||||
constructor(title: Title | BaseUIElement, content: BaseUIElement) {
|
||||
constructor(title: Title | Combine | BaseUIElement, content: BaseUIElement, options?: {
|
||||
closeOnClick: true | boolean
|
||||
}) {
|
||||
super([title, content])
|
||||
content.SetClass("animate-height border-l-4 pl-2 block")
|
||||
title.SetClass("background-subtle rounded-lg")
|
||||
const self = this
|
||||
this.onClick(() => self.isVisible.setData(!self.isVisible.data))
|
||||
this.onClick(() => {
|
||||
if(self.isVisible.data){
|
||||
if(options?.closeOnClick ?? true){
|
||||
self.isVisible.setData(false)
|
||||
}
|
||||
}else{
|
||||
self.isVisible.setData(true)
|
||||
}
|
||||
})
|
||||
const contentElement = content.ConstructElement()
|
||||
|
||||
if(title instanceof Combine){
|
||||
for(const el of title.getElements()){
|
||||
if(el instanceof Title){
|
||||
title = el;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (title instanceof Title) {
|
||||
Hash.hash.addCallbackAndRun(h => {
|
||||
if (h === title.id) {
|
||||
if (h === (<Title> title).id) {
|
||||
self.isVisible.setData(true)
|
||||
content.RemoveClass("border-gray-300")
|
||||
content.SetClass("border-red-300")
|
||||
|
@ -46,14 +65,14 @@ export default class Toggleable extends Combine {
|
|||
})
|
||||
this.isVisible.addCallbackAndRun(isVis => {
|
||||
if (isVis) {
|
||||
Hash.hash.setData(title.id)
|
||||
Hash.hash.setData((<Title>title).id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.isVisible.addCallbackAndRun(isVisible => {
|
||||
if (isVisible) {
|
||||
contentElement.style.maxHeight = "50vh"
|
||||
contentElement.style.maxHeight = "100vh"
|
||||
contentElement.style.overflowY = "auto"
|
||||
contentElement.style["-webkit-mask-image"] = "unset"
|
||||
} else {
|
||||
|
|
|
@ -23,6 +23,7 @@ import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline";
|
|||
import {ElementStorage} from "../../Logic/ElementStorage";
|
||||
import ConfirmLocationOfPoint from "../NewPoint/ConfirmLocationOfPoint";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
import Loading from "../Base/Loading";
|
||||
|
||||
/*
|
||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||
|
@ -35,7 +36,8 @@ import BaseLayer from "../../Models/BaseLayer";
|
|||
export interface PresetInfo extends PresetConfig {
|
||||
name: string | BaseUIElement,
|
||||
icon: () => BaseUIElement,
|
||||
layerToAddTo: FilteredLayer
|
||||
layerToAddTo: FilteredLayer,
|
||||
boundsFactor?: 0.25 | number
|
||||
}
|
||||
|
||||
export default class SimpleAddUI extends Toggle {
|
||||
|
@ -124,7 +126,7 @@ export default class SimpleAddUI extends Toggle {
|
|||
new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
Translations.t.general.add.stillLoading.Clone().SetClass("alert"),
|
||||
new Loading(Translations.t.general.add.stillLoading).SetClass("alert"),
|
||||
addUi,
|
||||
state.featurePipeline.runningQuery
|
||||
),
|
||||
|
|
|
@ -2,7 +2,6 @@ import Combine from "../Base/Combine";
|
|||
import UserRelatedState from "../../Logic/State/UserRelatedState";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {Utils} from "../../Utils";
|
||||
import UserDetails from "../../Logic/Osm/OsmConnection";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Title from "../Base/Title";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
@ -21,6 +20,7 @@ import Toggleable, {Accordeon} from "../Base/Toggleable";
|
|||
import TableOfContents from "../Base/TableOfContents";
|
||||
import LoginButton from "../Popup/LoginButton";
|
||||
import BackToIndex from "../BigComponents/BackToIndex";
|
||||
import {QueryParameters} from "../../Logic/Web/QueryParameters";
|
||||
|
||||
interface NoteProperties {
|
||||
"id": number,
|
||||
|
@ -32,7 +32,8 @@ interface NoteProperties {
|
|||
date: string,
|
||||
uid: number,
|
||||
user: string,
|
||||
text: string
|
||||
text: string,
|
||||
html: string
|
||||
}[]
|
||||
}
|
||||
|
||||
|
@ -41,7 +42,7 @@ interface NoteState {
|
|||
theme: string,
|
||||
intro: string,
|
||||
dateStr: string,
|
||||
status: "imported" | "already_mapped" | "invalid" | "closed" | "not_found" | "open"
|
||||
status: "imported" | "already_mapped" | "invalid" | "closed" | "not_found" | "open" | "has_comments"
|
||||
}
|
||||
|
||||
class MassAction extends Combine {
|
||||
|
@ -127,16 +128,50 @@ class MassAction extends Combine {
|
|||
|
||||
|
||||
class BatchView extends Toggleable {
|
||||
constructor(state: UserRelatedState, noteStates: NoteState[]) {
|
||||
|
||||
private static icons = {
|
||||
open: Svg.compass_svg,
|
||||
has_comments: Svg.speech_bubble_svg,
|
||||
imported: Svg.addSmall_svg,
|
||||
already_mapped: Svg.checkmark_svg,
|
||||
invalid: Svg.invalid_svg,
|
||||
closed: Svg.close_svg,
|
||||
not_found: Svg.not_found_svg,
|
||||
}
|
||||
|
||||
constructor(noteStates: NoteState[], state?: UserRelatedState) {
|
||||
|
||||
noteStates.sort((a, b) => a.props.id - b.props.id)
|
||||
|
||||
const {theme, intro, dateStr} = noteStates[0]
|
||||
console.log("Creating a batchview for ", noteStates)
|
||||
|
||||
const statusHist = new Map<string, number>()
|
||||
for (const noteState of noteStates) {
|
||||
const st = noteState.status
|
||||
const c = statusHist.get(st) ?? 0
|
||||
statusHist.set(st, c + 1)
|
||||
}
|
||||
|
||||
const badges: (BaseUIElement)[] = [new FixedUiElement(dateStr).SetClass("literal-code rounded-full")]
|
||||
statusHist.forEach((count, status) => {
|
||||
const icon = BatchView.icons[status]().SetClass("h-6 m-1")
|
||||
badges.push(new Combine([icon, count + " " + status])
|
||||
.SetClass("flex ml-1 mb-1 pl-1 pr-3 items-center rounded-full border border-black"))
|
||||
})
|
||||
|
||||
const typicalComment = noteStates[0].props.comments[0].html
|
||||
|
||||
|
||||
super(
|
||||
new Title(theme + ": " + intro, 2),
|
||||
new Combine([
|
||||
new FixedUiElement(dateStr),
|
||||
new FixedUiElement("Click to expand/collapse table"),
|
||||
|
||||
|
||||
new Title(theme + ": " + intro, 2),
|
||||
new Combine(badges).SetClass("flex flex-wrap"),
|
||||
]),
|
||||
new Combine([
|
||||
new Title("Example note", 4),
|
||||
new FixedUiElement(typicalComment).SetClass("literal-code link-underline"),
|
||||
new Title("Mass apply an action"),
|
||||
state !== undefined ? new MassAction(state, noteStates.map(ns => ns.props)).SetClass("block") : undefined,
|
||||
new Table(
|
||||
["id", "status", "last comment"],
|
||||
noteStates.map(ns => {
|
||||
|
@ -144,30 +179,39 @@ class BatchView extends Toggleable {
|
|||
"" + ns.props.id,
|
||||
"https://openstreetmap.org/note/" + ns.props.id, true
|
||||
)
|
||||
const last_comment = ns.props.comments[ns.props.comments.length - 1].text
|
||||
return [link, ns.status, last_comment]
|
||||
let last_comment = "";
|
||||
if (ns.props.comments.length > 1) {
|
||||
last_comment = ns.props.comments[ns.props.comments.length - 1].text
|
||||
}
|
||||
const statusIcon = BatchView.icons[ns.status]().SetClass("h-4 w-4 shrink-0")
|
||||
return [link, new Combine([statusIcon, ns.status]).SetClass("flex"), last_comment]
|
||||
})
|
||||
).SetClass("zebra-table link-underline"),
|
||||
).SetClass("zebra-table link-underline")
|
||||
]).SetClass("flex flex-col"),
|
||||
{
|
||||
closeOnClick: false
|
||||
})
|
||||
|
||||
|
||||
new Title("Mass apply an action"),
|
||||
new MassAction(state, noteStates.map(ns => ns.props)).SetClass("block")]).SetClass("flex flex-col"))
|
||||
}
|
||||
}
|
||||
|
||||
class ImportInspector extends VariableUiElement {
|
||||
|
||||
constructor(userDetails: UserDetails, state: UserRelatedState) {
|
||||
const t = Translations.t.importInspector;
|
||||
constructor(userDetails: { uid: number } | { display_name: string, search?: string }, state: UserRelatedState) {
|
||||
let url;
|
||||
if (userDetails["uid"] !== undefined) {
|
||||
url = "https://api.openstreetmap.org/api/0.6/notes/search.json?user=" + userDetails["uid"] + "&limit=10000&sort=created_at&q=%23import"
|
||||
} else {
|
||||
url = "https://api.openstreetmap.org/api/0.6/notes/search.json?display_name=" +
|
||||
encodeURIComponent(userDetails["display_name"]) + "&limit=10000&sort=created_at&q=" + encodeURIComponent(userDetails["search"] ?? "#import")
|
||||
}
|
||||
|
||||
|
||||
const url = "https://api.openstreetmap.org/api/0.6/notes/search.json?user=" + userDetails.uid + "&limit=10000&sort=created_at&q=%23import"
|
||||
const notes: UIEventSource<{ error: string } | { success: { features: { properties: NoteProperties }[] } }> = UIEventSource.FromPromiseWithErr(Utils.downloadJson(url))
|
||||
notes.addCallbackAndRun(n => console.log("Notes are:", n))
|
||||
super(notes.map(notes => {
|
||||
|
||||
if (notes === undefined) {
|
||||
return new Loading("Loading your notes which mention '#import'")
|
||||
return new Loading("Loading notes which mention '#import'")
|
||||
}
|
||||
if (notes["error"] !== undefined) {
|
||||
return new FixedUiElement("Something went wrong: " + notes["error"]).SetClass("alert")
|
||||
|
@ -175,13 +219,18 @@ class ImportInspector extends VariableUiElement {
|
|||
// We only care about the properties here
|
||||
const props: NoteProperties[] = notes["success"].features.map(f => f.properties)
|
||||
const perBatch: NoteState[][] = Array.from(ImportInspector.SplitNotesIntoBatches(props).values());
|
||||
const els: Toggleable[] = perBatch.map(noteStates => new BatchView(state, noteStates))
|
||||
const els: Toggleable[] = perBatch.map(noteStates => new BatchView(noteStates, state))
|
||||
|
||||
const accordeon = new Accordeon(els)
|
||||
const content = new Combine([
|
||||
new Title(Translations.t.importInspector.title, 1),
|
||||
new SubtleButton(undefined, "Create a new batch of imports",{url:'import_helper.html'}),
|
||||
accordeon])
|
||||
let contents = [];
|
||||
if (state?.osmConnection?.isLoggedIn?.data) {
|
||||
contents =
|
||||
[
|
||||
new Title(Translations.t.importInspector.title, 1),
|
||||
new SubtleButton(undefined, "Create a new batch of imports", {url: 'import_helper.html'})]
|
||||
}
|
||||
contents.push(accordeon)
|
||||
const content = new Combine(contents)
|
||||
return new LeftIndex(
|
||||
[new TableOfContents(content, {noTopLevel: true, maxDepth: 1}).SetClass("subtle")],
|
||||
content
|
||||
|
@ -205,12 +254,12 @@ class ImportInspector extends VariableUiElement {
|
|||
let theme = lines[trigger].substr(prefix.length)
|
||||
theme = theme.substr(0, theme.indexOf("."))
|
||||
const date = Utils.ParseDate(prop.date_created)
|
||||
const dateStr = date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate()
|
||||
const dateStr = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate()
|
||||
const key = theme + lines[0] + dateStr
|
||||
if (!perBatch.has(key)) {
|
||||
perBatch.set(key, [])
|
||||
}
|
||||
let status: "open" | "closed" | "imported" | "invalid" | "already_mapped" | "not_found" = "open"
|
||||
let status: "open" | "closed" | "imported" | "invalid" | "already_mapped" | "not_found" | "has_comments" = "open"
|
||||
if (prop.closed_at !== undefined) {
|
||||
const lastComment = prop.comments[prop.comments.length - 1].text.toLowerCase()
|
||||
if (lastComment.indexOf("does not exist") >= 0) {
|
||||
|
@ -224,6 +273,8 @@ class ImportInspector extends VariableUiElement {
|
|||
} else {
|
||||
status = "closed"
|
||||
}
|
||||
} else if (prop.comments.length > 1) {
|
||||
status = "has_comments"
|
||||
}
|
||||
|
||||
perBatch.get(key).push({
|
||||
|
@ -242,15 +293,23 @@ class ImportViewerGui extends Combine {
|
|||
|
||||
constructor() {
|
||||
const state = new UserRelatedState(undefined)
|
||||
const displayNameParam = QueryParameters.GetQueryParameter("user", "", "The username of the person whom you want to see the notes for");
|
||||
const searchParam = QueryParameters.GetQueryParameter("search", "", "A text that should be included in the first comment of the note to be shown")
|
||||
super([
|
||||
new VariableUiElement(state.osmConnection.userDetails.map(ud => {
|
||||
const display_name = displayNameParam.data;
|
||||
const search = searchParam.data;
|
||||
if (display_name !== "" && search !== "") {
|
||||
return new ImportInspector({display_name, search}, state);
|
||||
}
|
||||
|
||||
if (ud === undefined || ud.loggedIn === false) {
|
||||
return new Combine([new LoginButton("Login to inspect your import flows", state),
|
||||
new BackToIndex()
|
||||
new BackToIndex()
|
||||
])
|
||||
}
|
||||
return new ImportInspector(ud, state);
|
||||
}))
|
||||
}, [displayNameParam, searchParam]))
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ export default class ConfirmLocationOfPoint extends Combine {
|
|||
maxSnapDistance: preset.preciseInput.maxSnapDistance,
|
||||
bounds: mapBounds
|
||||
})
|
||||
preciseInput.installBounds(0.15, true)
|
||||
preciseInput.installBounds(preset.boundsFactor ?? 0.25, true)
|
||||
preciseInput.SetClass("h-32 rounded-xl overflow-hidden border border-gray").SetStyle("height: 12rem;")
|
||||
|
||||
|
||||
|
@ -78,7 +78,7 @@ export default class ConfirmLocationOfPoint extends Combine {
|
|||
// return;
|
||||
}
|
||||
|
||||
bbox = bbox.pad(2);
|
||||
bbox = bbox.pad(Math.max(preset.boundsFactor , 2), Math.max(preset.boundsFactor , 2));
|
||||
loadedBbox = bbox;
|
||||
const allFeatures: { feature: any }[] = []
|
||||
preset.preciseInput.snapToLayers.forEach(layerId => {
|
||||
|
|
|
@ -40,7 +40,7 @@ import {LoginToggle} from "./LoginButton";
|
|||
|
||||
/**
|
||||
* A helper class for the various import-flows.
|
||||
* An import-flow always starts with a 'Import this'-button. Upon click, a custom confirmation panel is provided
|
||||
* An import-flow always starts with a 'Import this'-button. Upon click, a custom confirmation panel is provided
|
||||
*/
|
||||
abstract class AbstractImportButton implements SpecialVisualizations {
|
||||
public readonly funcName: string
|
||||
|
@ -136,12 +136,12 @@ ${Utils.special_visualizations_importRequirementDocs}
|
|||
|
||||
|
||||
// Explanation of the tags that will be applied onto the imported/conflated object
|
||||
|
||||
|
||||
let tagSpec = args.tags;
|
||||
if(tagSpec.indexOf(" ")< 0 && tagSpec.indexOf(";") < 0 && tagSource.data[args.tags] !== undefined){
|
||||
if (tagSpec.indexOf(" ") < 0 && tagSpec.indexOf(";") < 0 && tagSource.data[args.tags] !== undefined) {
|
||||
// This is probably a key
|
||||
tagSpec = tagSource.data[args.tags]
|
||||
console.debug("The import button is using tags from properties["+args.tags+"] of this object, namely ",tagSpec)
|
||||
console.debug("The import button is using tags from properties[" + args.tags + "] of this object, namely ", tagSpec)
|
||||
}
|
||||
|
||||
const importClicked = new UIEventSource(false);
|
||||
|
@ -193,23 +193,6 @@ ${Utils.special_visualizations_importRequirementDocs}
|
|||
|
||||
}
|
||||
|
||||
private parseArgs(argsRaw: string[], originalFeatureTags: UIEventSource<any>): { minzoom: string, max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, tags: string, targetLayer: string, newTags: UIEventSource<Tag[]> } {
|
||||
const baseArgs = Utils.ParseVisArgs(this.args, argsRaw)
|
||||
if (originalFeatureTags !== undefined) {
|
||||
|
||||
const tags = baseArgs.tags
|
||||
if(tags.indexOf(" ") < 0 && tags.indexOf(";") < 0 && originalFeatureTags.data[tags] !== undefined){
|
||||
// This might be a property to expand...
|
||||
const items : string = originalFeatureTags.data[tags]
|
||||
console.debug("The import button is using tags from properties["+tags+"] of this object, namely ",items)
|
||||
baseArgs["newTags"] = TagApplyButton.generateTagsToApply(items, originalFeatureTags)
|
||||
}else{
|
||||
baseArgs["newTags"] = TagApplyButton.generateTagsToApply(tags, originalFeatureTags)
|
||||
}
|
||||
}
|
||||
return baseArgs
|
||||
}
|
||||
|
||||
getLayerDependencies(argsRaw: string[]) {
|
||||
const args = this.parseArgs(argsRaw, undefined)
|
||||
|
||||
|
@ -226,7 +209,6 @@ ${Utils.special_visualizations_importRequirementDocs}
|
|||
return dependsOnLayers
|
||||
}
|
||||
|
||||
|
||||
protected abstract canBeImported(feature: any)
|
||||
|
||||
protected createConfirmPanelForWay(
|
||||
|
@ -286,6 +268,23 @@ ${Utils.special_visualizations_importRequirementDocs}
|
|||
const cancel = new SubtleButton(Svg.close_ui(), Translations.t.general.cancel).onClick(onCancel)
|
||||
return new Combine([confirmationMap, confirmButton, cancel]).SetClass("flex flex-col")
|
||||
}
|
||||
|
||||
private parseArgs(argsRaw: string[], originalFeatureTags: UIEventSource<any>): { minzoom: string, max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, tags: string, targetLayer: string, newTags: UIEventSource<Tag[]> } {
|
||||
const baseArgs = Utils.ParseVisArgs(this.args, argsRaw)
|
||||
if (originalFeatureTags !== undefined) {
|
||||
|
||||
const tags = baseArgs.tags
|
||||
if (tags.indexOf(" ") < 0 && tags.indexOf(";") < 0 && originalFeatureTags.data[tags] !== undefined) {
|
||||
// This might be a property to expand...
|
||||
const items: string = originalFeatureTags.data[tags]
|
||||
console.debug("The import button is using tags from properties[" + tags + "] of this object, namely ", items)
|
||||
baseArgs["newTags"] = TagApplyButton.generateTagsToApply(items, originalFeatureTags)
|
||||
} else {
|
||||
baseArgs["newTags"] = TagApplyButton.generateTagsToApply(tags, originalFeatureTags)
|
||||
}
|
||||
}
|
||||
return baseArgs
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflateButton extends AbstractImportButton {
|
||||
|
@ -299,10 +298,6 @@ export class ConflateButton extends AbstractImportButton {
|
|||
);
|
||||
}
|
||||
|
||||
protected canBeImported(feature: any) {
|
||||
return feature.geometry.type === "LineString" || (feature.geometry.type === "Polygon" && feature.geometry.coordinates.length === 1)
|
||||
}
|
||||
|
||||
getLayerDependencies(argsRaw: string[]): string[] {
|
||||
const deps = super.getLayerDependencies(argsRaw);
|
||||
// Force 'type_node' as dependency
|
||||
|
@ -350,6 +345,10 @@ export class ConflateButton extends AbstractImportButton {
|
|||
)
|
||||
}
|
||||
|
||||
protected canBeImported(feature: any) {
|
||||
return feature.geometry.type === "LineString" || (feature.geometry.type === "Polygon" && feature.geometry.coordinates.length === 1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ImportWayButton extends AbstractImportButton {
|
||||
|
@ -498,29 +497,14 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
name: "max_snap_distance",
|
||||
doc: "The maximum distance that the imported point will be moved to snap onto a way in an already existing layer (in meters). This is previewed to the contributor, similar to the 'add new point'-action of MapComplete",
|
||||
defaultValue: "5"
|
||||
},{
|
||||
name:"note_id",
|
||||
doc:"If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'"
|
||||
}],
|
||||
}, {
|
||||
name: "note_id",
|
||||
doc: "If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'"
|
||||
}],
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
canBeImported(feature: any) {
|
||||
return feature.geometry.type === "Point"
|
||||
}
|
||||
|
||||
getLayerDependencies(argsRaw: string[]): string[] {
|
||||
const deps = super.getLayerDependencies(argsRaw);
|
||||
const layerSnap = argsRaw["snap_onto_layers"] ?? ""
|
||||
if (layerSnap === "") {
|
||||
return deps
|
||||
}
|
||||
|
||||
deps.push(...layerSnap.split(";"))
|
||||
return deps
|
||||
}
|
||||
|
||||
private static createConfirmPanelForPoint(
|
||||
args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource<any>, targetLayer: string, note_id: string },
|
||||
state: FeaturePipelineState,
|
||||
|
@ -539,8 +523,8 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
snapOnto = await OsmObject.DownloadObjectAsync(snapOntoWayId)
|
||||
}
|
||||
let specialMotivation = undefined
|
||||
if(args.note_id !== undefined){
|
||||
specialMotivation = "source: https://osm.org/note/"+args.note_id
|
||||
if (args.note_id !== undefined) {
|
||||
specialMotivation = "source: https://osm.org/note/" + args.note_id
|
||||
}
|
||||
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
|
||||
theme: state.layoutToUse.id,
|
||||
|
@ -553,8 +537,13 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
state.selectedElement.setData(state.allElements.ContainingFeatures.get(
|
||||
newElementAction.newElementId
|
||||
))
|
||||
if(args.note_id !== undefined){
|
||||
state.osmConnection.closeNote(args.note_id, "imported")
|
||||
if (args.note_id !== undefined) {
|
||||
let note_id = args.note_id
|
||||
if (isNaN(Number(args.note_id))) {
|
||||
note_id = originalFeatureTags.data[args.note_id]
|
||||
}
|
||||
|
||||
state.osmConnection.closeNote(note_id, "imported")
|
||||
originalFeatureTags.data["closed_at"] = new Date().toISOString()
|
||||
originalFeatureTags.ping()
|
||||
}
|
||||
|
@ -569,7 +558,8 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
preciseInput: {
|
||||
snapToLayers: args.snap_onto_layers?.split(";"),
|
||||
maxSnapDistance: Number(args.max_snap_distance)
|
||||
}
|
||||
},
|
||||
boundsFactor: 3
|
||||
}
|
||||
|
||||
const [lon, lat] = feature.geometry.coordinates
|
||||
|
@ -580,6 +570,21 @@ export class ImportPointButton extends AbstractImportButton {
|
|||
|
||||
}
|
||||
|
||||
canBeImported(feature: any) {
|
||||
return feature.geometry.type === "Point"
|
||||
}
|
||||
|
||||
getLayerDependencies(argsRaw: string[]): string[] {
|
||||
const deps = super.getLayerDependencies(argsRaw);
|
||||
const layerSnap = argsRaw["snap_onto_layers"] ?? ""
|
||||
if (layerSnap === "") {
|
||||
return deps
|
||||
}
|
||||
|
||||
deps.push(...layerSnap.split(";"))
|
||||
return deps
|
||||
}
|
||||
|
||||
constructElement(state, args,
|
||||
originalFeatureTags,
|
||||
guiState,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue