diff --git a/Logic/ImageSearcher.ts b/Logic/ImageSearcher.ts index c75593aab2..22f6194e8c 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 0000000000..048e637163 --- /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/State.ts b/State.ts index fcfc4e2cd6..57ae445b73 100644 --- a/State.ts +++ b/State.ts @@ -27,7 +27,7 @@ export default class State { // The user journey states thresholds when a new feature gets unlocked public static userJourney = { - addNewPointsUnlock: 1, + addNewPointsUnlock: 0, moreScreenUnlock: 5, personalLayoutUnlock: 20, tagsVisibleAt: 100, @@ -141,11 +141,11 @@ export default class State { } this.zoom = asFloat( QueryParameters.GetQueryParameter("z", "" + layoutToUse.startzoom) - .syncWith(LocalStorageSource.Get("zoom"), true)); + .syncWith(LocalStorageSource.Get("zoom"))); this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) - .syncWith(LocalStorageSource.Get("lat"), true)); + .syncWith(LocalStorageSource.Get("lat"))); this.lon = asFloat(QueryParameters.GetQueryParameter("lon", "" + layoutToUse.startLon) - .syncWith(LocalStorageSource.Get("lon"), true)); + .syncWith(LocalStorageSource.Get("lon"))); this.locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({ diff --git a/UI/Image/ImageCarouselWithUpload.ts b/UI/Image/ImageCarouselWithUpload.ts deleted file mode 100644 index 89f40ae757..0000000000 --- a/UI/Image/ImageCarouselWithUpload.ts +++ /dev/null @@ -1,61 +0,0 @@ -import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; -import {ImageCarousel} from "./ImageCarousel"; -import {ImageUploadFlow} from "./ImageUploadFlow"; -import Translation from "../i18n/Translation"; -import {UIEventSource} from "../../Logic/UIEventSource"; - -export default class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ - - IsKnown(properties: any): boolean { - return true; - } - - IsQuestioning(properties: any): boolean { - return false; - } - - construct(dependencies): TagDependantUIElement { - return new ImageCarouselWithUpload(dependencies); - } - - GetContent(tags: any): Translation { - return new Translation({"*": "Image carousel with uploader"}); - } -} - -class OsmImageUploadHandler { - constructor(tags: UIEventSource) { - - } - -} - -class ImageCarouselWithUpload extends TagDependantUIElement { - private _imageElement: ImageCarousel; - private _pictureUploader: ImageUploadFlow; - - constructor(tags: UIEventSource) { - super(tags); - this._imageElement = new ImageCarousel(tags); - this._pictureUploader = new OsmImageUploadHandler(tags).getUI(); - - } - - InnerRender(): string { - return this._imageElement.Render() + - this._pictureUploader.Render(); - } - - IsKnown(): boolean { - return true; - } - - IsQuestioning(): boolean { - return false; - } - - IsSkipped(): boolean { - return false; - } - -} \ No newline at end of file diff --git a/UI/Image/ImgurImage.ts b/UI/Image/ImgurImage.ts index adabd2e5f9..70cfb7f50b 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 0000000000..554100c684 --- /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 beb8b57d78..95ce591551 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 4a50264ec7..f9976837d8 100644 --- a/test.ts +++ b/test.ts @@ -1,15 +1,9 @@ //* +import SpecialVisualizations from "./UI/SpecialVisualizations"; -import LiveQueryHandler from "./Logic/Web/LiveQueryHandler"; -import {VariableUiElement} from "./UI/Base/VariableUIElement"; +SpecialVisualizations.HelpMessage.AttachTo("maindivgi") -const source = LiveQueryHandler.FetchLiveData("https://data.mobility.brussels/bike/api/counts/?request=live&featureID=CJM90", - "hour:data.hour_cnt;day:data.day_cnt;year:data.year_cnt".split(";")) -source.addCallback((data) => {console.log(data)}) -new VariableUiElement(source.map(data => { - return ["Data is:", data.hour, "last hour;", data.day, "last day; ", data.year, "last year;"].join(" ") -})).AttachTo('maindiv') /*/