Visualize attribution in the attribution panel
This commit is contained in:
parent
a16745d0d1
commit
12d99e0323
10 changed files with 122 additions and 28 deletions
|
@ -452,5 +452,23 @@ export default class LayerConfig {
|
|||
};
|
||||
}
|
||||
|
||||
public ExtractImages(): Set<string> {
|
||||
const parts: Set<string>[] = []
|
||||
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<string>(preset.description?.ExtractImages(false)))
|
||||
}
|
||||
|
||||
const allIcons = new Set<string>();
|
||||
for (const part of parts) {
|
||||
part?.forEach(allIcons.add, allIcons)
|
||||
}
|
||||
|
||||
return allIcons;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -182,4 +182,14 @@ export default class LayoutConfig {
|
|||
custom.splice(0, 0, msg);
|
||||
return custom;
|
||||
}
|
||||
|
||||
public ExtractImages() : Set<string>{
|
||||
const icons = new Set<string>()
|
||||
for (const layer of this.layers) {
|
||||
layer.ExtractImages().forEach(icons.add, icons)
|
||||
}
|
||||
icons.add(this.icon)
|
||||
icons.add(this.socialImage)
|
||||
return icons
|
||||
}
|
||||
}
|
|
@ -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<string> {
|
||||
|
||||
const usedIcons = new Set<string>()
|
||||
this.render?.ExtractImages(isIcon)?.forEach(usedIcons.add, usedIcons)
|
||||
|
||||
for (const mapping of this.mappings ?? []) {
|
||||
mapping.then.ExtractImages(isIcon).forEach(usedIcons.add, usedIcons)
|
||||
}
|
||||
|
||||
return usedIcons;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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<LayoutConfig>) {
|
||||
super([
|
||||
Translations.t.general.attribution.attributionContent,
|
||||
|
@ -19,8 +25,42 @@ export default class AttributionPanel extends Combine {
|
|||
"<br/>",
|
||||
new Attribution(undefined, undefined, State.state.layoutToUse, undefined),
|
||||
"<br/>",
|
||||
"<h4>", Translations.t.general.attribution.iconAttribution.title, "</h4>"
|
||||
|
||||
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([
|
||||
`<img src='${iconPath}' style="width: 50px; height: 50px; margin-right: 0.5em;">`,
|
||||
new Combine([
|
||||
new FixedUiElement(license.authors.join("; ")).SetClass("font-bold"),
|
||||
new Combine([license.license, license.sources.length > 0 ? " - " : "",
|
||||
...license.sources.map(link => `<a href='${link}'>${new URL(link).hostname}</a> `)]).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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@
|
|||
<link rel="icon" href="./assets/svg/add.svg" sizes="any" type="image/svg+xml">
|
||||
<meta property="og:image" content="./assets/SocialImage.png">
|
||||
<meta property="og:title" content="MapComplete - editable, thematic maps with OpenStreetMap">
|
||||
<meta property="og:description" content="MapComplete is a platform to visualize OpenStreetMap on a specific topic and to easily contribute data back to it.">`
|
||||
<meta property="og:description" content="MapComplete is a platform to visualize OpenStreetMap on a specific topic and to easily contribute data back to it.">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="./assets/generated/svg_mapcomplete_logo512.png">
|
||||
<link rel="apple-touch-icon" sizes="384x384" href="./assets/generated/svg_mapcomplete_logo384.png">
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in a new issue