diff --git a/Logic/ImageSearcher.ts b/Logic/ImageSearcher.ts index c75593aab..22f6194e8 100644 --- a/Logic/ImageSearcher.ts +++ b/Logic/ImageSearcher.ts @@ -4,6 +4,7 @@ import {UIElement} from "../UI/UIElement"; import {ImgurImage} from "../UI/Image/ImgurImage"; import {ImagesInCategory, Wikidata, Wikimedia} from "./Web/Wikimedia"; import {UIEventSource} from "./UIEventSource"; +import {MapillaryImage} from "../UI/Image/MapillaryImage"; /** * There are multiple way to fetch images for an object @@ -33,50 +34,12 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { this._tags = tags; const self = this; - this._wdItem.addCallback(() => { - // Load the wikidata item, then detect usage on 'commons' - let allWikidataId = self._wdItem.data.split(";"); - for (let wikidataId of allWikidataId) { - // @ts-ignore - if (wikidataId.startsWith("Q")) { - wikidataId = wikidataId.substr(1); - } - Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { - self.AddImage(undefined, wd.image); - Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { - for (const image of images.images) { - // @ts-ignore - if (image.startsWith("File:")) { - self.AddImage(undefined, image); - } - } - }) - }) - } - } - ); + + // By wrapping this in a UIEventSource, we prevent multiple queries of loadWikiData + this._wdItem.addCallback(() => self.loadWikidata()); + this._commons.addCallback(() => self.LoadCommons()); - this._commons.addCallback(() => { - const allCommons: string[] = self._commons.data.split(";"); - for (const commons of allCommons) { - // @ts-ignore - if (commons.startsWith("Category:")) { - Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => { - for (const image of images.images) { - // @ts-ignore - if (image.startsWith("File:")) { - self.AddImage(undefined, image); - } - } - }) - } else { // @ts-ignore - if (commons.startsWith("File:")) { - self.AddImage(undefined, commons); - } - } - } - }); this._tags.addCallbackAndRun(() => self.LoadImages()); } @@ -92,11 +55,54 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { } } - this.data.push({key:key, url:url}); + this.data.push({key: key, url: url}); this.ping(); } - - private LoadImages(): void { + + private loadWikidata() { + // Load the wikidata item, then detect usage on 'commons' + let allWikidataId = this._wdItem.data.split(";"); + for (let wikidataId of allWikidataId) { + // @ts-ignore + if (wikidataId.startsWith("Q")) { + wikidataId = wikidataId.substr(1); + } + Wikimedia.GetWikiData(parseInt(wikidataId), (wd: Wikidata) => { + this.AddImage(undefined, wd.image); + Wikimedia.GetCategoryFiles(wd.commonsWiki, (images: ImagesInCategory) => { + for (const image of images.images) { + // @ts-ignore + if (image.startsWith("File:")) { + this.AddImage(undefined, image); + } + } + }) + }) + } + } + + private LoadCommons() { + const allCommons: string[] = this._commons.data.split(";"); + for (const commons of allCommons) { + // @ts-ignore + if (commons.startsWith("Category:")) { + Wikimedia.GetCategoryFiles(commons, (images: ImagesInCategory) => { + for (const image of images.images) { + // @ts-ignore + if (image.startsWith("File:")) { + this.AddImage(undefined, image); + } + } + }) + } else { // @ts-ignore + if (commons.startsWith("File:")) { + this.AddImage(undefined, commons); + } + } + } + } + + private LoadImages(imagePrefix: string = "image", loadAdditional = true): void { const imageTag = this._tags.data.image; if (imageTag !== undefined) { const bareImages = imageTag.split(";"); @@ -112,13 +118,21 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { } } - const wdItem = this._tags.data.wikidata; - if (wdItem !== undefined) { - this._wdItem.setData(wdItem); - } - const commons = this._tags.data.wikimedia_commons; - if (commons !== undefined) { - this._commons.setData(commons); + if (loadAdditional) { + + const wdItem = this._tags.data.wikidata; + if (wdItem !== undefined) { + this._wdItem.setData(wdItem); + } + const commons = this._tags.data.wikimedia_commons; + if (commons !== undefined) { + this._commons.setData(commons); + } + + if (this._tags.data.mapillary) { + this.AddImage("mapillary", "https://www.mapillary.com/map/im/" + this._tags.data.mapillary) + } + } } @@ -132,11 +146,13 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { // @ts-ignore if (url.startsWith("File:")) { return new WikimediaImage(url); - }else if (url.startsWith("https://commons.wikimedia.org/wiki/")) { + } else if (url.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) { const commons = url.substr("https://commons.wikimedia.org/wiki/".length); return new WikimediaImage(commons); - }else if(url.startsWith("https://i.imgur.com/")){ + } else if (url.toLowerCase().startsWith("https://i.imgur.com/")) { return new ImgurImage(url); + } else if (url.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) { + return new MapillaryImage(url); } else { return new SimpleImageElement(new UIEventSource(url)); } diff --git a/Logic/Web/Mapillary.ts b/Logic/Web/Mapillary.ts new file mode 100644 index 000000000..048e63716 --- /dev/null +++ b/Logic/Web/Mapillary.ts @@ -0,0 +1,26 @@ +import $ from "jquery" +import {LicenseInfo} from "./Wikimedia"; + +export class Mapillary { + + + static getDescriptionOfImage(key: string, + handleDescription: ((license: LicenseInfo) => void)) { + const url = `https://a.mapillary.com/v3/images/${key}?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2` + + const settings = { + async: true, + type: 'GET', + url: url + }; + $.getJSON(url, function(data) { + const license = new LicenseInfo(); + license.artist = data.properties?.username; + license.licenseShortName = "CC BY-SA 4.0"; + license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; + license.attributionRequired = true; + handleDescription(license); + }) + + } +} \ No newline at end of file diff --git a/UI/Image/ImgurImage.ts b/UI/Image/ImgurImage.ts index adabd2e5f..70cfb7f50 100644 --- a/UI/Image/ImgurImage.ts +++ b/UI/Image/ImgurImage.ts @@ -10,7 +10,7 @@ export class ImgurImage extends UIElement { /*** * Dictionary from url to alreayd known license info */ - static allLicenseInfos: any = {}; + private static allLicenseInfos: any = {}; private readonly _imageMeta: UIEventSource; private readonly _imageLocation: string; @@ -33,7 +33,7 @@ export class ImgurImage extends UIElement { } InnerRender(): string { - const image = ""; + const image = ``; if(this._imageMeta.data === null){ return image; diff --git a/UI/Image/MapillaryImage.ts b/UI/Image/MapillaryImage.ts new file mode 100644 index 000000000..554100c68 --- /dev/null +++ b/UI/Image/MapillaryImage.ts @@ -0,0 +1,64 @@ +import {UIElement} from "../UIElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import {LicenseInfo} from "../../Logic/Web/Wikimedia"; +import {Imgur} from "../../Logic/Web/Imgur"; +import {Mapillary} from "../../Logic/Web/Mapillary"; +import {Img} from "../Img"; +import {FixedUiElement} from "../Base/FixedUiElement"; + + +export class MapillaryImage extends UIElement { + + /*** + * Dictionary from url to alreayd known license info + */ + private static allLicenseInfos: any = {}; + private readonly _imageMeta: UIEventSource; + private readonly _imageLocation: string; + + constructor(source: string) { + super() + + if(source.toLowerCase().startsWith("https://www.mapillary.com/map/im/")){ + source = source.substring("https://www.mapillary.com/map/im/".length); + } + + this._imageLocation = source; + if (MapillaryImage.allLicenseInfos[source] !== undefined) { + this._imageMeta = MapillaryImage.allLicenseInfos[source]; + } else { + this._imageMeta = new UIEventSource(null); + MapillaryImage.allLicenseInfos[source] = this._imageMeta; + const self = this; + Mapillary.getDescriptionOfImage(source, (license) => { + self._imageMeta.setData(license) + }) + } + + this.ListenTo(this._imageMeta); + + } + + InnerRender(): string { + const url = `https://images.mapillary.com/${this._imageLocation}/thumb-640.jpg?client_id=TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2`; + const image = ``; + + if (this._imageMeta === undefined || this._imageMeta.data === null) { + return image; + } + + const attribution = + "" + (this._imageMeta.data.artist ?? "") + "" + " " + (this._imageMeta.data.licenseShortName ?? "") + ""; + + return "
" + + image + + "
" + + + new FixedUiElement(Img.mapillaryLogo).SetStyle("height: 1.5em").Render() + + attribution + + "
" + + "
"; + } + + +} \ No newline at end of file diff --git a/UI/Img.ts b/UI/Img.ts index beb8b57d7..95ce59155 100644 --- a/UI/Img.ts +++ b/UI/Img.ts @@ -37,5 +37,7 @@ export class Img { static openFilterButton: string = ` ` + + static mapillaryLogo = "" } diff --git a/test.ts b/test.ts index 47cf57d32..d1c9f2b8c 100644 --- a/test.ts +++ b/test.ts @@ -1,15 +1,10 @@ //* -import {UIEventSource} from "./Logic/UIEventSource"; -import OpeningHoursVisualization from "./UI/OhVisualization"; +import {MapillaryImage} from "./UI/Image/MapillaryImage"; -const oh = "Tu-Fr 09:00-17:00 'as usual'; mo off 'yyy'; su off 'xxx'" -const tags = new UIEventSource({opening_hours:oh}); -new OpeningHoursVisualization(tags, 'opening_hours').AttachTo('maindiv') +new MapillaryImage("JVUQ9Lxtfef-Yj6-GYdGwQ").AttachTo("maindiv") - -window.setTimeout(() => {tags.data._country = "be"; }, 5000) /*/