From 951bd3c0aee598d23fad9ded2aede2b8b6496ade Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 1 Dec 2024 01:39:13 +0100 Subject: [PATCH] More work on inspector --- assets/layers/questions/questions.json | 14 ++- src/Logic/ImageProviders/Panoramax.ts | 8 +- src/UI/History/AggregateImages.svelte | 43 +++++++ src/UI/History/AggregateView.svelte | 80 ++++++++---- .../History/AttributedPanoramaxImage.svelte | 13 ++ src/UI/History/History.svelte | 119 +++++++++--------- src/UI/History/HistoryUtils.ts | 24 +++- src/UI/Image/AttributedImage.svelte | 10 +- src/UI/InspectorGUI.svelte | 54 +++++--- 9 files changed, 257 insertions(+), 108 deletions(-) create mode 100644 src/UI/History/AggregateImages.svelte create mode 100644 src/UI/History/AttributedPanoramaxImage.svelte diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index a5b70f182..8ca79bbff 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -3077,7 +3077,19 @@ } } ] + }, + { + "id": "name", + "question":{ + "en": "What is the name of this place?" + }, + "render": { + "*": "{name}" + }, + "freeform": { + "key": "name" + } } ], "allowMove": false -} \ No newline at end of file +} diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index bd8970efe..4f818ba18 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -12,7 +12,7 @@ import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" import Link from "../../UI/Base/Link" export default class PanoramaxImageProvider extends ImageProvider { - public static readonly singleton = new PanoramaxImageProvider() + public static readonly singleton: PanoramaxImageProvider = new PanoramaxImageProvider() private static readonly xyz = new PanoramaxXYZ() private static defaultPanoramax = new AuthorizedPanoramax( Constants.panoramax.url, @@ -126,7 +126,11 @@ export default class PanoramaxImageProvider extends ImageProvider { if (!Panoramax.isId(value)) { return undefined } - return [await this.getInfoFor(value).then((r) => this.featureToImage(r))] + return [await this.getInfo(value)] + } + + public async getInfo(hash: string): Promise { + return await this.getInfoFor(hash).then((r) => this.featureToImage(r)) } getRelevantUrls(tags: Record, prefixes: string[]): Store { diff --git a/src/UI/History/AggregateImages.svelte b/src/UI/History/AggregateImages.svelte new file mode 100644 index 000000000..9955360eb --- /dev/null +++ b/src/UI/History/AggregateImages.svelte @@ -0,0 +1,43 @@ + +{#if $allDiffs === undefined} + +{:else if $addedImages.length === 0} + No images added by this contributor +{:else} + {#each $addedImages as imgDiff} + + {/each} +{/if} diff --git a/src/UI/History/AggregateView.svelte b/src/UI/History/AggregateView.svelte index ae1afcb24..79baeda81 100644 --- a/src/UI/History/AggregateView.svelte +++ b/src/UI/History/AggregateView.svelte @@ -5,6 +5,11 @@ import { OsmObject } from "../../Logic/Osm/OsmObject" import Loading from "../Base/Loading.svelte" import { HistoryUtils } from "./HistoryUtils" + import * as shared_questions from "../../assets/generated/layers/questions.json" + import TagRenderingQuestion from "../Popup/TagRendering/TagRenderingQuestion.svelte" + import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" + import Tr from "../Base/Tr.svelte" + import AccordionSingle from "../Flowbite/AccordionSingle.svelte" export let onlyShowUsername: string export let features: Feature[] @@ -13,26 +18,28 @@ let allHistories: UIEventSource = UIEventSource.FromPromise( Promise.all(features.map(f => downloader.downloadHistory(f.properties.id))) ) - let allDiffs: Store<{ key: string; value?: string; oldValue?: string }[]> = allHistories.mapD(histories => { - const allDiffs = [].concat(...histories.map( - history => { - const filtered = history.filter(step => !onlyShowUsername || step.tags["_last_edit:contributor"] === onlyShowUsername) - const diffs: { - key: string; - value?: string; - oldValue?: string - }[][] = filtered.map(step => HistoryUtils.tagHistoryDiff(step, history)) - return [].concat(...diffs) - } - )) - return allDiffs - }) + let allDiffs: Store<{ + key: string; + value?: string; + oldValue?: string + }[]> = allHistories.mapD(histories => HistoryUtils.fullHistoryDiff(histories, onlyShowUsername)) - const mergedCount = allDiffs.mapD(allDiffs => { + const trs = shared_questions.tagRenderings.map(tr => new TagRenderingConfig(tr)) + + function detectQuestion(key: string): TagRenderingConfig { + return trs.find(tr => tr.freeform?.key === key) + } + + const mergedCount: Store<{ + key: string; + tr: TagRenderingConfig; + count: number; + values: { value: string; count: number }[] + }[]> = allDiffs.mapD(allDiffs => { const keyCounts = new Map>() for (const diff of allDiffs) { const k = diff.key - if(!keyCounts.has(k)){ + if (!keyCounts.has(k)) { keyCounts.set(k, new Map()) } const valueCounts = keyCounts.get(k) @@ -40,34 +47,57 @@ valueCounts.set(v, 1 + (valueCounts.get(v) ?? 0)) } - const perKey: {key: string, count: number, values: - {value: string, count: number}[] + const perKey: { + key: string, tr: TagRenderingConfig, count: number, values: + { value: string, count: number }[] }[] = [] keyCounts.forEach((values, key) => { - const keyTotal : {value: string, count: number}[] = [] + const keyTotal: { value: string, count: number }[] = [] values.forEach((count, value) => { - keyTotal.push({value, count}) + keyTotal.push({ value, count }) }) let countForKey = 0 - for (const {count} of keyTotal) { + for (const { count } of keyTotal) { countForKey += count } keyTotal.sort((a, b) => b.count - a.count) - perKey.push({count: countForKey, key, values: keyTotal}) + const tr = detectQuestion(key) + perKey.push({ count: countForKey, tr, key, values: keyTotal }) }) perKey.sort((a, b) => b.count - a.count) return perKey }) + {#if allHistories === undefined} {:else if $allDiffs !== undefined} {#each $mergedCount as diff} -
- {JSON.stringify(diff)} -
+

+ {#if diff.tr} + + {:else} + {diff.key} + {/if} +

+ + + + Answered {diff.count} times + +
    + {#each diff.values as value} +
  • + {value.value} + {#if value.count > 1} + - {value.count} + {/if} +
  • + {/each} +
+
{/each} {/if} diff --git a/src/UI/History/AttributedPanoramaxImage.svelte b/src/UI/History/AttributedPanoramaxImage.svelte new file mode 100644 index 000000000..83463ada6 --- /dev/null +++ b/src/UI/History/AttributedPanoramaxImage.svelte @@ -0,0 +1,13 @@ + + +{#if $image !== undefined} + +{/if} diff --git a/src/UI/History/History.svelte b/src/UI/History/History.svelte index 16ee90ed3..e4354b048 100644 --- a/src/UI/History/History.svelte +++ b/src/UI/History/History.svelte @@ -8,7 +8,6 @@ import Loading from "../Base/Loading.svelte" import { HistoryUtils } from "./HistoryUtils" import ToSvelte from "../Base/ToSvelte.svelte" - import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import Tr from "../Base/Tr.svelte" export let onlyShowChangesBy: string @@ -28,68 +27,76 @@ console.log("Comparing ", step.tags["_last_edit:contributor"], onlyShowChangesBy, step.tags["_last_edit:contributor"] === onlyShowChangesBy) return step.tags["_last_edit:contributor"] === onlyShowChangesBy + }).map(({ step, layer }) => { + const diff = HistoryUtils.tagHistoryDiff(step, fullHistory.data) + return { step, layer, diff } })) let lastStep = filteredHistory.mapD(history => history.at(-1)) -let l : LayerConfig - // l.title.GetRenderValue({}).Subs({}) + let allGeometry = filteredHistory.mapD(all => !all.some(x => x.diff.length > 0)) + /** + * These layers are only shown if there are tag changes as well + */ + const ignoreLayersIfNoChanges: ReadonlySet = new Set(["walls_and_buildings"]) -{#if $lastStep?.layer} - -

-
- -
- -

-
-{/if} +{#if !$allGeometry || !ignoreLayersIfNoChanges.has($lastStep?.layer?.id)} + {#if $lastStep?.layer} + +

+
+ +
+ +

+
+ {/if} -{#if !$filteredHistory} - Loading history... -{:else if $filteredHistory.length === 0} - Only geometry changes found -{:else} - - {#each $filteredHistory as { step, layer }} + {#if !$filteredHistory} + Loading history... + {:else if $filteredHistory.length === 0} + Only geometry changes found + {:else} +
+ {#each $filteredHistory as { step, layer }} - {#if step.version === 1} - - - - {/if} - {#if HistoryUtils.tagHistoryDiff(step, $fullHistory).length === 0} - - - - {:else} - {#each HistoryUtils.tagHistoryDiff(step, $fullHistory) as diff} + {#if step.version === 1} - - - {#if diff.oldValue === undefined} - - - {:else if diff.value === undefined } - - - {:else} - - - {/if} - - + - {/each} - {/if} - {/each} -
-

- Created by {step.tags["_last_edit:contributor"]} -

-
- Only changes in geometry -
{step.version}{layer?.id ?? "Unknown layer"}{diff.key}{diff.value}{diff.key} {diff.value}{diff.key} {diff.oldValue} → {diff.value} +

+ Created by {step.tags["_last_edit:contributor"]} +

+
+ {/if} + {#if HistoryUtils.tagHistoryDiff(step, $fullHistory).length === 0} + + + Only changes in geometry + + + {:else} + {#each HistoryUtils.tagHistoryDiff(step, $fullHistory) as diff} + + {step.version} + {layer?.id ?? "Unknown layer"} + {#if diff.oldValue === undefined} + {diff.key} + {diff.value} + {:else if diff.value === undefined } + {diff.key} + {diff.value} + {:else} + {diff.key} + {diff.oldValue} → {diff.value} + {/if} + + + + {/each} + {/if} + {/each} + + {/if} {/if} diff --git a/src/UI/History/HistoryUtils.ts b/src/UI/History/HistoryUtils.ts index 768d0754d..9ba4bf0fe 100644 --- a/src/UI/History/HistoryUtils.ts +++ b/src/UI/History/HistoryUtils.ts @@ -4,7 +4,7 @@ import { OsmObject } from "../../Logic/Osm/OsmObject" export class HistoryUtils { - private static personalTheme = new ThemeConfig( all_layers, true) + public static readonly personalTheme = new ThemeConfig( all_layers, true) private static ignoredLayers = new Set(["fixme"]) public static determineLayer(properties: Record){ return this.personalTheme.getMatchingLayer(properties, this.ignoredLayers) @@ -13,12 +13,13 @@ export class HistoryUtils { public static tagHistoryDiff(step: OsmObject, history: OsmObject[]): { key: string, value?: string, - oldValue?: string + oldValue?: string, + step: OsmObject }[] { const previous = history[step.version - 2] if (!previous) { return Object.keys(step.tags).filter(key => !key.startsWith("_") && key !== "id").map(key => ({ - key, value: step.tags[key] + key, value: step.tags[key], step })) } const previousTags = previous.tags @@ -27,9 +28,24 @@ export class HistoryUtils { const value = step.tags[key] const oldValue = previousTags[key] return { - key, value, oldValue + key, value, oldValue, step } }).filter(ch => ch.oldValue !== ch.value) } + public static fullHistoryDiff(histories: OsmObject[][], onlyShowUsername?: string){ + const allDiffs: {key: string, oldValue?: string, value?: string}[] = [].concat(...histories.map( + history => { + const filtered = history.filter(step => !onlyShowUsername || step.tags["_last_edit:contributor"] === onlyShowUsername) + const diffs: { + key: string; + value?: string; + oldValue?: string + }[][] = filtered.map(step => HistoryUtils.tagHistoryDiff(step, history)) + return [].concat(...diffs) + } + )) + return allDiffs + } + } diff --git a/src/UI/Image/AttributedImage.svelte b/src/UI/Image/AttributedImage.svelte index 02a871249..546de016d 100644 --- a/src/UI/Image/AttributedImage.svelte +++ b/src/UI/Image/AttributedImage.svelte @@ -28,22 +28,24 @@ export let imgClass: string = undefined export let state: SpecialVisualizationState = undefined export let attributionFormat: "minimal" | "medium" | "large" = "medium" - export let previewedImage: UIEventSource + export let previewedImage: UIEventSource = undefined export let canZoom = previewedImage !== undefined let loaded = false let showBigPreview = new UIEventSource(false) onDestroy( showBigPreview.addCallbackAndRun((shown) => { if (!shown) { - previewedImage.set(undefined) + previewedImage?.set(undefined) } }) ) + if(previewedImage){ onDestroy( previewedImage.addCallbackAndRun((previewedImage) => { showBigPreview.set(previewedImage?.id === image.id) }) ) + } function highlight(entered: boolean = true) { if (!entered) { @@ -82,7 +84,7 @@ class="normal-background" on:click={() => { console.log("Closing") - previewedImage.set(undefined) + previewedImage?.set(undefined) }} /> @@ -124,7 +126,7 @@ {#if canZoom && loaded}
previewedImage.set(image)} + on:click={() => previewedImage?.set(image)} >
diff --git a/src/UI/InspectorGUI.svelte b/src/UI/InspectorGUI.svelte index d02f513a3..c7d187013 100644 --- a/src/UI/InspectorGUI.svelte +++ b/src/UI/InspectorGUI.svelte @@ -21,6 +21,8 @@ import { XCircleIcon } from "@babeard/svelte-heroicons/solid" import { Utils } from "../Utils" import AggregateView from "./History/AggregateView.svelte" + import { HistoryUtils } from "./History/HistoryUtils" + import AggregateImages from "./History/AggregateImages.svelte" let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user") let step = new UIEventSource<"waiting" | "loading" | "done">("waiting") @@ -49,18 +51,28 @@ let featuresStore = new UIEventSource([]) let features = new StaticFeatureSource(featuresStore) - new ShowDataLayer(map, - { - layer, - zoomToFeatures: true, - features, - onClick: (f: Feature) => { - selectedElement.set(undefined) - Utils.waitFor(200).then(() => { - selectedElement.set(f) - }) - } - }) + ShowDataLayer.showMultipleLayers(map, features, HistoryUtils.personalTheme.layers, { + zoomToFeatures: true, + onClick: (f: Feature) => { + selectedElement.set(undefined) + Utils.waitFor(200).then(() => { + selectedElement.set(f) + }) + } + }) + + /* new ShowDataLayer(map, + { + layer, + zoomToFeatures: true, + features, + onClick: (f: Feature) => { + selectedElement.set(undefined) + Utils.waitFor(200).then(() => { + selectedElement.set(f) + }) + } + })*/ async function load() { @@ -87,7 +99,7 @@ return true }) - let mode: "map" | "table" | "aggregate" = "map" + let mode: "map" | "table" | "aggregate" | "images" = "map"
@@ -113,6 +125,9 @@ +
{#if mode === "map"} @@ -155,11 +170,18 @@ {:else if mode === "table"} +
{#each $featuresStore as f} -

{f.properties.id}

{/each} - {:else} - +
+ {:else if mode === "aggregate"} +
+ +
+ {:else if mode === "images"} +
+ +
{/if}