Automatically load appropriate suggestions for brands and operators

This commit is contained in:
Pieter Vander Vennet 2024-05-23 11:28:51 +02:00
parent 3146fa0d26
commit 67f23eea14
11 changed files with 70 additions and 47 deletions

View file

@ -4,22 +4,12 @@ import * as nsiWD from "../node_modules/name-suggestion-index/dist/wikidata.min.
import { existsSync, writeFileSync } from "fs" import { existsSync, writeFileSync } from "fs"
import ScriptUtils from "./ScriptUtils" import ScriptUtils from "./ScriptUtils"
import { Utils } from "../src/Utils" import { Utils } from "../src/Utils"
import { WikimediaImageProvider } from "../src/Logic/ImageProviders/WikimediaImageProvider"
import { renameSync } from "node:fs"
export default class DownloadNsiLogos extends Script { class DownloadNsiLogos extends Script {
constructor() { constructor() {
super("Downloads all images of the NSI") super("Downloads all images of the NSI")
} }
private async getWikimediaUrl(startUrl: string) {
if (!startUrl) {
return startUrl
}
}
private async downloadLogo(nsiItem: NSIItem, type: string, basePath: string) { private async downloadLogo(nsiItem: NSIItem, type: string, basePath: string) {
try { try {
return await this.downloadLogoUnsafe(nsiItem, type, basePath) return await this.downloadLogoUnsafe(nsiItem, type, basePath)
@ -71,8 +61,8 @@ export default class DownloadNsiLogos extends Script {
} }
if ((<string>logos.wikidata).toLowerCase().endsWith(".svg")) { if ((<string>logos.wikidata).toLowerCase().endsWith(".svg")) {
console.log("Written SVG", path) console.log("Written SVG", path)
if(!path.endsWith(".svg")){ if (!path.endsWith(".svg")) {
throw "Undetected svg path:"+logos.wikidata throw "Undetected svg path:" + logos.wikidata
} }
writeFileSync(path, dloaded["content"], "utf8") writeFileSync(path, dloaded["content"], "utf8")
return true return true
@ -90,8 +80,12 @@ export default class DownloadNsiLogos extends Script {
} }
async main(args: string[]): Promise<void> { async main(): Promise<void> {
const type = "brand" await this.downloadFor("operator")
await this.downloadFor("brand")
}
async downloadFor(type: "brand" | "operator"): Promise<void> {
const items = NameSuggestionIndex.allPossible(type) const items = NameSuggestionIndex.allPossible(type)
const basePath = "./public/assets/data/nsi/logos/" const basePath = "./public/assets/data/nsi/logos/"
let downloadCount = 0 let downloadCount = 0

View file

@ -883,7 +883,8 @@ export class TagRenderingConfigUtils {
return config return config
} }
const clone: TagRenderingConfig = Object.create(config) const clone: TagRenderingConfig = Object.create(config)
clone.mappings = [...clone.mappings ?? [], ...extraMappings] const oldMappingsCloned = clone.mappings?.map(m => ({ ...m, priorityIf: m.priorityIf ?? TagUtils.Tag("id~*") })) ?? []
clone.mappings = [...oldMappingsCloned, ...extraMappings]
return clone return clone
}) })
} }

View file

@ -11,6 +11,7 @@
import UserRelatedState from "../../Logic/State/UserRelatedState" import UserRelatedState from "../../Logic/State/UserRelatedState"
import Delete_icon from "../../assets/svg/Delete_icon.svelte" import Delete_icon from "../../assets/svg/Delete_icon.svelte"
import BackButton from "../Base/BackButton.svelte" import BackButton from "../Base/BackButton.svelte"
import TagRenderingEditableDynamic from "../Popup/TagRendering/TagRenderingEditableDynamic.svelte"
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
export let selectedElement: Feature export let selectedElement: Feature
@ -68,7 +69,7 @@
tabindex="-1" tabindex="-1"
> >
{#each $knownTagRenderings as config (config.id)} {#each $knownTagRenderings as config (config.id)}
<TagRenderingEditable <TagRenderingEditableDynamic
{tags} {tags}
{config} {config}
{state} {state}

View file

@ -550,14 +550,13 @@ export default class ShowDataLayer {
return return
} }
const bbox = BBox.bboxAroundAll(features.map(BBox.get)) const bbox = BBox.bboxAroundAll(features.map(BBox.get))
console.log("Zooming to features", bbox.asGeoJson()) console.debug("Zooming to features", bbox.asGeoJson())
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
map.resize()
map.resize() map.fitBounds(bbox.toLngLat(), {
map.fitBounds(bbox.toLngLat(), { padding: { top: 10, bottom: 10, left: 10, right: 10 },
padding: { top: 10, bottom: 10, left: 10, right: 10 }, animate: false
animate: false })
})
}) })
} }

