Integrating plantnet-API, add rudimentary UI

This commit is contained in:
pietervdvn 2022-08-17 02:42:59 +02:00
parent 89eecdb38a
commit a8959fc934
4 changed files with 80 additions and 9 deletions

File diff suppressed because one or more lines are too long

View file

@ -354,6 +354,25 @@ export default class Wikidata {
throw "Unknown id type: " + id
}
/**
* Build a SPARQL-query, return the result
*
* @param keys: how variables are named. Every key not ending with 'Label' should appear in at least one statement
* @param statements
* @constructor
*/
public static async Sparql<T>(keys: string[], statements: string[]):Promise< (T & Record<string, {type: string, value: string}>) []> {
const query = "SELECT "+keys.map(k => k.startsWith("?") ? k : "?"+k).join(" ")+"\n" +
"WHERE\n" +
"{\n" +
statements.map(stmt => stmt.endsWith(".") ? stmt : stmt+".").join("\n") +
" SERVICE wikibase:label { bd:serviceParam wikibase:language \"[AUTO_LANGUAGE]\". }\n" +
"}"
const url = wds.sparqlQuery(query)
const result = await Utils.downloadJsonCached(url, 24 * 60 * 60 * 1000)
return result.results.bindings
}
/**
* Loads a wikidata page
* @returns the entity of the given value

View file

@ -1,5 +1,5 @@
import {VariableUiElement} from "../Base/VariableUIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import {Store, UIEventSource} from "../../Logic/UIEventSource";
import Wikidata, {WikidataResponse} from "../../Logic/Web/Wikidata";
import {Translation, TypedTranslation} from "../i18n/Translation";
import {FixedUiElement} from "../Base/FixedUiElement";
@ -57,7 +57,7 @@ export default class WikidataPreviewBox extends VariableUiElement {
}
]
constructor(wikidataId: UIEventSource<string>, options?: {noImages?: boolean}) {
constructor(wikidataId: Store<string>, options?: {noImages?: boolean, whileLoading?: BaseUIElement | string, extraItems?: (BaseUIElement | string)[]}) {
let inited = false;
const wikidata = wikidataId
.stabilized(250)
@ -71,7 +71,7 @@ export default class WikidataPreviewBox extends VariableUiElement {
super(wikidata.map(maybeWikidata => {
if (maybeWikidata === null || !inited) {
return undefined;
return options?.whileLoading;
}
if (maybeWikidata === undefined) {
@ -87,7 +87,7 @@ export default class WikidataPreviewBox extends VariableUiElement {
}
public static WikidataResponsePreview(wikidata: WikidataResponse, options?: {noImages?: boolean}): BaseUIElement {
public static WikidataResponsePreview(wikidata: WikidataResponse, options?: {noImages?: boolean, extraItems?: (BaseUIElement | string)[]}): BaseUIElement {
let link = new Link(
new Combine([
wikidata.id,
@ -100,7 +100,8 @@ export default class WikidataPreviewBox extends VariableUiElement {
[Translation.fromMap(wikidata.labels)?.SetClass("font-bold"),
link]).SetClass("flex justify-between"),
Translation.fromMap(wikidata.descriptions),
WikidataPreviewBox.QuickFacts(wikidata, options)
WikidataPreviewBox.QuickFacts(wikidata, options),
...(options.extraItems ?? [])
]).SetClass("flex flex-col link-underline")
@ -108,8 +109,6 @@ export default class WikidataPreviewBox extends VariableUiElement {
if (wikidata.claims.get("P18")?.size > 0) {
imageUrl = Array.from(wikidata.claims.get("P18"))[0]
}
if (imageUrl && !options?.noImages) {
imageUrl = WikimediaImageProvider.singleton.PrepUrl(imageUrl).url
info = new Combine([new Img(imageUrl).SetStyle("max-width: 5rem; width: unset; height: 4rem").SetClass("rounded-xl mr-2"),

53
test.ts
View file

@ -0,0 +1,53 @@
import PlantNet from "./Logic/Web/PlantNet";
import {UIEventSource} from "./Logic/UIEventSource";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
import List from "./UI/Base/List";
import Combine from "./UI/Base/Combine";
import {FixedUiElement} from "./UI/Base/FixedUiElement";
import Wikidata from "./Logic/Web/Wikidata";
import WikidataPreviewBox from "./UI/Wikipedia/WikidataPreviewBox";
import Loading from "./UI/Base/Loading";
function build(images: UIEventSource<string[]>) {
return new VariableUiElement(images
.bind(images => {
if (images.length === 0) {
return null
}
return new UIEventSource({success: PlantNet.exampleResultPrunus}) /*/ UIEventSource.FromPromiseWithErr(PlantNet.query(images.slice(0,5))); //*/
})
.map(result => {
if (result === undefined) {
return new Loading()
}
if (result === null) {
return "Take images of the tree to automatically detect the tree type"
}
if (result["error"] !== undefined) {
return result["error"]
}
console.log(result)
const success = result["success"]
return new Combine(success.results
.filter(species => species.score >= 0.005)
.map(species => {
const wikidata = UIEventSource.FromPromise(Wikidata.Sparql<{ species }>(["?species", "?speciesLabel"],
["?species wdt:P846 \"" + species.gbif.id + "\""]));
return new WikidataPreviewBox(wikidata.map(wd => wd == undefined ? undefined : wd[0]?.species?.value),
{
whileLoading: new Loading("Loading information about " + species.species.scientificNameWithoutAuthor),
extraItems: [Math.round(species.score * 100) + "% match"]
})
.SetClass("border-2 border-subtle rounded-xl block mb-2")
}
)).SetClass("flex flex-col")
}
))
}
const images = ["https://i.imgur.com/VJp1qG1.jpg"]
build(new UIEventSource(images)).AttachTo("maindiv")