diff --git a/scripts/build.sh b/scripts/build.sh index 3d3186b73..fa5638783 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -24,6 +24,8 @@ else exit 1 fi +cp node_modules/name-suggestion-index/dist/nsi.json public/assets/data/nsi +cp node_modules/name-suggestion-index/dist/wikidata.min.json public/assets/data/nsi export NODE_OPTIONS=--max-old-space-size=16000 which vite diff --git a/src/Logic/Web/NameSuggestionIndex.ts b/src/Logic/Web/NameSuggestionIndex.ts index 1560fd189..94d245591 100644 --- a/src/Logic/Web/NameSuggestionIndex.ts +++ b/src/Logic/Web/NameSuggestionIndex.ts @@ -1,6 +1,3 @@ -import * as nsi from "../../../node_modules/name-suggestion-index/dist/nsi.json" -import * as nsiWD from "../../../node_modules/name-suggestion-index/dist/wikidata.min.json" - import * as nsiFeatures from "../../../node_modules/name-suggestion-index/dist/featureCollection.json" import { LocationConflation } from "@rapideditor/location-conflation" import type { Feature, MultiPolygon } from "geojson" @@ -56,27 +53,53 @@ export interface NSIItem { } export default class NameSuggestionIndex { - private static readonly nsiFile: Readonly = nsi - private static readonly nsiWdFile: Readonly< + public static readonly supportedTypes = ["brand", + "flag", + "operator", + "transit"] as const + private readonly nsiFile: Readonly + private readonly nsiWdFile: Readonly< Record< string, { logos: { wikidata?: string; facebook?: string } } > - > = nsiWD["wikidata"] + > private static loco = new LocationConflation(nsiFeatures) // Some additional boundaries - private static _supportedTypes: string[] + private _supportedTypes: string[] - public static supportedTypes(): string[] { + constructor(nsiFile: Readonly, nsiWdFile: Readonly< + Record< + string, + { + logos: { wikidata?: string; facebook?: string } + } + >>) { + this.nsiFile = nsiFile + this.nsiWdFile = nsiWdFile + } + + private static inited: NameSuggestionIndex = undefined + + public static async getNsiIndex(): Promise { + if (NameSuggestionIndex.inited) { + return NameSuggestionIndex.inited + } + const [nsi, nsiWd] = await Promise.all(["assets/data/nsi/nsi.json", "assets/data/nsi/wikidata.min.json"].map(url => Utils.downloadJsonCached(url, 1000 * 60 * 60 * 24 * 30))) + NameSuggestionIndex.inited = new NameSuggestionIndex(nsi, nsiWd["wikidata"]) + return NameSuggestionIndex.inited + } + + public supportedTypes(): string[] { if (this._supportedTypes) { return this._supportedTypes } - const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi) + const keys = Object.keys(this.nsiFile.nsi) const all = keys.map( - (k) => NameSuggestionIndex.nsiFile.nsi[k].properties.path.split("/")[0] + (k) => this.nsiFile.nsi[k].properties.path.split("/")[0], ) this._supportedTypes = Utils.Dedup(all).map((s) => { if (s.endsWith("s")) { @@ -99,13 +122,13 @@ export default class NameSuggestionIndex { try { return Utils.downloadJsonCached>( `./assets/data/nsi/stats/${type}.${c.toUpperCase()}.json`, - 24 * 60 * 60 * 1000 + 24 * 60 * 60 * 1000, ) } catch (e) { console.error("Could not fetch " + type + " statistics due to", e) return undefined } - }) + }), ) stats = Utils.NoNull(stats) if (stats.length === 1) { @@ -123,7 +146,10 @@ export default class NameSuggestionIndex { return merged } - public static isSvg(nsiItem: NSIItem, type: string): boolean | undefined { + public isSvg(nsiItem: NSIItem, type: string): boolean | undefined { + if (this.nsiWdFile === undefined) { + throw "nsiWdi file is not loaded, cannot determine if " + nsiItem.id + " has an SVG image" + } const logos = this.nsiWdFile[nsiItem?.tags?.[type + ":wikidata"]]?.logos if (!logos) { return undefined @@ -138,7 +164,7 @@ export default class NameSuggestionIndex { return false } - public static async generateMappings( + public async generateMappings( type: string, tags: Record, country: string[], @@ -148,7 +174,7 @@ export default class NameSuggestionIndex { * If set, sort by frequency instead of alphabetically */ sortByFrequency: boolean - } + }, ): Promise { const mappings: (Mapping & { frequency: number })[] = [] const frequencies = await NameSuggestionIndex.fetchFrequenciesFor(type, country) @@ -157,12 +183,12 @@ export default class NameSuggestionIndex { continue } const value = tags[key] - const actualBrands = NameSuggestionIndex.getSuggestionsForKV( + const actualBrands = this.getSuggestionsForKV( type, key, value, country.join(";"), - location + location, ) if (!actualBrands) { continue @@ -177,7 +203,7 @@ export default class NameSuggestionIndex { if (hasIcon) { // Using works fine without an extension for JPG and PNG, but _not_ svg :( icon = "./assets/data/nsi/logos/" + nsiItem.id - if (NameSuggestionIndex.isSvg(nsiItem, type)) { + if (this.isSvg(nsiItem, type)) { icon = icon + ".svg" } } @@ -207,13 +233,13 @@ export default class NameSuggestionIndex { return mappings } - public static supportedTags( - type: "operator" | "brand" | "flag" | "transit" | string + public supportedTags( + type: "operator" | "brand" | "flag" | "transit" | string, ): Record { const tags: Record = {} - const keys = Object.keys(NameSuggestionIndex.nsiFile.nsi) + const keys = Object.keys(this.nsiFile.nsi) for (const key of keys) { - const nsiItem = NameSuggestionIndex.nsiFile.nsi[key] + const nsiItem = this.nsiFile.nsi[key] const path = nsiItem.properties.path const [osmType, osmkey, osmvalue] = path.split("/") if (type !== osmType && type + "s" !== osmType) { @@ -231,9 +257,9 @@ export default class NameSuggestionIndex { * Returns a list of all brands/operators * @param type */ - public static allPossible(type: "brand" | "operator"): NSIItem[] { + public allPossible(type: "brand" | "operator"): NSIItem[] { const options: NSIItem[] = [] - const tags = NameSuggestionIndex.supportedTags(type) + const tags = this.supportedTags(type) for (const osmKey in tags) { const values = tags[osmKey] for (const osmValue of values) { @@ -249,14 +275,14 @@ export default class NameSuggestionIndex { * @param country: a string containing one or more country codes, separated by ";" * @param location: center point of the feature, should be [lon, lat] */ - public static getSuggestionsFor( + public getSuggestionsFor( type: string, tags: { key: string; value: string }[], country: string = undefined, - location: [number, number] = undefined + location: [number, number] = undefined, ): NSIItem[] { return tags.flatMap((tag) => - this.getSuggestionsForKV(type, tag.key, tag.value, country, location) + this.getSuggestionsForKV(type, tag.key, tag.value, country, location), ) } @@ -274,15 +300,15 @@ export default class NameSuggestionIndex { * @param country: a string containing one or more country codes, separated by ";" * @param location: center point of the feature, should be [lon, lat] */ - public static getSuggestionsForKV( + public getSuggestionsForKV( type: string, key: string, value: string, country: string = undefined, - location: [number, number] = undefined + location: [number, number] = undefined, ): NSIItem[] { const path = `${type}s/${key}/${value}` - const entry = NameSuggestionIndex.nsiFile.nsi[path] + const entry = this.nsiFile.nsi[path] const countries = country?.split(";") ?? [] return entry?.items?.filter((i) => { if (i.locationSet.include.indexOf("001") >= 0) { @@ -335,4 +361,11 @@ export default class NameSuggestionIndex { return false }) } + + public static async generateMappings(key: string, tags: Exclude, undefined | null>, country: string[], center: [number, number], options: { + sortByFrequency: boolean + }): Promise { + const nsi = await NameSuggestionIndex.getNsiIndex() + return nsi.generateMappings(key, tags, country, center, options) + } } diff --git a/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts b/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts index c37f2a4b7..eabb1e5a9 100644 --- a/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts +++ b/src/Models/ThemeConfig/Conversion/MiscTagRenderingChecks.ts @@ -1,17 +1,13 @@ import { DesugaringStep } from "./Conversion" import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" import { LayerConfigJson } from "../Json/LayerConfigJson" -import { - MappingConfigJson, - QuestionableTagRenderingConfigJson, -} from "../Json/QuestionableTagRenderingConfigJson" +import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" import { ConversionContext } from "./ConversionContext" import { Translation } from "../../../UI/i18n/Translation" -import NameSuggestionIndex from "../../../Logic/Web/NameSuggestionIndex" import { TagUtils } from "../../../Logic/Tags/TagUtils" -import { Tag } from "../../../Logic/Tags/Tag" import Validators from "../../../UI/InputElement/Validators" import { CheckTranslation } from "./Validation" +import NameSuggestionIndex from "../../../Logic/Web/NameSuggestionIndex" export class MiscTagRenderingChecks extends DesugaringStep { private readonly _layerConfig: LayerConfigJson @@ -197,11 +193,11 @@ export class MiscTagRenderingChecks extends DesugaringStep= 0 + this._layerConfig?.source?.["osmTags"] && + NameSuggestionIndex.supportedTypes.indexOf( json.freeform.key) >= 0 ) { - const tags = TagUtils.TagD(this._layerConfig?.source?.osmTags)?.usedTags() - const suggestions = NameSuggestionIndex.getSuggestionsFor(json.freeform.key, tags) + const tags = TagUtils.TagD(this._layerConfig?.source?.["osmTags"])?.usedTags() + /* const suggestions = nameSuggestionIndexBundled.getSuggestionsFor(json.freeform.key, tags) if (suggestions === undefined) { context .enters("freeform", "type") @@ -209,8 +205,8 @@ export class MiscTagRenderingChecks extends DesugaringStep new Tag(t.key, t.value).asHumanString()).join(" ; ") ) - } - } else if (json.freeform.type === "nsi") { + }*/ + } else if (json.freeform["type"] === "nsi") { context .enters("freeform", "type") .warn( diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 1a33ed5d3..77f5f91e2 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -999,7 +999,7 @@ export class TagRenderingConfigUtils { tags: UIEventSource>, feature?: Feature ): Store { - const isNSI = NameSuggestionIndex.supportedTypes().indexOf(config.freeform?.key) >= 0 + const isNSI = NameSuggestionIndex.supportedTypes.indexOf( config.freeform?.key) >= 0 if (!isNSI) { return new ImmutableStore(config) } @@ -1019,8 +1019,8 @@ export class TagRenderingConfigUtils { ) ) }) - return extraMappings.map((extraMappings) => { - if (!extraMappings || extraMappings.length == 0) { + return extraMappings.mapD((extraMappings) => { + if (extraMappings.length == 0) { return config } const clone: TagRenderingConfig = Object.create(config) diff --git a/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte index 97184366f..56168d48e 100644 --- a/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingAnswerDynamic.svelte @@ -7,6 +7,7 @@ import { UIEventSource } from "../../../Logic/UIEventSource" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import TagRenderingAnswer from "./TagRenderingAnswer.svelte" + import Loading from "../../Base/Loading.svelte" export let tags: UIEventSource | undefined> @@ -20,12 +21,16 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - +{#if $dynamicConfig === undefined} + +{:else} + +{/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte index c4fe15c7b..aeeadd204 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditableDynamic.svelte @@ -7,6 +7,7 @@ import type { SpecialVisualizationState } from "../../SpecialVisualization" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import TagRenderingEditable from "./TagRenderingEditable.svelte" + import Loading from "../../Base/Loading.svelte" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -23,14 +24,18 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - +{#if $dynamicConfig} + +{:else} + +{/if} diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte index 15cb4f19b..a0ca38077 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestionDynamic.svelte @@ -18,6 +18,7 @@ import { twJoin } from "tailwind-merge" import Tr from "../../Base/Tr.svelte" import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid" + import Loading from "../../Base/Loading.svelte" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -31,14 +32,18 @@ let dynamicConfig = TagRenderingConfigUtils.withNameSuggestionIndex(config, tags, selectedElement) - - - +{#if $dynamicConfig } + + + +{:else} + +{/if}