View file

@ -55,7 +55,7 @@ export default class OpeningHoursVisualization extends Toggle {
applicableWeek.startingMonday applicableWeek.startingMonday
) )
Locale.language.mapD((lng) => { Locale.language.mapD((lng) => {
console.log("Setting OH description to", lng, textual) console.debug("Setting OH description to", lng, textual)
vis.ConstructElement().ariaLabel = textual.textFor(lng) vis.ConstructElement().ariaLabel = textual.textFor(lng)
}) })
return vis return vis

View file

@ -3,16 +3,16 @@
* Shows all questions for which the answers are unknown. * Shows all questions for which the answers are unknown.
* The questions can either be shown all at once or one at a time (in which case they can be skipped) * The questions can either be shown all at once or one at a time (in which case they can be skipped)
*/ */
import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
import { Store, UIEventSource } from "../../../Logic/UIEventSource" import { UIEventSource } from "../../../Logic/UIEventSource"
import type { Feature } from "geojson" import type { Feature } from "geojson"
import type { SpecialVisualizationState } from "../../SpecialVisualization" import type { SpecialVisualizationState } from "../../SpecialVisualization"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
import Tr from "../../Base/Tr.svelte" import Tr from "../../Base/Tr.svelte"
import Translations from "../../i18n/Translations.js" import Translations from "../../i18n/Translations.js"
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
import TagRenderingQuestionDynamic from "./TagRenderingQuestionDynamic.svelte" import TagRenderingQuestionDynamic from "./TagRenderingQuestionDynamic.svelte"
export let layer: LayerConfig export let layer: LayerConfig
@ -26,7 +26,8 @@
export let onlyForLabels: string[] | undefined = undefined export let onlyForLabels: string[] | undefined = undefined
const _onlyForLabels = new Set(onlyForLabels) const _onlyForLabels = new Set(onlyForLabels)
/** /**
* If set, only questions _not_ having these labels will be shown * If set, only questions _not_ having these labels will be shown.
* This is used for a partial questionbox
*/ */
export let notForLabels: string[] | undefined = undefined export let notForLabels: string[] | undefined = undefined
const _notForLabels = new Set(notForLabels) const _notForLabels = new Set(notForLabels)
@ -41,14 +42,15 @@
} }
return true return true
} }
const baseQuestions = (layer.tagRenderings ?? [])?.filter(
(tr) => allowed(tr.labels) && tr.question !== undefined
)
let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>()) let skippedQuestions = new UIEventSource<Set<string>>(new Set<string>())
let questionboxElem: HTMLDivElement let questionboxElem: HTMLDivElement
let questionsToAsk = tags.map( let questionsToAsk = tags.map(
(tags) => { (tags) => {
const baseQuestions = (layer.tagRenderings ?? [])?.filter(
(tr) => allowed(tr.labels) && tr.question !== undefined
)
const questionsToAsk: TagRenderingConfig[] = [] const questionsToAsk: TagRenderingConfig[] = []
for (const baseQuestion of baseQuestions) { for (const baseQuestion of baseQuestions) {
if (skippedQuestions.data.has(baseQuestion.id)) { if (skippedQuestions.data.has(baseQuestion.id)) {

View file

@ -11,7 +11,7 @@
import { Utils } from "../../../Utils" import { Utils } from "../../../Utils"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
import EditButton from "./EditButton.svelte" import EditButton from "./EditButton.svelte"
import TagRenderingAnswerDynamic from "./TagRenderingAnswerDynamic.svelte" import TagRenderingAnswer from "./TagRenderingAnswer.svelte"
export let config: TagRenderingConfig export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>> export let tags: UIEventSource<Record<string, string>>
@ -100,7 +100,7 @@
</TagRenderingQuestion> </TagRenderingQuestion>
{:else} {:else}
<div class="low-interaction flex items-center justify-between overflow-hidden rounded px-2"> <div class="low-interaction flex items-center justify-between overflow-hidden rounded px-2">
<TagRenderingAnswerDynamic id={answerId} {config} {tags} {selectedElement} {state} {layer} /> <TagRenderingAnswer id={answerId} {config} {tags} {selectedElement} {state} {layer} />
<EditButton <EditButton
arialabel={config.editButtonAriaLabel} arialabel={config.editButtonAriaLabel}
ariaLabelledBy={answerId} ariaLabelledBy={answerId}
@ -112,7 +112,7 @@
{/if} {/if}
{:else} {:else}
<div class="h-full w-full overflow-hidden"> <div class="h-full w-full overflow-hidden">
<TagRenderingAnswerDynamic {config} {tags} {selectedElement} {state} {layer} /> <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} />
</div> </div>
{/if} {/if}
</div> </div>

View file

@ -0,0 +1,26 @@
<script lang="ts">
import TagRenderingConfig, { TagRenderingConfigUtils } from "../../../Models/ThemeConfig/TagRenderingConfig"
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
import type { Feature } from "geojson"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import TagRenderingEditable from "./TagRenderingEditable.svelte"
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature | undefined
export let state: SpecialVisualizationState
export let layer: LayerConfig = undefined
export let editingEnabled: Store<boolean> | undefined = state?.featureSwitchUserbadge
export let highlightedRendering: UIEventSource<string> = undefined
export let clss = undefined
export let editMode = !config.IsKnown(tags.data)
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
</script>
<TagRenderingEditable config={$dynamicConfig} {editMode} {clss} {highlightedRendering} {editingEnabled} {layer} {state}
{selectedElement} {tags} />

View file

@ -3,7 +3,6 @@
import type { SpecialVisualizationState } from "../../SpecialVisualization" import type { SpecialVisualizationState } from "../../SpecialVisualization"
import Tr from "../../Base/Tr.svelte" import Tr from "../../Base/Tr.svelte"
import type { Feature } from "geojson" import type { Feature } from "geojson"
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
import { TagsFilter } from "../../../Logic/Tags/TagsFilter" import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
import FreeformInput from "./FreeformInput.svelte" import FreeformInput from "./FreeformInput.svelte"
@ -59,7 +58,6 @@
*/ */
let checkedMappings: boolean[] let checkedMappings: boolean[]
let mappings: Mapping[] = config?.mappings ?? []
let searchTerm: UIEventSource<string> = new UIEventSource("") let searchTerm: UIEventSource<string> = new UIEventSource("")
let dispatch = createEventDispatcher<{ let dispatch = createEventDispatcher<{
@ -74,7 +72,7 @@
*/ */
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig): void { function initialize(tgs: Record<string, string>, confg: TagRenderingConfig): void {
mappings = confg.mappings?.filter((m) => { const mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") { if (typeof m.hideInAnswer === "boolean") {
return !m.hideInAnswer return !m.hideInAnswer
} }
@ -169,7 +167,7 @@
onDestroy( onDestroy(
freeformInput.subscribe((freeformValue) => { freeformInput.subscribe((freeformValue) => {
if (!mappings || mappings?.length == 0 || config.freeform?.key === undefined) { if (!config?.mappings || config?.mappings?.length == 0 || config.freeform?.key === undefined) {
return return
} }
// If a freeform value is given, mark the 'mapping' as marked // If a freeform value is given, mark the 'mapping' as marked
@ -178,11 +176,11 @@
// Initialization didn't yet run // Initialization didn't yet run
return return
} }
checkedMappings[mappings.length] = freeformValue?.length > 0 checkedMappings[config?.mappings.length] = freeformValue?.length > 0
return return
} }
if (freeformValue?.length > 0) { if (freeformValue?.length > 0) {
selectedMapping = mappings.length selectedMapping = config.mappings.length
} }
}) })
) )
@ -192,7 +190,7 @@
allowDeleteOfFreeform && allowDeleteOfFreeform &&
$freeformInput === undefined && $freeformInput === undefined &&
$freeformInputUnvalidated === "" && $freeformInputUnvalidated === "" &&
(mappings?.length ?? 0) === 0 (config?.mappings?.length ?? 0) === 0
) { ) {
selectedTags = new Tag(config.freeform.key, "") selectedTags = new Tag(config.freeform.key, "")
} else { } else {
@ -353,7 +351,7 @@
{#if config.freeform?.key && !(mappings?.length > 0)} {#if config.freeform?.key && !(config?.mappings?.length > 0)}
<!-- There are no options to choose from, simply show the input element: fill out the text field --> <!-- There are no options to choose from, simply show the input element: fill out the text field -->
<FreeformInput <FreeformInput
{config} {config}
@ -367,7 +365,7 @@
unvalidatedText={freeformInputUnvalidated} unvalidatedText={freeformInputUnvalidated}
on:submit={() => onSave()} on:submit={() => onSave()}
/> />
{:else if mappings !== undefined && !config.multiAnswer} {:else if config.mappings !== undefined && !config.multiAnswer}
<!-- Simple radiobuttons as mapping --> <!-- Simple radiobuttons as mapping -->
<div class="flex flex-col"> <div class="flex flex-col">
{#each config.mappings as mapping, i (mapping.then)} {#each config.mappings as mapping, i (mapping.then)}
@ -416,7 +414,7 @@
</label> </label>
{/if} {/if}
</div> </div>
{:else if mappings !== undefined && config.multiAnswer} {:else if config.mappings !== undefined && config.multiAnswer}
<!-- Multiple answers can be chosen: checkboxes --> <!-- Multiple answers can be chosen: checkboxes -->
<div class="flex flex-col"> <div class="flex flex-col">
{#each config.mappings as mapping, i (mapping.then)} {#each config.mappings as mapping, i (mapping.then)}
@ -480,7 +478,7 @@
<!-- TagRenderingQuestion-buttons --> <!-- TagRenderingQuestion-buttons -->
<slot name="cancel" /> <slot name="cancel" />
<slot name="save-button" {selectedTags}> <slot name="save-button" {selectedTags}>
{#if allowDeleteOfFreeform && (mappings?.length ?? 0) === 0 && $freeformInput === undefined && $freeformInputUnvalidated === ""} {#if allowDeleteOfFreeform && (config?.mappings?.length ?? 0) === 0 && $freeformInput === undefined && $freeformInputUnvalidated === ""}
<button <button
class="primary flex" class="primary flex"
on:click|stopPropagation|preventDefault={() => onSave()} on:click|stopPropagation|preventDefault={() => onSave()}

View file

@ -8,6 +8,7 @@ import type { SpecialVisualizationState } from "../../SpecialVisualization"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import TagRenderingQuestion from "./TagRenderingQuestion.svelte" import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
import type { UploadableTag } from "../../../Logic/Tags/TagUtils" import type { UploadableTag } from "../../../Logic/Tags/TagUtils"
import { writable } from "svelte/store"
export let config: TagRenderingConfig export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>> export let tags: UIEventSource<Record<string, string>>
@ -22,6 +23,7 @@ export let allowDeleteOfFreeform: boolean = false
let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement)
</script> </script>
<TagRenderingQuestion <TagRenderingQuestion

View file

@ -685,6 +685,6 @@
<CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme"/> <CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme"/>
<CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu"/> <CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu"/>
<CloseAnimation isOpened={selectedLayer.map(sl => (sl !== undefined && sl === currentViewLayer))} moveTo={openCurrentViewLayerButton} debug="currentViewLayer"/> <CloseAnimation isOpened={selectedLayer.map(sl => (sl !== undefined && sl === currentViewLayer))} moveTo={openCurrentViewLayerButton} debug="currentViewLayer"/>
<CloseAnimation isOpened={selectedElement.map(sl =>{ console.log("SE is", sl); return sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId })} moveTo={openNewElementButton} debug="newElement"/> <CloseAnimation isOpened={selectedElement.map(sl => sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId)} moveTo={openNewElementButton} debug="newElement"/>
<CloseAnimation isOpened={state.guistate.filtersPanelIsOpened} moveTo={openFilterButton} debug="filter"/> <CloseAnimation isOpened={state.guistate.filtersPanelIsOpened} moveTo={openFilterButton} debug="filter"/>
<CloseAnimation isOpened={state.guistate.backgroundLayerSelectionIsOpened} moveTo={openBackgroundButton} debug="bg"/> <CloseAnimation isOpened={state.guistate.backgroundLayerSelectionIsOpened} moveTo={openBackgroundButton} debug="bg"/>