Merge master

This commit is contained in:
Pieter Vander Vennet 2020-10-17 01:37:39 +02:00
commit 3ab3cef249
8 changed files with 170 additions and 129 deletions

View file

@ -4,6 +4,7 @@ import {UIElement} from "../UI/UIElement";
import {ImgurImage} from "../UI/Image/ImgurImage"; import {ImgurImage} from "../UI/Image/ImgurImage";
import {ImagesInCategory, Wikidata, Wikimedia} from "./Web/Wikimedia"; import {ImagesInCategory, Wikidata, Wikimedia} from "./Web/Wikimedia";
import {UIEventSource} from "./UIEventSource"; import {UIEventSource} from "./UIEventSource";
import {MapillaryImage} from "../UI/Image/MapillaryImage";
/** /**
* There are multiple way to fetch images for an object * 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; this._tags = tags;
const self = this; const self = this;
this._wdItem.addCallback(() => {
// Load the wikidata item, then detect usage on 'commons' // By wrapping this in a UIEventSource, we prevent multiple queries of loadWikiData
let allWikidataId = self._wdItem.data.split(";"); this._wdItem.addCallback(() => self.loadWikidata());
for (let wikidataId of allWikidataId) { this._commons.addCallback(() => self.LoadCommons());
// @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);
}
}
})
})
}
}
);
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()); this._tags.addCallbackAndRun(() => self.LoadImages());
} }
@ -96,7 +59,50 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
this.ping(); 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; const imageTag = this._tags.data.image;
if (imageTag !== undefined) { if (imageTag !== undefined) {
const bareImages = imageTag.split(";"); const bareImages = imageTag.split(";");
@ -112,6 +118,8 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
} }
} }
if (loadAdditional) {
const wdItem = this._tags.data.wikidata; const wdItem = this._tags.data.wikidata;
if (wdItem !== undefined) { if (wdItem !== undefined) {
this._wdItem.setData(wdItem); this._wdItem.setData(wdItem);
@ -120,6 +128,12 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> {
if (commons !== undefined) { if (commons !== undefined) {
this._commons.setData(commons); 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 // @ts-ignore
if (url.startsWith("File:")) { if (url.startsWith("File:")) {
return new WikimediaImage(url); 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); const commons = url.substr("https://commons.wikimedia.org/wiki/".length);
return new WikimediaImage(commons); 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); return new ImgurImage(url);
} else if (url.toLowerCase().startsWith("https://www.mapillary.com/map/im/")) {
return new MapillaryImage(url);
} else { } else {
return new SimpleImageElement(new UIEventSource<string>(url)); return new SimpleImageElement(new UIEventSource<string>(url));
} }

26
Logic/Web/Mapillary.ts Normal file
View file

@ -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);
})
}
}

View file

@ -27,7 +27,7 @@ export default class State {
// The user journey states thresholds when a new feature gets unlocked // The user journey states thresholds when a new feature gets unlocked
public static userJourney = { public static userJourney = {
addNewPointsUnlock: 1, addNewPointsUnlock: 0,
moreScreenUnlock: 5, moreScreenUnlock: 5,
personalLayoutUnlock: 20, personalLayoutUnlock: 20,
tagsVisibleAt: 100, tagsVisibleAt: 100,
@ -141,11 +141,11 @@ export default class State {
} }
this.zoom = asFloat( this.zoom = asFloat(
QueryParameters.GetQueryParameter("z", "" + layoutToUse.startzoom) QueryParameters.GetQueryParameter("z", "" + layoutToUse.startzoom)
.syncWith(LocalStorageSource.Get("zoom"), true)); .syncWith(LocalStorageSource.Get("zoom")));
this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) 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) 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 }>({ this.locationControl = new UIEventSource<{ lat: number, lon: number, zoom: number }>({

View file

@ -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<any>) {
}
}
class ImageCarouselWithUpload extends TagDependantUIElement {
private _imageElement: ImageCarousel;
private _pictureUploader: ImageUploadFlow;
constructor(tags: UIEventSource<any>) {
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;
}
}

View file

@ -10,7 +10,7 @@ export class ImgurImage extends UIElement {
/*** /***
* Dictionary from url to alreayd known license info * Dictionary from url to alreayd known license info
*/ */
static allLicenseInfos: any = {}; private static allLicenseInfos: any = {};
private readonly _imageMeta: UIEventSource<LicenseInfo>; private readonly _imageMeta: UIEventSource<LicenseInfo>;
private readonly _imageLocation: string; private readonly _imageLocation: string;
@ -33,7 +33,7 @@ export class ImgurImage extends UIElement {
} }
InnerRender(): string { InnerRender(): string {
const image = "<img src='" + this._imageLocation + "' " + "alt='' >"; const image = `<img src='${this._imageLocation}' alt='' >`;
if(this._imageMeta.data === null){ if(this._imageMeta.data === null){
return image; return image;

View file

@ -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<LicenseInfo>;
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<LicenseInfo>(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 = `<img src='${url}'>`;
if (this._imageMeta === undefined || this._imageMeta.data === null) {
return image;
}
const attribution =
"<span class='attribution-author'><b>" + (this._imageMeta.data.artist ?? "") + "</b></span>" + " <span class='license'>" + (this._imageMeta.data.licenseShortName ?? "") + "</span>";
return "<div class='imgWithAttr'>" +
image +
"<div class='attribution'>" +
new FixedUiElement(Img.mapillaryLogo).SetStyle("height: 1.5em").Render() +
attribution +
"</div>" +
"</div>";
}
}

View file

@ -37,5 +37,7 @@ export class Img {
static openFilterButton: string = `<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> static openFilterButton: string = `<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 2L2 20M20 20L2 2" stroke="#003B8B" stroke-width="4"/> <path d="M20 2L2 20M20 20L2 2" stroke="#003B8B" stroke-width="4"/>
</svg> ` </svg> `
static mapillaryLogo = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"none\" viewBox=\"0 0 32 32\"><path fill=\"#fff\" fill-rule=\"evenodd\" d=\"M32 16c0 8.837-7.163 16-16 16S0 24.837 0 16 7.163 0 16 0s16 7.163 16 16zm-24.44-.974c-.371-.201-.303-.725.166-.859.398-.113 3.627-1.196 4.605-1.524a.59.59 0 00.373-.369l1.57-4.603c.146-.43.678-.432.894-.015.024.046.325.593.731 1.331v.001c.896 1.629 2.302 4.185 2.372 4.34a.473.473 0 01-.194.617c-.133.083-.314.19-.469.28l-.269.16c-.237.148-.464.045-.573-.183-.065-.137-.39-.719-.713-1.299-.217-.389-.433-.776-.57-1.027-.17-.313-.682-.433-.854.072l-.566 1.66a.613.613 0 01-.376.373l-1.703.564c-.336.111-.5.626-.046.843.038.018.383.202.798.423h.001c.631.337 1.425.76 1.552.82.21.1.328.39.204.595-.168.28-.384.635-.462.75a.48.48 0 01-.626.149c-.223-.119-5.711-3.027-5.844-3.099zm7.378 3.9c.288.147 7.276 3.844 7.496 3.963.441.238.907-.222.668-.652-.041-.073-.507-.929-1.107-2.034l-.002-.004c-1.166-2.144-2.84-5.224-2.925-5.365-.128-.214-.442-.322-.678-.178-.232.14-.498.298-.648.374-.3.153-.338.383-.203.639.297.562 1.232 2.267 1.34 2.446.223.374-.276.801-.615.615-.054-.03-.408-.217-.834-.442-.697-.368-1.587-.839-1.684-.896-.157-.09-.435-.09-.626.218-.138.224-.308.502-.386.642-.155.274-.084.527.204.674z\" clip-rule=\"evenodd\" style=\"transition: all 0.2s ease 0s;\"></path></svg>"
} }

10
test.ts
View file

@ -1,15 +1,9 @@
//* //*
import SpecialVisualizations from "./UI/SpecialVisualizations";
import LiveQueryHandler from "./Logic/Web/LiveQueryHandler"; SpecialVisualizations.HelpMessage.AttachTo("maindivgi")
import {VariableUiElement} from "./UI/Base/VariableUIElement";
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')
/*/ /*/