Merge master
This commit is contained in:
commit
3ab3cef249
8 changed files with 170 additions and 129 deletions
|
@ -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
26
Logic/Web/Mapillary.ts
Normal 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);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
8
State.ts
8
State.ts
|
@ -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 }>({
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
64
UI/Image/MapillaryImage.ts
Normal file
64
UI/Image/MapillaryImage.ts
Normal 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>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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
10
test.ts
|
@ -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')
|
|
||||||
|
|
||||||
/*/
|
/*/
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue