From 12d99e03235a9e0b6c22780ca357367d726d5140 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 9 Apr 2021 02:57:06 +0200 Subject: [PATCH] Visualize attribution in the attribution panel --- Customizations/JSON/LayerConfig.ts | 18 ++++++++++ Customizations/JSON/LayoutConfig.ts | 10 ++++++ Customizations/JSON/TagRenderingConfig.ts | 13 ++++++- README.md | 2 -- UI/BigComponents/AttributionPanel.ts | 44 +++++++++++++++++++++-- UI/i18n/Translation.ts | 24 +++++++++++-- index.html | 2 +- package.json | 2 +- scripts/generateLicenseInfo.ts | 18 ++++------ test/ImageAttribution.spec.ts | 17 ++++----- 10 files changed, 122 insertions(+), 28 deletions(-) diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index ba6bd4f..d304448 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -452,5 +452,23 @@ export default class LayerConfig { }; } + public ExtractImages(): Set { + const parts: Set[] = [] + parts.push(...this.tagRenderings?.map(tr => tr.ExtractImages(false))) + parts.push(...this.titleIcons?.map(tr => tr.ExtractImages(true))) + parts.push(this.icon?.ExtractImages(true)) + parts.push(...this.iconOverlays?.map(overlay => overlay.then.ExtractImages(true))) + for (const preset of this.presets) { + parts.push(new Set(preset.description?.ExtractImages(false))) + } + + const allIcons = new Set(); + for (const part of parts) { + part?.forEach(allIcons.add, allIcons) + } + + return allIcons; + } + } \ No newline at end of file diff --git a/Customizations/JSON/LayoutConfig.ts b/Customizations/JSON/LayoutConfig.ts index 7e587ae..89421e1 100644 --- a/Customizations/JSON/LayoutConfig.ts +++ b/Customizations/JSON/LayoutConfig.ts @@ -182,4 +182,14 @@ export default class LayoutConfig { custom.splice(0, 0, msg); return custom; } + + public ExtractImages() : Set{ + const icons = new Set() + for (const layer of this.layers) { + layer.ExtractImages().forEach(icons.add, icons) + } + icons.add(this.icon) + icons.add(this.socialImage) + return icons + } } \ No newline at end of file diff --git a/Customizations/JSON/TagRenderingConfig.ts b/Customizations/JSON/TagRenderingConfig.ts index c550a30..9dc92f7 100644 --- a/Customizations/JSON/TagRenderingConfig.ts +++ b/Customizations/JSON/TagRenderingConfig.ts @@ -70,7 +70,7 @@ export default class TagRenderingConfig { addExtraTags: json.freeform.addExtraTags?.map((tg, i) => FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [] } - if(json.freeform["extraTags"] !== undefined){ + if (json.freeform["extraTags"] !== undefined) { throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})` } if (this.freeform.key === undefined || this.freeform.key === "") { @@ -254,5 +254,16 @@ export default class TagRenderingConfig { return undefined; } + public ExtractImages(isIcon: boolean): Set { + + const usedIcons = new Set() + this.render?.ExtractImages(isIcon)?.forEach(usedIcons.add, usedIcons) + + for (const mapping of this.mappings ?? []) { + mapping.then.ExtractImages(isIcon).forEach(usedIcons.add, usedIcons) + } + + return usedIcons; + } } \ No newline at end of file diff --git a/README.md b/README.md index 0178562..50ce713 100644 --- a/README.md +++ b/README.md @@ -201,8 +201,6 @@ Data from OpenStreetMap Background layer selection: curated by https://github.com/osmlab/editor-layer-index -Images from Wikipedia/Wikimedia - https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg Camera Icon, Dave Gandy, CC-BY-SA 3.0 diff --git a/UI/BigComponents/AttributionPanel.ts b/UI/BigComponents/AttributionPanel.ts index c8793b5..e909323 100644 --- a/UI/BigComponents/AttributionPanel.ts +++ b/UI/BigComponents/AttributionPanel.ts @@ -6,12 +6,18 @@ import State from "../../State"; import {UIEventSource} from "../../Logic/UIEventSource"; import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; import {FixedUiElement} from "../Base/FixedUiElement"; +import * as licenses from "../../assets/generated/license_info.json" +import SmallLicense from "../../Models/smallLicense"; +import {Icon} from "leaflet"; +import Img from "../Base/Img"; /** * The attribution panel shown on mobile */ export default class AttributionPanel extends Combine { + private static LicenseObject = AttributionPanel.GenerateLicenses(); + constructor(layoutToUse: UIEventSource) { super([ Translations.t.general.attribution.attributionContent, @@ -19,8 +25,42 @@ export default class AttributionPanel extends Combine { "
", new Attribution(undefined, undefined, State.state.layoutToUse, undefined), "
", - "

", Translations.t.general.attribution.iconAttribution.title, "

" - + Translations.t.general.attribution.iconAttribution.title.Clone().SetClass("font-bold pt-12 pb-3"), + ...Array.from(layoutToUse.data.ExtractImages()).map(AttributionPanel.IconAttribution) ]); + this.SetClass("flex flex-col") + } + + private static IconAttribution(iconPath: string) { + console.log("Attribution panel for ", iconPath) + if (iconPath.startsWith("http")) { + iconPath = "." + new URL(iconPath).pathname; + } + + const license: SmallLicense = AttributionPanel.LicenseObject[iconPath] + if (license == undefined) { + return undefined; + } + if(license.license.indexOf("trivial")>=0){ + return undefined; + } + + return new Combine([ + ``, + new Combine([ + new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"), + new Combine([license.license, license.sources.length > 0 ? " - " : "", + ...license.sources.map(link => `${new URL(link).hostname} `)]).SetClass("block") + ]).SetClass("flex flex-col") + ]).SetClass("flex") + } + + private static GenerateLicenses() { + const allLicenses = {} + for (const key in licenses) { + const license: SmallLicense = licenses[key]; + allLicenses[license.path] = license + } + return allLicenses; } } \ No newline at end of file diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index 3808757..c8a8b5a 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -8,6 +8,8 @@ export class Translation extends UIElement { public static forcedLanguage = undefined; public readonly translations: object + return + allIcons; constructor(translations: object, context?: string) { super(Locale.language) @@ -46,7 +48,7 @@ export class Translation extends UIElement { public SupportedLanguages(): string[] { const langs = [] for (const translationsKey in this.translations) { - if(translationsKey === "#"){ + if (translationsKey === "#") { continue; } langs.push(translationsKey) @@ -102,7 +104,6 @@ export class Translation extends UIElement { return new Translation(this.translations) } - FirstSentence() { const tr = {}; @@ -115,4 +116,23 @@ export class Translation extends UIElement { return new Translation(tr); } + + public ExtractImages(isIcon = false): string[] { + const allIcons: string[] = [] + for (const key in this.translations) { + const render = this.translations[key] + + if (isIcon) { + const icons = render.split(";").filter(part => part.match(/(\.svg|\.png|\.jpg)$/) != null) + allIcons.push(...icons) + } else if(!Utils.runningFromConsole){ + // This might be a tagrendering containing some img as html + const htmlElement = document.createElement("div") + htmlElement.innerHTML = render + const images = Array.from(htmlElement.getElementsByTagName("img")).map(img => img.src) + allIcons.push(...images) + } + } + return allIcons.filter(icon => icon != undefined) + } } \ No newline at end of file diff --git a/index.html b/index.html index 8f0253a..c2cd5e8 100644 --- a/index.html +++ b/index.html @@ -27,7 +27,7 @@ - ` + diff --git a/package.json b/package.json index 691c168..2904b96 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", "start": "npm run increase-memory && parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", - "test": "ts-node test/Tag.spec.ts && ts-node test/TagQuestion.spec.ts && ts-node test/ImageSearcher.spec.ts", + "test": "ts-node test/Tag.spec.ts && ts-node test/TagQuestion.spec.ts && ts-node test/ImageSearcher.spec.ts && ts-node test/ImageAttribution.spec.ts", "generate:editor-layer-index": "cd assets/ && wget https://osmlab.github.io/editor-layer-index/imagery.geojson --output-document=editor-layer-index.json", "generate:images": "ts-node scripts/generateIncludedImages.ts", "generate:translations": "ts-node scripts/generateTranslations.ts", diff --git a/scripts/generateLicenseInfo.ts b/scripts/generateLicenseInfo.ts index 8279f18..7f1ff34 100644 --- a/scripts/generateLicenseInfo.ts +++ b/scripts/generateLicenseInfo.ts @@ -1,18 +1,9 @@ import {Utils} from "../Utils"; +import {lstatSync, readdirSync, readFileSync, writeFileSync} from "fs"; +import SmallLicense from "../Models/smallLicense"; Utils.runningFromConsole = true; -import {existsSync, mkdirSync, readdirSync, readFileSync, writeFile, writeFileSync, lstatSync} from "fs"; -import {LicenseInfo} from "../Logic/Web/Wikimedia"; -import {icon, Icon} from "leaflet"; - -interface SmallLicense { - path: string, - authors: string[], - license: string, - sources: string[] -} - /** * Sweeps the entire 'assets/' (except assets/generated) directory for image files and any 'license_info.json'-file. * Checks that the license info is included for each of them and generates a compiles license_info.json for those @@ -179,6 +170,11 @@ console.log(`There are ${missingLicenses.length} licenses missing.`) // shuffle(missingLicenses) +process.on('SIGINT', function() { + console.log("Aborting... Bye!"); + process.exit(); +}); + let i = 1; for (const missingLicens of missingLicenses) { console.log(i + " / " + missingLicenses.length) diff --git a/test/ImageAttribution.spec.ts b/test/ImageAttribution.spec.ts index 69462be..f234579 100644 --- a/test/ImageAttribution.spec.ts +++ b/test/ImageAttribution.spec.ts @@ -16,18 +16,19 @@ import {SubstitutedTranslation} from "../UI/SubstitutedTranslation"; import {Tag} from "../Logic/Tags/Tag"; import {And} from "../Logic/Tags/And"; import {ImageSearcher} from "../Logic/Actors/ImageSearcher"; +import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; +import AllKnownLayers from "../Customizations/AllKnownLayers"; -new T("ImageSearcher", [ +new T("ImageAttribution Tests", [ [ - "Should find images", + "Should find all the images", () => { - const tags = new UIEventSource({ - "mapillary": "https://www.mapillary.com/app/?pKey=bYH6FFl8LXAPapz4PNSh3Q" - }); - const searcher = ImageSearcher.construct(tags) - const result = searcher.data[0]; - equal(result.url, "https://www.mapillary.com/map/im/bYH6FFl8LXAPapz4PNSh3Q"); + const pumps = AllKnownLayers.sharedLayers["bike_repair_station"] + const expected = "./assets/layers/bike_repair_station/pump_example_manual.jpg" + const images = pumps.ExtractImages(); + + equal(images.length, 5, "The pump example was not found") } ]