From 34799f161da2d7e2f80ffc0bb45eb7af8b4aec2e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 17 Aug 2022 01:30:07 +0200 Subject: [PATCH 1/8] Add plantnet as API --- Logic/Web/PlantNet.ts | 346 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 Logic/Web/PlantNet.ts diff --git a/Logic/Web/PlantNet.ts b/Logic/Web/PlantNet.ts new file mode 100644 index 000000000..11757f317 --- /dev/null +++ b/Logic/Web/PlantNet.ts @@ -0,0 +1,346 @@ +import {Utils} from "../../Utils"; + +export default class PlantNet { + private static baseUrl = "https://my-api.plantnet.org/v2/identify/all?api-key=2b10AAsjzwzJvucA5Ncm5qxe" + + public static query(imageUrls: string[]) : Promise{ + if (imageUrls.length > 5) { + throw "At most 5 images can be given to PlantNet.query" + } + if (imageUrls.length == 0) { + throw "At least one image should be given to PlantNet.query" + } + let url = PlantNet. baseUrl; + for (const image of imageUrls) { + url += "&images="+encodeURIComponent(image) + } + return Utils.downloadJson(url) + } + + public static exampleResult: PlantNetResult = { + "query": { + "project": "all", + "images": ["https://my.plantnet.org/images/image_1.jpeg", "https://my.plantnet.org/images/image_2.jpeg"], + "organs": ["flower", "leaf"], + "includeRelatedImages": false + }, + "language": "en", + "preferedReferential": "the-plant-list", + "bestMatch": "Hibiscus rosa-sinensis L.", + "results": [{ + "score": 0.91806, + "species": { + "scientificNameWithoutAuthor": "Hibiscus rosa-sinensis", + "scientificNameAuthorship": "L.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Hawaiian hibiscus", "Hibiscus", "Chinese hibiscus"], + "scientificName": "Hibiscus rosa-sinensis L." + }, + "gbif": {"id": "3152559"} + }, { + "score": 0.00759, + "species": { + "scientificNameWithoutAuthor": "Hibiscus moscheutos", + "scientificNameAuthorship": "L.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Crimsoneyed rosemallow", "Mallow-rose", "Swamp rose-mallow"], + "scientificName": "Hibiscus moscheutos L." + }, + "gbif": {"id": "3152596"} + }, { + "score": 0.00676, + "species": { + "scientificNameWithoutAuthor": "Hibiscus schizopetalus", + "scientificNameAuthorship": "(Dyer) Hook.f.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Campanilla", "Chinese lantern", "Fringed rosemallow"], + "scientificName": "Hibiscus schizopetalus (Dyer) Hook.f." + }, + "gbif": {"id": "9064581"} + }, { + "score": 0.00544, + "species": { + "scientificNameWithoutAuthor": "Hibiscus palustris", + "scientificNameAuthorship": "L.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Swamp Rose Mallow", "Hardy Hidiscus", "Twisted Hibiscus"], + "scientificName": "Hibiscus palustris L." + }, + "gbif": {"id": "6377046"} + }, { + "score": 0.0047, + "species": { + "scientificNameWithoutAuthor": "Hibiscus sabdariffa", + "scientificNameAuthorship": "L.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Indian-sorrel", "Roselle", "Jamaica-sorrel"], + "scientificName": "Hibiscus sabdariffa L." + }, + "gbif": {"id": "3152582"} + }, { + "score": 0.0037, + "species": { + "scientificNameWithoutAuthor": "Abelmoschus moschatus", + "scientificNameAuthorship": "Medik.", + "genus": { + "scientificNameWithoutAuthor": "Abelmoschus", + "scientificNameAuthorship": "", + "scientificName": "Abelmoschus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Musk okra", "Musk-mallow", "Tropical jewel-hibiscus"], + "scientificName": "Abelmoschus moschatus Medik." + }, + "gbif": {"id": "8312665"} + }, { + "score": 0.00278, + "species": { + "scientificNameWithoutAuthor": "Hibiscus grandiflorus", + "scientificNameAuthorship": "Michx.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Swamp rosemallow", "Swamp Rose-Mallow"], + "scientificName": "Hibiscus grandiflorus Michx." + }, + "gbif": {"id": "3152592"} + }, { + "score": 0.00265, + "species": { + "scientificNameWithoutAuthor": "Hibiscus acetosella", + "scientificNameAuthorship": "Welw. ex Hiern", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["False roselle", "Red-leaf hibiscus", "African rosemallow"], + "scientificName": "Hibiscus acetosella Welw. ex Hiern" + }, + "gbif": {"id": "3152551"} + }, { + "score": 0.00253, + "species": { + "scientificNameWithoutAuthor": "Bixa orellana", + "scientificNameAuthorship": "L.", + "genus": { + "scientificNameWithoutAuthor": "Bixa", + "scientificNameAuthorship": "", + "scientificName": "Bixa" + }, + "family": { + "scientificNameWithoutAuthor": "Bixaceae", + "scientificNameAuthorship": "", + "scientificName": "Bixaceae" + }, + "commonNames": ["Arnatto", "Lipsticktree", "Annatto"], + "scientificName": "Bixa orellana L." + }, + "gbif": {"id": "2874863"} + }, { + "score": 0.00179, + "species": { + "scientificNameWithoutAuthor": "Malvaviscus penduliflorus", + "scientificNameAuthorship": "Moc. & Sessé ex DC.", + "genus": { + "scientificNameWithoutAuthor": "Malvaviscus", + "scientificNameAuthorship": "", + "scientificName": "Malvaviscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Mazapan"], + "scientificName": "Malvaviscus penduliflorus Moc. & Sessé ex DC." + }, + "gbif": {"id": "3152776"} + }, { + "score": 0.00145, + "species": { + "scientificNameWithoutAuthor": "Hibiscus diversifolius", + "scientificNameAuthorship": "Jacq.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Cape hibiscus", "Swamp hibiscus", "Comfortroot"], + "scientificName": "Hibiscus diversifolius Jacq." + }, + "gbif": {"id": "7279239"} + }, { + "score": 0.00141, + "species": { + "scientificNameWithoutAuthor": "Hippeastrum reginae", + "scientificNameAuthorship": "(L.) Herb.", + "genus": { + "scientificNameWithoutAuthor": "Hippeastrum", + "scientificNameAuthorship": "", + "scientificName": "Hippeastrum" + }, + "family": { + "scientificNameWithoutAuthor": "Amaryllidaceae", + "scientificNameAuthorship": "", + "scientificName": "Amaryllidaceae" + }, + "commonNames": ["Amaryllis", "Cheryl's Treasure", "Easter lily"], + "scientificName": "Hippeastrum reginae (L.) Herb." + }, + "gbif": {"id": "2854474"} + }, { + "score": 0.00114, + "species": { + "scientificNameWithoutAuthor": "Hibiscus martianus", + "scientificNameAuthorship": "Zucc.", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["Heartleaf rosemallow", "Mountain rosemallow", "Heartleaf rose-mallow"], + "scientificName": "Hibiscus martianus Zucc." + }, + "gbif": {"id": "3152578"} + }, { + "score": 0.00109, + "species": { + "scientificNameWithoutAuthor": "Acalypha hispida", + "scientificNameAuthorship": "Burm.f.", + "genus": { + "scientificNameWithoutAuthor": "Acalypha", + "scientificNameAuthorship": "", + "scientificName": "Acalypha" + }, + "family": { + "scientificNameWithoutAuthor": "Euphorbiaceae", + "scientificNameAuthorship": "", + "scientificName": "Euphorbiaceae" + }, + "commonNames": ["Philippine-medusa", "Bristly copperleaf", "Chenilleplant"], + "scientificName": "Acalypha hispida Burm.f." + }, + "gbif": {"id": "3056375"} + }, { + "score": 0.00071, + "species": { + "scientificNameWithoutAuthor": "Hibiscus arnottianus", + "scientificNameAuthorship": "A. Gray", + "genus": { + "scientificNameWithoutAuthor": "Hibiscus", + "scientificNameAuthorship": "", + "scientificName": "Hibiscus" + }, + "family": { + "scientificNameWithoutAuthor": "Malvaceae", + "scientificNameAuthorship": "", + "scientificName": "Malvaceae" + }, + "commonNames": ["White rosemallow", "Native Hawaiian White Hibiscus", "Native White Rose-Mallow"], + "scientificName": "Hibiscus arnottianus A. Gray" + }, + "gbif": {"id": "3152543"} + }], + "version": "2022-06-14 (6.0)", + "remainingIdentificationRequests": 499 + } + +} + +export interface PlantNetResult { + "query": { + "project": string, "images": string[], + "organs": string[], + "includeRelatedImages": boolean + }, + "language": string, + "preferedReferential": string, + "bestMatch": string, + "results": { + "score": number, + "gbif": { "id": string /*Actually a number*/ } + "species": + { + "scientificNameWithoutAuthor": string, + "scientificNameAuthorship": string, + "genus": { "scientificNameWithoutAuthor": string, scientificNameAuthorship: string, "scientificName": string }, + "family": { "scientificNameWithoutAuthor": string, scientificNameAuthorship: string, "scientificName": string }, + "commonNames": string [], + "scientificName": string + }, + }[], + "version": string, + "remainingIdentificationRequests": number +} From 89eecdb38af1e3ba22c832d0d8053d1ec3d0490d Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 17 Aug 2022 02:42:16 +0200 Subject: [PATCH 2/8] Reorder questions order; add condition to not ask for properties of the tree that can be read from wikidata --- assets/layers/tree_node/tree_node.json | 230 +++++++++++++------------ 1 file changed, 119 insertions(+), 111 deletions(-) diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index 3d020aac5..dc0074d17 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -105,80 +105,44 @@ ] }, { - "id": "tree-leaf_type", + "id": "tree-species-wikidata", "question": { - "nl": "Is dit een naald- of loofboom?", - "en": "Is this a broadleaved or needleleaved tree?", - "it": "Si tratta di un albero latifoglia o aghifoglia?", - "fr": "Cet arbre est-il un feuillu ou un résineux ?", - "de": "Ist dies ein Laub- oder Nadelbaum?", - "es": "¿Es un árbol de hoja ancha o de hoja aguja?" + "en": "What species is this tree?", + "de": "Um welche Baumart handelt es sich?", + "id": "Spesies pohon apa ini?", + "es": "¿De qué especie es este árbol?", + "nl": "Wat is de boomsoort?", + "fr": "Quelle est l'espèce de cet arbre ?" }, - "mappings": [ - { - "if": { - "and": [ - "leaf_type=broadleaved" + "render": { + "*": "{wikipedia(species:wikidata):max-height: 25rem}" + }, + "freeform": { + "key": "species:wikidata", + "type": "wikidata", + "helperArgs": [ + "species", + { + "instanceOf": [ + 10884, + 16521 ] - }, - "then": { - "nl": "Loofboom", - "en": "Broadleaved", - "it": "Latifoglia", - "fr": "Feuillu", - "de": "Laubbaum", - "ca": "De fulla ampla", - "es": "Latifoliada", - "id": "Berdaun lebar" - }, - "icon": { - "path": "./assets/layers/tree_node/broadleaved.svg", - "class": "small" } - }, - { - "if": { - "and": [ - "leaf_type=needleleaved" - ] - }, - "then": { - "nl": "Naaldboom", - "en": "Needleleaved", - "it": "Aghifoglia", - "fr": "Résineux", - "de": "Nadelbaum", - "ca": "Amb fulles d'agulla", - "es": "Hoja aguja", - "id": "Berdaun jarum" - }, - "icon": { - "path": "./assets/layers/tree_node/needleleaved.svg", - "class": "small" - } - }, - { - "if": { - "and": [ - "leaf_type=leafless" - ] - }, - "then": { - "nl": "Permanent bladloos", - "en": "Permanently leafless", - "it": "Privo di foglie (permanente)", - "fr": "Sans feuilles (Permanent)", - "de": "Dauerhaft blattlos", - "es": "Permanentemente sin hojas", - "id": "Tanpa daun permanen" - }, - "hideInAnswer": true, - "icon": { - "path": "./assets/layers/tree_node/leafless.svg", - "class": "small" - } - } - ] + ] + } + }, + { + "id": "tree-wikipedia", + "#": "If this tree has a wikipedia article, show it. People can _only_ set the species though!", + "render": { + "*": "{wikipedia()}" + }, + "condition": { + "or": [ + "wikipedia~*", + "wikidata~*" + ] + } }, { "id": "tree-denotation", @@ -313,6 +277,88 @@ } ] }, + { + "id": "tree-leaf_type", + "question": { + "nl": "Is dit een naald- of loofboom?", + "en": "Is this a broadleaved or needleleaved tree?", + "it": "Si tratta di un albero latifoglia o aghifoglia?", + "fr": "Cet arbre est-il un feuillu ou un résineux ?", + "de": "Ist dies ein Laub- oder Nadelbaum?", + "es": "¿Es un árbol de hoja ancha o de hoja aguja?" + }, + "mappings": [ + { + "if": { + "and": [ + "leaf_type=broadleaved" + ] + }, + "then": { + "nl": "Loofboom", + "en": "Broadleaved", + "it": "Latifoglia", + "fr": "Feuillu", + "de": "Laubbaum", + "ca": "De fulla ampla", + "es": "Latifoliada", + "id": "Berdaun lebar" + }, + "icon": { + "path": "./assets/layers/tree_node/broadleaved.svg", + "class": "small" + } + }, + { + "if": { + "and": [ + "leaf_type=needleleaved" + ] + }, + "then": { + "nl": "Naaldboom", + "en": "Needleleaved", + "it": "Aghifoglia", + "fr": "Résineux", + "de": "Nadelbaum", + "ca": "Amb fulles d'agulla", + "es": "Hoja aguja", + "id": "Berdaun jarum" + }, + "icon": { + "path": "./assets/layers/tree_node/needleleaved.svg", + "class": "small" + } + }, + { + "if": { + "and": [ + "leaf_type=leafless" + ] + }, + "then": { + "nl": "Permanent bladloos", + "en": "Permanently leafless", + "it": "Privo di foglie (permanente)", + "fr": "Sans feuilles (Permanent)", + "de": "Dauerhaft blattlos", + "es": "Permanentemente sin hojas", + "id": "Tanpa daun permanen" + }, + "hideInAnswer": true, + "icon": { + "path": "./assets/layers/tree_node/leafless.svg", + "class": "small" + } + } + ], + "condition": { + "#": "If the wikidata entry is known, no need to ask for redundant data such as leaf type", + "and": [ + "species:wikidata=" + ] + } + }, { "id": "tree-decidouous", "question": { @@ -360,48 +406,10 @@ } ], "condition": { + "#": "If the wikidata entry is known, no need to ask for redundant data such as leaf type", "and": [ - "leaf_type!~^leafless$" - ] - } - }, - { - "id": "tree-species-wikidata", - "question": { - "en": "What species is this tree?", - "de": "Um welche Baumart handelt es sich?", - "id": "Spesies pohon apa ini?", - "es": "¿De qué especie es este árbol?", - "nl": "Wat is de boomsoort?", - "fr": "Quelle est l'espèce de cet arbre ?" - }, - "render": { - "*": "{wikipedia(species:wikidata):max-height: 25rem}" - }, - "freeform": { - "key": "species:wikidata", - "type": "wikidata", - "helperArgs": [ - "species", - { - "instanceOf": [ - 10884, - 16521 - ] - } - ] - } - }, - { - "id": "tree-wikipedia", - "#": "If this tree has a wikipedia article, show it. People can _only_ set the species though!", - "render": { - "*": "{wikipedia()}" - }, - "condition": { - "or": [ - "wikipedia~*", - "wikidata~*" + "leaf_type!~^leafless$", + "species:wikidata=" ] } }, From a8959fc9344f7d1479a2b86974c77f4339c72188 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 17 Aug 2022 02:42:59 +0200 Subject: [PATCH 3/8] Integrating plantnet-API, add rudimentary UI --- Logic/Web/PlantNet.ts | 4 +-- Logic/Web/Wikidata.ts | 19 +++++++++++ UI/Wikipedia/WikidataPreviewBox.ts | 13 ++++---- test.ts | 53 ++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/Logic/Web/PlantNet.ts b/Logic/Web/PlantNet.ts index 11757f317..d980cf403 100644 --- a/Logic/Web/PlantNet.ts +++ b/Logic/Web/PlantNet.ts @@ -14,7 +14,7 @@ export default class PlantNet { for (const image of imageUrls) { url += "&images="+encodeURIComponent(image) } - return Utils.downloadJson(url) + return Utils.downloadJsonCached(url, 365*24*60*60*1000) } public static exampleResult: PlantNetResult = { @@ -316,7 +316,7 @@ export default class PlantNet { "version": "2022-06-14 (6.0)", "remainingIdentificationRequests": 499 } - +public static exampleResultPrunus : PlantNetResult = {"query":{"project":"all","images":["https://i.imgur.com/VJp1qG1.jpg"],"organs":["auto"],"includeRelatedImages":false},"language":"en","preferedReferential":"the-plant-list","bestMatch":"Malus halliana Koehne","results":[{"score":0.23548,"species":{"scientificNameWithoutAuthor":"Malus halliana","scientificNameAuthorship":"Koehne","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Hall crab apple","Adirondack Crabapple","Hall's crabapple"],"scientificName":"Malus halliana Koehne"},"gbif":{"id":"3001220"}},{"score":0.1514,"species":{"scientificNameWithoutAuthor":"Prunus campanulata","scientificNameAuthorship":"Maxim.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Formosan cherry","Bellflower cherry","Taiwan cherry"],"scientificName":"Prunus campanulata Maxim."},"gbif":{"id":"3021408"}},{"score":0.14758,"species":{"scientificNameWithoutAuthor":"Malus coronaria","scientificNameAuthorship":"(L.) Mill.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Sweet crab apple","American crabapple","Fragrant crabapple"],"scientificName":"Malus coronaria (L.) Mill."},"gbif":{"id":"3001166"}},{"score":0.13092,"species":{"scientificNameWithoutAuthor":"Prunus serrulata","scientificNameAuthorship":"Lindl.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Japanese flowering cherry","Japanese cherry","Oriental cherry"],"scientificName":"Prunus serrulata Lindl."},"gbif":{"id":"3022609"}},{"score":0.10147,"species":{"scientificNameWithoutAuthor":"Malus floribunda","scientificNameAuthorship":"Siebold ex Van Houtte","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Japanese flowering Crabapple","Japanese crab","Japanese crab apple"],"scientificName":"Malus floribunda Siebold ex Van Houtte"},"gbif":{"id":"3001365"}},{"score":0.05122,"species":{"scientificNameWithoutAuthor":"Prunus sargentii","scientificNameAuthorship":"Rehder","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Sargent's cherry","Northern Japanese hill cherry","Sargent Cherry"],"scientificName":"Prunus sargentii Rehder"},"gbif":{"id":"3020955"}},{"score":0.02576,"species":{"scientificNameWithoutAuthor":"Malus × spectabilis","scientificNameAuthorship":"(Sol.) Borkh.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Asiatic apple","Chinese crab","Chinese flowering apple"],"scientificName":"Malus × spectabilis (Sol.) Borkh."},"gbif":{"id":"3001108"}},{"score":0.01802,"species":{"scientificNameWithoutAuthor":"Prunus triloba","scientificNameAuthorship":"Lindl.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Flowering almond","Flowering plum"],"scientificName":"Prunus triloba Lindl."},"gbif":{"id":"3023130"}},{"score":0.01206,"species":{"scientificNameWithoutAuthor":"Prunus japonica","scientificNameAuthorship":"Thunb.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Chinese bush cherry","Japanese bush cherry","Oriental bush cherry"],"scientificName":"Prunus japonica Thunb."},"gbif":{"id":"3020565"}},{"score":0.01161,"species":{"scientificNameWithoutAuthor":"Prunus × yedoensis","scientificNameAuthorship":"Matsum.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Yoshino cherry","Potomac cherry","Tokyo cherry"],"scientificName":"Prunus × yedoensis Matsum."},"gbif":{"id":"3021335"}},{"score":0.00914,"species":{"scientificNameWithoutAuthor":"Prunus mume","scientificNameAuthorship":"(Siebold) Siebold & Zucc.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Japanese apricot","Ume","Chinese Plum"],"scientificName":"Prunus mume (Siebold) Siebold & Zucc."},"gbif":{"id":"3021046"}},{"score":0.0088,"species":{"scientificNameWithoutAuthor":"Malus niedzwetzkyana","scientificNameAuthorship":"Dieck ex Koehne","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Apple","Paradise apple","Kulturapfel"],"scientificName":"Malus niedzwetzkyana Dieck ex Koehne"},"gbif":{"id":"3001327"}},{"score":0.00734,"species":{"scientificNameWithoutAuthor":"Malus hupehensis","scientificNameAuthorship":"(Pamp.) Rehder","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Chinese crab apple","Hupeh crab","Tea crab apple"],"scientificName":"Malus hupehensis (Pamp.) Rehder"},"gbif":{"id":"3001077"}},{"score":0.00688,"species":{"scientificNameWithoutAuthor":"Malus angustifolia","scientificNameAuthorship":"(Aiton) Michx.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Southern crab apple","Narrow-leaved crabapple","Southern crabapple"],"scientificName":"Malus angustifolia (Aiton) Michx."},"gbif":{"id":"3001548"}},{"score":0.00614,"species":{"scientificNameWithoutAuthor":"Prunus subhirtella","scientificNameAuthorship":"Miq.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Rosebud cherry","Spring cherry","Autumn cherry"],"scientificName":"Prunus subhirtella Miq."},"gbif":{"id":"3021229"}},{"score":0.00267,"species":{"scientificNameWithoutAuthor":"Robinia viscosa","scientificNameAuthorship":"Vent.","genus":{"scientificNameWithoutAuthor":"Robinia","scientificNameAuthorship":"","scientificName":"Robinia"},"family":{"scientificNameWithoutAuthor":"Leguminosae","scientificNameAuthorship":"","scientificName":"Leguminosae"},"commonNames":["Clammy locust","Rose acacia","Clammy-bark locust"],"scientificName":"Robinia viscosa Vent."},"gbif":{"id":"5352245"}},{"score":0.0026,"species":{"scientificNameWithoutAuthor":"Handroanthus impetiginosus","scientificNameAuthorship":"(Mart. ex DC.) Mattos","genus":{"scientificNameWithoutAuthor":"Handroanthus","scientificNameAuthorship":"","scientificName":"Handroanthus"},"family":{"scientificNameWithoutAuthor":"Bignoniaceae","scientificNameAuthorship":"","scientificName":"Bignoniaceae"},"commonNames":["Pink trumpet-tree","Taheebo","Pink Trumpet Tree"],"scientificName":"Handroanthus impetiginosus (Mart. ex DC.) Mattos"},"gbif":{"id":"4092242"}},{"score":0.00187,"species":{"scientificNameWithoutAuthor":"Prunus glandulosa","scientificNameAuthorship":"Thunb.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Chinese bush cherry","Dwarf flowering almond","Flowering almond"],"scientificName":"Prunus glandulosa Thunb."},"gbif":{"id":"3022160"}},{"score":0.00162,"species":{"scientificNameWithoutAuthor":"Prunus persica","scientificNameAuthorship":"(L.) Batsch","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Peach","هلو","Peach tree"],"scientificName":"Prunus persica (L.) Batsch"},"gbif":{"id":"3022511"}},{"score":0.00162,"species":{"scientificNameWithoutAuthor":"Prunus cerasifera","scientificNameAuthorship":"Ehrh.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Cherry plum, myrobalan","Cherry plum","Myrobalan plum"],"scientificName":"Prunus cerasifera Ehrh."},"gbif":{"id":"3021730"}},{"score":0.00159,"species":{"scientificNameWithoutAuthor":"Malus prattii","scientificNameAuthorship":"(Hemsl.) C.K.Schneid.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Pratt apple","Pratt's Crab Apple"],"scientificName":"Malus prattii (Hemsl.) C.K.Schneid."},"gbif":{"id":"3001504"}},{"score":0.00159,"species":{"scientificNameWithoutAuthor":"Prunus pedunculata","scientificNameAuthorship":"(Pall.) Maxim.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":[],"scientificName":"Prunus pedunculata (Pall.) Maxim."},"gbif":{"id":"3022277"}},{"score":0.00153,"species":{"scientificNameWithoutAuthor":"Cercis siliquastrum","scientificNameAuthorship":"L.","genus":{"scientificNameWithoutAuthor":"Cercis","scientificNameAuthorship":"","scientificName":"Cercis"},"family":{"scientificNameWithoutAuthor":"Leguminosae","scientificNameAuthorship":"","scientificName":"Leguminosae"},"commonNames":["Judastree","Lovetree","Judas-tree"],"scientificName":"Cercis siliquastrum L."},"gbif":{"id":"5353590"}},{"score":0.00128,"species":{"scientificNameWithoutAuthor":"Malus sylvestris","scientificNameAuthorship":"(L.) Mill.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Crab apple","European crab apple","Lopâr"],"scientificName":"Malus sylvestris (L.) Mill."},"gbif":{"id":"3001509"}},{"score":0.0012,"species":{"scientificNameWithoutAuthor":"Magnolia × soulangeana","scientificNameAuthorship":"Soul.-Bod.","genus":{"scientificNameWithoutAuthor":"Magnolia","scientificNameAuthorship":"","scientificName":"Magnolia"},"family":{"scientificNameWithoutAuthor":"Magnoliaceae","scientificNameAuthorship":"","scientificName":"Magnoliaceae"},"commonNames":["Chinese magnolia","Saucer magnolia"],"scientificName":"Magnolia × soulangeana Soul.-Bod."},"gbif":{"id":"7925303"}},{"score":0.00118,"species":{"scientificNameWithoutAuthor":"Cercis canadensis","scientificNameAuthorship":"L.","genus":{"scientificNameWithoutAuthor":"Cercis","scientificNameAuthorship":"","scientificName":"Cercis"},"family":{"scientificNameWithoutAuthor":"Leguminosae","scientificNameAuthorship":"","scientificName":"Leguminosae"},"commonNames":["Eastern redbud","Judastree","Redbud"],"scientificName":"Cercis canadensis L."},"gbif":{"id":"5353583"}},{"score":0.00114,"species":{"scientificNameWithoutAuthor":"Malus × prunifolia","scientificNameAuthorship":"(Willd.) Borkh.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Plumleaf crab apple","Chinese apple","Crab apple"],"scientificName":"Malus × prunifolia (Willd.) Borkh."},"gbif":{"id":"3001157"}},{"score":0.00111,"species":{"scientificNameWithoutAuthor":"Prunus serrula","scientificNameAuthorship":"Franch.","genus":{"scientificNameWithoutAuthor":"Prunus","scientificNameAuthorship":"","scientificName":"Prunus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Birchbark cherry"],"scientificName":"Prunus serrula Franch."},"gbif":{"id":"3023582"}},{"score":0.00106,"species":{"scientificNameWithoutAuthor":"Malus pumila","scientificNameAuthorship":"Mill.","genus":{"scientificNameWithoutAuthor":"Malus","scientificNameAuthorship":"","scientificName":"Malus"},"family":{"scientificNameWithoutAuthor":"Rosaceae","scientificNameAuthorship":"","scientificName":"Rosaceae"},"commonNames":["Apple","Paradise apple","Kulturapfel"],"scientificName":"Malus pumila Mill."},"gbif":{"id":"3001093"}},{"score":0.00101,"species":{"scientificNameWithoutAuthor":"Viburnum farreri","scientificNameAuthorship":"Stearn","genus":{"scientificNameWithoutAuthor":"Viburnum","scientificNameAuthorship":"","scientificName":"Viburnum"},"family":{"scientificNameWithoutAuthor":"Adoxaceae","scientificNameAuthorship":"","scientificName":"Adoxaceae"},"commonNames":["Fragrant viburnum","Culver's root","Farrer's Viburnum"],"scientificName":"Viburnum farreri Stearn"},"gbif":{"id":"6369599"}}],"version":"2022-06-14 (6.0)","remainingIdentificationRequests":498} } export interface PlantNetResult { diff --git a/Logic/Web/Wikidata.ts b/Logic/Web/Wikidata.ts index 68f7242a3..910b26d0d 100644 --- a/Logic/Web/Wikidata.ts +++ b/Logic/Web/Wikidata.ts @@ -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(keys: string[], statements: string[]):Promise< (T & Record) []> { + 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 diff --git a/UI/Wikipedia/WikidataPreviewBox.ts b/UI/Wikipedia/WikidataPreviewBox.ts index ca997d42d..2a15fcaf4 100644 --- a/UI/Wikipedia/WikidataPreviewBox.ts +++ b/UI/Wikipedia/WikidataPreviewBox.ts @@ -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, options?: {noImages?: boolean}) { + constructor(wikidataId: Store, 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"), diff --git a/test.ts b/test.ts index e69de29bb..2f79181f6 100644 --- a/test.ts +++ b/test.ts @@ -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) { + 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") \ No newline at end of file From 06f8cf70060e06c7f8c06939d66a43d4bfaab1fc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 22 Aug 2022 19:16:37 +0200 Subject: [PATCH 4/8] First working plantnet UI --- Logic/Osm/Actions/ChangeTagAction.ts | 6 +- UI/Base/Button.ts | 6 +- UI/BaseUIElement.ts | 11 +-- UI/BigComponents/PlantNetSpeciesSearch.ts | 103 ++++++++++++++++++++++ UI/SpecialVisualizations.ts | 44 ++++++++- UI/Wikipedia/WikidataPreviewBox.ts | 13 ++- assets/layers/tree_node/tree_node.json | 5 ++ css/index-tailwind-output.css | 56 +++++++----- langs/en.json | 11 +++ 9 files changed, 216 insertions(+), 39 deletions(-) create mode 100644 UI/BigComponents/PlantNetSpeciesSearch.ts diff --git a/Logic/Osm/Actions/ChangeTagAction.ts b/Logic/Osm/Actions/ChangeTagAction.ts index c4a08f34f..199eab28d 100644 --- a/Logic/Osm/Actions/ChangeTagAction.ts +++ b/Logic/Osm/Actions/ChangeTagAction.ts @@ -2,15 +2,17 @@ import OsmChangeAction from "./OsmChangeAction"; import {Changes} from "../Changes"; import {ChangeDescription} from "./ChangeDescription"; import {TagsFilter} from "../../Tags/TagsFilter"; +import {OsmTags} from "../../../Models/OsmFeature"; export default class ChangeTagAction extends OsmChangeAction { private readonly _elementId: string; private readonly _tagsFilter: TagsFilter; - private readonly _currentTags: any; + private readonly _currentTags: Record | OsmTags; private readonly _meta: { theme: string, changeType: string }; constructor(elementId: string, - tagsFilter: TagsFilter, currentTags: any, meta: { + tagsFilter: TagsFilter, + currentTags: Record, meta: { theme: string, changeType: "answer" | "soft-delete" | "add-image" | string }) { diff --git a/UI/Base/Button.ts b/UI/Base/Button.ts index 8d4e0dc1e..aa5fc299e 100644 --- a/UI/Base/Button.ts +++ b/UI/Base/Button.ts @@ -3,12 +3,11 @@ import BaseUIElement from "../BaseUIElement"; export class Button extends BaseUIElement { private _text: BaseUIElement; - private _onclick: () => void; - constructor(text: string | BaseUIElement, onclick: (() => void)) { + constructor(text: string | BaseUIElement, onclick: (() => void | Promise)) { super(); this._text = Translations.W(text); - this._onclick = onclick; + this.onClick(onclick) } protected InnerConstructElement(): HTMLElement { @@ -20,7 +19,6 @@ export class Button extends BaseUIElement { const button = document.createElement("button") button.type = "button" button.appendChild(el) - button.onclick = this._onclick form.appendChild(button) return form; } diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 4c084d088..a9e076560 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -1,5 +1,3 @@ -import { Utils } from "../Utils"; - /** * A thin wrapper around a html element, which allows to generate a HTML-element. * @@ -11,7 +9,7 @@ export default abstract class BaseUIElement { protected isDestroyed = false; private readonly clss: Set = new Set(); private style: string; - private _onClick: () => void; + private _onClick: () => void | Promise; public onClick(f: (() => void)) { this._onClick = f; @@ -127,12 +125,15 @@ export default abstract class BaseUIElement { if (this._onClick !== undefined) { const self = this; - el.onclick = (e) => { + el.onclick = async (e) => { // @ts-ignore if (e.consumed) { return; } - self._onClick(); + const v = self._onClick(); + if(typeof v === "object"){ + await v + } // @ts-ignore e.consumed = true; } diff --git a/UI/BigComponents/PlantNetSpeciesSearch.ts b/UI/BigComponents/PlantNetSpeciesSearch.ts new file mode 100644 index 000000000..f1b2ba434 --- /dev/null +++ b/UI/BigComponents/PlantNetSpeciesSearch.ts @@ -0,0 +1,103 @@ +import {VariableUiElement} from "../Base/VariableUIElement"; +import {Store, UIEventSource} from "../../Logic/UIEventSource"; +import PlantNet from "../../Logic/Web/PlantNet"; +import Loading from "../Base/Loading"; +import Wikidata from "../../Logic/Web/Wikidata"; +import WikidataPreviewBox from "../Wikipedia/WikidataPreviewBox"; +import {Button} from "../Base/Button"; +import Combine from "../Base/Combine"; +import Title from "../Base/Title"; +import WikipediaBox from "../Wikipedia/WikipediaBox"; +import Translations from "../i18n/Translations"; + + +export default class PlantNetSpeciesSearch extends VariableUiElement { + /*** + * Given images, queries plantnet to search a species matching those images. + * A list of species will be presented to the user, after which they can confirm an item. + * The wikidata-url is returned in the callback when the user selects one + */ + constructor(images: Store, onConfirm: (wikidataUrl: string) => Promise) { + const t = Translations.t.plantDetection + super( + 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(t.querying.Subs(images.data)) + } + if (result === null) { + return t.takeImages + } + if (result["error"] !== undefined) { + return t.error.Subs(result).SetClass("alert") + } + console.log(result) + const success = result["success"] + + const selectedSpecies = new UIEventSource(undefined) + const speciesInformation = 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 + "\""])); + + const confirmButton = new Button(t.seeInfo, async() => { + await selectedSpecies.setData(wikidata.data[0].species?.value) + }).SetClass("btn") + + const match = t.matchPercentage.Subs({match: Math.round(species.score * 100)}).SetClass("font-bold") + + const extraItems = new Combine([match, confirmButton]).SetClass("flex flex-col") + + return new WikidataPreviewBox(wikidata.map(wd => wd == undefined ? undefined : wd[0]?.species?.value), + { + whileLoading: new Loading( + t.loadingWikidata.Subs({species: species.species.scientificNameWithoutAuthor})), + extraItems: [new Combine([extraItems])], + + imageStyle: "max-width: 8rem; width: unset; height: 8rem" + }) + .SetClass("border-2 border-subtle rounded-xl block mb-2") + } + ); + const plantOverview = new Combine([ + new Title(t.overviewTitle), + t.overviewIntro, + t.overviewVerify.SetClass("font-bold"), + ...speciesInformation]).SetClass("flex flex-col") + + + return new VariableUiElement(selectedSpecies.map(wikidataSpecies => { + if (wikidataSpecies === undefined) { + return plantOverview + } + const buttons = new Combine([ + new Button("Confirm", () => { + onConfirm(wikidataSpecies) + }).SetClass("btn"), + new Button("Back to plant overview", () => { + selectedSpecies.setData(undefined) + }).SetClass("btn btn-secondary") + ]).SetClass("flex self-end"); + + return new Combine([ + new WikipediaBox([wikidataSpecies], { + firstParagraphOnly: false, + noImages: false, + addHeader: false + }).SetClass("h-96"), + buttons + ]).SetClass("flex flex-col self-end") + })) + + } + )) + } + +} \ No newline at end of file diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 062563118..3cccea1f0 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -61,6 +61,8 @@ import StatisticsPanel from "./BigComponents/StatisticsPanel"; import {OsmFeature} from "../Models/OsmFeature"; import EditableTagRendering from "./Popup/EditableTagRendering"; import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"; +import {ProvidedImage} from "../Logic/ImageProviders/ImageProvider"; +import PlantNetSpeciesSearch from "./BigComponents/PlantNetSpeciesSearch"; export interface SpecialVisualization { funcName: string, @@ -196,7 +198,7 @@ class NearbyImageVis implements SpecialVisualization { new ChangeTagAction( id, new And(tags), - tagSource, + tagSource.data, { theme: state?.layoutToUse.id, changeType: "link-image" @@ -1299,6 +1301,46 @@ export default class SpecialVisualizations { const [layerId, __] = tagRenderingId.split(".") return [layerId] } + }, + { + funcName: "plantnet_detection", + + docs: "Sends the images linked to the current object to plantnet.org and asks it what plant species is shown on it. The user can then select the correct species; the corresponding wikidata-identifier will then be added to the object (together with `source:species:wikidata=plantnet.org AI`). ", + args: [{ + name: "image_key", + defaultValue: AllImageProviders.defaultKeys.join(","), + doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... Multiple values are allowed if ';'-separated " + }], + constr: (state, tags, args) => { + let imagePrefixes: string[] = undefined; + if (args.length > 0) { + imagePrefixes = [].concat(...args.map(a => a.split(","))); + } + + const detect = new UIEventSource(false) + return new Toggle( + new Lazy(() => { + const allProvidedImages: Store = AllImageProviders.LoadImagesFor(tags, imagePrefixes) + const allImages: Store = allProvidedImages.map(pi => pi.map(pi => pi.url)) + return new PlantNetSpeciesSearch(allImages, async selectedWikidata => { + selectedWikidata = Wikidata.ExtractKey(selectedWikidata) + const change = new ChangeTagAction(tags.data.id, + new And([new Tag("species:wikidata", selectedWikidata), + new Tag("source:species:wikidata","PlantNet.org AI") + ]), + tags.data, + { + theme: state.layoutToUse.id, + changeType: "plantnet-ai-detection" + } + ) + await state.changes.applyAction(change) + }) + }), + new SubtleButton(undefined, "Detect plant species with plantnet.org").onClick(() => detect.setData(true)), + detect + ) + } } ] diff --git a/UI/Wikipedia/WikidataPreviewBox.ts b/UI/Wikipedia/WikidataPreviewBox.ts index 2a15fcaf4..2af1f230d 100644 --- a/UI/Wikipedia/WikidataPreviewBox.ts +++ b/UI/Wikipedia/WikidataPreviewBox.ts @@ -57,7 +57,11 @@ export default class WikidataPreviewBox extends VariableUiElement { } ] - constructor(wikidataId: Store, options?: {noImages?: boolean, whileLoading?: BaseUIElement | string, extraItems?: (BaseUIElement | string)[]}) { + constructor(wikidataId: Store, options?: { + noImages?: boolean, + imageStyle?: string, + whileLoading?: BaseUIElement | string, + extraItems?: (BaseUIElement | string)[]}) { let inited = false; const wikidata = wikidataId .stabilized(250) @@ -87,7 +91,10 @@ export default class WikidataPreviewBox extends VariableUiElement { } - public static WikidataResponsePreview(wikidata: WikidataResponse, options?: {noImages?: boolean, extraItems?: (BaseUIElement | string)[]}): BaseUIElement { + public static WikidataResponsePreview(wikidata: WikidataResponse, options?: { + noImages?: boolean, + imageStyle?: string, + extraItems?: (BaseUIElement | string)[]}): BaseUIElement { let link = new Link( new Combine([ wikidata.id, @@ -111,7 +118,7 @@ export default class WikidataPreviewBox extends VariableUiElement { } 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"), + info = new Combine([new Img(imageUrl).SetStyle(options?.imageStyle ?? "max-width: 5rem; width: unset; height: 4rem").SetClass("rounded-xl mr-2"), info.SetClass("w-full")]).SetClass("flex") } diff --git a/assets/layers/tree_node/tree_node.json b/assets/layers/tree_node/tree_node.json index dc0074d17..5ad4d9124 100644 --- a/assets/layers/tree_node/tree_node.json +++ b/assets/layers/tree_node/tree_node.json @@ -104,6 +104,11 @@ } ] }, + { + "id": "plantnet", + "render": "{plantnet_detection()}", + "condition": "species:wikidata=" + }, { "id": "tree-species-wikidata", "question": { diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 5b3eb4abf..4983438de 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -858,6 +858,10 @@ video { margin-bottom: 0.75rem; } +.mb-2 { + margin-bottom: 0.5rem; +} + .ml-3 { margin-left: 0.75rem; } @@ -866,14 +870,6 @@ video { margin-bottom: 1rem; } -.mt-8 { - margin-top: 2rem; -} - -.mt-4 { - margin-top: 1rem; -} - .mt-2 { margin-top: 0.5rem; } @@ -886,6 +882,10 @@ video { margin-right: 2rem; } +.mt-4 { + margin-top: 1rem; +} + .mt-6 { margin-top: 1.5rem; } @@ -910,10 +910,6 @@ video { margin-right: 1rem; } -.mb-2 { - margin-bottom: 0.5rem; -} - .ml-2 { margin-left: 0.5rem; } @@ -934,6 +930,10 @@ video { margin-top: 0px; } +.mt-8 { + margin-top: 2rem; +} + .mb-8 { margin-bottom: 2rem; } @@ -1054,6 +1054,10 @@ video { height: 6rem; } +.h-96 { + height: 24rem; +} + .h-64 { height: 16rem; } @@ -1162,6 +1166,10 @@ video { width: 2rem; } +.w-1\/3 { + width: 33.333333%; +} + .w-4 { width: 1rem; } @@ -1407,6 +1415,10 @@ video { border-radius: 9999px; } +.rounded-xl { + border-radius: 0.75rem; +} + .rounded-3xl { border-radius: 1.5rem; } @@ -1423,10 +1435,6 @@ video { border-radius: 0.5rem; } -.rounded-xl { - border-radius: 0.75rem; -} - .rounded-sm { border-radius: 0.125rem; } @@ -1436,14 +1444,14 @@ video { border-bottom-left-radius: 0.25rem; } -.border { - border-width: 1px; -} - .border-2 { border-width: 2px; } +.border { + border-width: 1px; +} + .border-4 { border-width: 4px; } @@ -2866,10 +2874,6 @@ input { width: 75%; } - .lg\:w-1\/3 { - width: 33.333333%; - } - .lg\:w-1\/4 { width: 25%; } @@ -2878,6 +2882,10 @@ input { width: 16.666667%; } + .lg\:w-1\/3 { + width: 33.333333%; + } + .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } diff --git a/langs/en.json b/langs/en.json index f359f52a9..88b694808 100644 --- a/langs/en.json +++ b/langs/en.json @@ -685,6 +685,17 @@ "typeText": "Type some text to add a comment", "warnAnonymous": "You are not logged in. We won't be able to contact you to resolve your issue." }, + "plantDetection": { + "error": "Something went wrong while detecting the tree species: {error}", + "loadingWikidata": "Loading information about {species}", + "matchPercentage": "{match}% match", + "overviewIntro": "The AI on PlantNet.org thinks the images show the species below.", + "overviewTitle": "Automatically detected species", + "overviewVerify": "Please verify that correct species and link it to the tree", + "querying": "Querying plantnet.org with {length} images", + "seeInfo": "See more information about the species", + "takeImages": "Take images of the tree to automatically detect the tree type" + }, "privacy": { "editing": "When you make a change to the map, this change is recorded on OpenStreetMap and is publicly available to anyone. A changeset made with MapComplete includes the following data:
  • The changes you made
  • Your username
  • When this change is made
  • The theme you used while making the change
  • The language of the user interface
  • An indication of how close you were to changed objects. Other mappers can use this information to determine if a change was made based on survey or on remote research
Please refer to the privacy policy on OpenStreetMap.org for detailed information. We'd like to remind you that you can use a fictional name when signing up.", "editingTitle": "When making changes", From b1ea98651592a1cb88416f99a43b6ebddc2fa445 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 22 Aug 2022 22:58:14 +0200 Subject: [PATCH 5/8] More work --- UI/BigComponents/PlantNetSpeciesSearch.ts | 9 +++++---- langs/en.json | 9 ++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/UI/BigComponents/PlantNetSpeciesSearch.ts b/UI/BigComponents/PlantNetSpeciesSearch.ts index f1b2ba434..f5bae3b11 100644 --- a/UI/BigComponents/PlantNetSpeciesSearch.ts +++ b/UI/BigComponents/PlantNetSpeciesSearch.ts @@ -25,15 +25,16 @@ export default class PlantNetSpeciesSearch extends VariableUiElement { if (images.length === 0) { return null } - return new UIEventSource({success: PlantNet.exampleResultPrunus}) /*/ UIEventSource.FromPromiseWithErr(PlantNet.query(images.slice(0,5))); //*/ + return UIEventSource.FromPromiseWithErr(PlantNet.query(images.slice(0,5))); }) .map(result => { + if (result === null) { + return t.takeImages + } if (result === undefined) { return new Loading(t.querying.Subs(images.data)) } - if (result === null) { - return t.takeImages - } + if (result["error"] !== undefined) { return t.error.Subs(result).SetClass("alert") } diff --git a/langs/en.json b/langs/en.json index 88b694808..cac8bdc2c 100644 --- a/langs/en.json +++ b/langs/en.json @@ -694,7 +694,14 @@ "overviewVerify": "Please verify that correct species and link it to the tree", "querying": "Querying plantnet.org with {length} images", "seeInfo": "See more information about the species", - "takeImages": "Take images of the tree to automatically detect the tree type" + "takeImages": "Take images of the tree to automatically detect the tree type", + "howTo":{ + "intro": "For optimal results,", + "li0": "take a picture which show the tree in the center without much background", + "li1": "take a picture which shows a single leaf", + "li2": "take a picture which shows the bark", + "li3": "take a picture of the flowers" + } }, "privacy": { "editing": "When you make a change to the map, this change is recorded on OpenStreetMap and is publicly available to anyone. A changeset made with MapComplete includes the following data:
  • The changes you made
  • Your username
  • When this change is made
  • The theme you used while making the change
  • The language of the user interface
  • An indication of how close you were to changed objects. Other mappers can use this information to determine if a change was made based on survey or on remote research
Please refer to the privacy policy on OpenStreetMap.org for detailed information. We'd like to remind you that you can use a fictional name when signing up.", From d713e00671a479bc2a4b4953feb9af1925e37ab9 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 23 Aug 2022 16:15:53 +0200 Subject: [PATCH 6/8] Add attribution for plantnet, styling tweaks --- UI/BigComponents/PlantNetSpeciesSearch.ts | 31 ++++++++++++++++++----- UI/SpecialVisualizations.ts | 9 ++++++- assets/svg/license_info.json | 10 ++++++++ assets/svg/plantnet_logo.svg | 22 ++++++++++++++++ css/index-tailwind-output.css | 28 ++++++++++---------- langs/en.json | 23 +++++++++-------- 6 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 assets/svg/plantnet_logo.svg diff --git a/UI/BigComponents/PlantNetSpeciesSearch.ts b/UI/BigComponents/PlantNetSpeciesSearch.ts index f5bae3b11..872dcecda 100644 --- a/UI/BigComponents/PlantNetSpeciesSearch.ts +++ b/UI/BigComponents/PlantNetSpeciesSearch.ts @@ -9,6 +9,8 @@ import Combine from "../Base/Combine"; import Title from "../Base/Title"; import WikipediaBox from "../Wikipedia/WikipediaBox"; import Translations from "../i18n/Translations"; +import List from "../Base/List"; +import Svg from "../../Svg"; export default class PlantNetSpeciesSearch extends VariableUiElement { @@ -28,8 +30,15 @@ export default class PlantNetSpeciesSearch extends VariableUiElement { return UIEventSource.FromPromiseWithErr(PlantNet.query(images.slice(0,5))); }) .map(result => { - if (result === null) { - return t.takeImages + if (images.data.length === 0) { + return new Combine([t.takeImages, t.howTo.intro, new List( + [ + t.howTo.li0, + t.howTo.li1, + t.howTo.li2, + t.howTo.li3 + ] + )]).SetClass("flex flex-col") } if (result === undefined) { return new Loading(t.querying.Subs(images.data)) @@ -79,13 +88,21 @@ export default class PlantNetSpeciesSearch extends VariableUiElement { return plantOverview } const buttons = new Combine([ - new Button("Confirm", () => { + new Button( + new Combine([ + Svg.back_svg().SetClass("w-4"), + t.back]).SetClass("flex"), + () => { + selectedSpecies.setData(undefined) + }).SetClass("btn btn-secondary"), + + new Button( + new Combine([Svg.confirm_svg().SetClass("w-4"), t.confirm]).SetClass("flex") + , () => { onConfirm(wikidataSpecies) }).SetClass("btn"), - new Button("Back to plant overview", () => { - selectedSpecies.setData(undefined) - }).SetClass("btn btn-secondary") - ]).SetClass("flex self-end"); + + ]).SetClass("flex justify-between"); return new Combine([ new WikipediaBox([wikidataSpecies], { diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 3cccea1f0..32901dbec 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -1318,7 +1318,7 @@ export default class SpecialVisualizations { } const detect = new UIEventSource(false) - return new Toggle( + const toggle = new Toggle( new Lazy(() => { const allProvidedImages: Store = AllImageProviders.LoadImagesFor(tags, imagePrefixes) const allImages: Store = allProvidedImages.map(pi => pi.map(pi => pi.url)) @@ -1340,6 +1340,13 @@ export default class SpecialVisualizations { new SubtleButton(undefined, "Detect plant species with plantnet.org").onClick(() => detect.setData(true)), detect ) + + return new Combine([ + toggle, + new Combine([Svg.plantnet_logo_svg().SetClass("w-8"), + Translations.t.plantDetection.poweredByPlantnet]) + .SetClass("flex p-2 bg-gray-200 rounded-xl self-end") + ]).SetClass("flex flex-col") } } ] diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 6d1d8401e..7c7cf6049 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -985,6 +985,16 @@ "authors": [], "sources": [] }, + { + "path": "plantnet_logo.svg", + "license": "Logo (fair usage)", + "authors": [ + "https://plantnet.org" + ], + "sources": [ + "Plantnet.org" + ] + }, { "path": "plus.svg", "license": "CC0; trivial", diff --git a/assets/svg/plantnet_logo.svg b/assets/svg/plantnet_logo.svg new file mode 100644 index 000000000..a9966e2f4 --- /dev/null +++ b/assets/svg/plantnet_logo.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 4983438de..a0b27ac2a 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -1054,10 +1054,6 @@ video { height: 6rem; } -.h-96 { - height: 24rem; -} - .h-64 { height: 16rem; } @@ -1102,6 +1098,10 @@ video { height: 8rem; } +.h-96 { + height: 24rem; +} + .h-16 { height: 4rem; } @@ -1166,6 +1166,11 @@ video { width: 2rem; } +.w-min { + width: -webkit-min-content; + width: min-content; +} + .w-1\/3 { width: 33.333333%; } @@ -1186,11 +1191,6 @@ video { width: 2.75rem; } -.w-min { - width: -webkit-min-content; - width: min-content; -} - .w-max { width: -webkit-max-content; width: max-content; @@ -1512,6 +1512,11 @@ video { background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); } +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + .bg-red-400 { --tw-bg-opacity: 1; background-color: rgba(248, 113, 113, var(--tw-bg-opacity)); @@ -1532,11 +1537,6 @@ video { background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); } -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); -} - .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); diff --git a/langs/en.json b/langs/en.json index cac8bdc2c..44f5a6df1 100644 --- a/langs/en.json +++ b/langs/en.json @@ -686,22 +686,25 @@ "warnAnonymous": "You are not logged in. We won't be able to contact you to resolve your issue." }, "plantDetection": { + "back": "Back to species overview", + "confirm": "Select species", "error": "Something went wrong while detecting the tree species: {error}", - "loadingWikidata": "Loading information about {species}", - "matchPercentage": "{match}% match", - "overviewIntro": "The AI on PlantNet.org thinks the images show the species below.", - "overviewTitle": "Automatically detected species", - "overviewVerify": "Please verify that correct species and link it to the tree", - "querying": "Querying plantnet.org with {length} images", - "seeInfo": "See more information about the species", - "takeImages": "Take images of the tree to automatically detect the tree type", - "howTo":{ + "howTo": { "intro": "For optimal results,", "li0": "take a picture which show the tree in the center without much background", "li1": "take a picture which shows a single leaf", "li2": "take a picture which shows the bark", "li3": "take a picture of the flowers" - } + }, + "loadingWikidata": "Loading information about {species}", + "matchPercentage": "{match}% match", + "overviewIntro": "The AI on PlantNet.org thinks the images show the species below.", + "overviewTitle": "Automatically detected species", + "overviewVerify": "Please verify that correct species and link it to the tree", + "poweredByPlantnet": "Powered by plantnet.org", + "querying": "Querying plantnet.org with {length} images", + "seeInfo": "See more information about the species", + "takeImages": "Take images of the tree to automatically detect the tree type" }, "privacy": { "editing": "When you make a change to the map, this change is recorded on OpenStreetMap and is publicly available to anyone. A changeset made with MapComplete includes the following data:
  • The changes you made
  • Your username
  • When this change is made
  • The theme you used while making the change
  • The language of the user interface
  • An indication of how close you were to changed objects. Other mappers can use this information to determine if a change was made based on survey or on remote research
Please refer to the privacy policy on OpenStreetMap.org for detailed information. We'd like to remind you that you can use a fictional name when signing up.", From 03dad399495713c4cf246bfb4cf62e82fd586514 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 24 Aug 2022 01:08:54 +0200 Subject: [PATCH 7/8] Plantnet-detection: finishing touches --- UI/BigComponents/PlantNetSpeciesSearch.ts | 4 ++-- UI/Image/ImageUploadFlow.ts | 7 ++++--- UI/SpecialVisualizations.ts | 2 +- assets/svg/license_info.json | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/UI/BigComponents/PlantNetSpeciesSearch.ts b/UI/BigComponents/PlantNetSpeciesSearch.ts index 872dcecda..70e456385 100644 --- a/UI/BigComponents/PlantNetSpeciesSearch.ts +++ b/UI/BigComponents/PlantNetSpeciesSearch.ts @@ -90,14 +90,14 @@ export default class PlantNetSpeciesSearch extends VariableUiElement { const buttons = new Combine([ new Button( new Combine([ - Svg.back_svg().SetClass("w-4"), + Svg.back_svg().SetClass("w-6 mr-1 bg-white rounded-full p-1"), t.back]).SetClass("flex"), () => { selectedSpecies.setData(undefined) }).SetClass("btn btn-secondary"), new Button( - new Combine([Svg.confirm_svg().SetClass("w-4"), t.confirm]).SetClass("flex") + new Combine([Svg.confirm_svg().SetClass("w-6 mr-1"), t.confirm]).SetClass("flex") , () => { onConfirm(wikidataSpecies) }).SetClass("btn"), diff --git a/UI/Image/ImageUploadFlow.ts b/UI/Image/ImageUploadFlow.ts index 033aecd03..1c4f49001 100644 --- a/UI/Image/ImageUploadFlow.ts +++ b/UI/Image/ImageUploadFlow.ts @@ -130,16 +130,17 @@ export class ImageUploadFlow extends Toggle { return undefined; } if (l == 1) { - return t.uploadingPicture.Clone().SetClass("alert") + return new Loading( t.uploadingPicture).SetClass("alert") } else { - return t.uploadingMultiple.Subs({count: "" + l}).SetClass("alert") + return new Loading(t.uploadingMultiple.Subs({count: "" + l})).SetClass("alert") } })), new VariableUiElement(uploader.failed.map(q => q.length).map(l => { if (l == 0) { return undefined } - return new Loading(t.uploadFailed).SetClass("alert"); + console.log(l) + return t.uploadFailed.SetClass("block alert"); })), new VariableUiElement(uploadedCount.map(l => { if (l == 0) { diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 32901dbec..826c45ab6 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -1343,7 +1343,7 @@ export default class SpecialVisualizations { return new Combine([ toggle, - new Combine([Svg.plantnet_logo_svg().SetClass("w-8"), + new Combine([Svg.plantnet_logo_svg().SetClass("w-10 h-10 p-1 mr-1 bg-white rounded-full"), Translations.t.plantDetection.poweredByPlantnet]) .SetClass("flex p-2 bg-gray-200 rounded-xl self-end") ]).SetClass("flex flex-col") diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 7c7cf6049..f645f3eb1 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -992,7 +992,7 @@ "https://plantnet.org" ], "sources": [ - "Plantnet.org" + "https://plantnet.org" ] }, { From e56423b016b315cd9fc354e419c276b600c4cab5 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 24 Aug 2022 01:09:48 +0200 Subject: [PATCH 8/8] Add css classes --- css/index-tailwind-output.css | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index a0b27ac2a..58e27b474 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -890,6 +890,10 @@ video { margin-top: 1.5rem; } +.mr-1 { + margin-right: 0.25rem; +} + .mr-2 { margin-right: 0.5rem; } @@ -962,10 +966,6 @@ video { margin-top: -3rem; } -.mr-1 { - margin-right: 0.25rem; -} - .mb-0 { margin-bottom: 0px; } @@ -1074,6 +1074,10 @@ video { height: 2rem; } +.h-14 { + height: 3.5rem; +} + .h-4 { height: 1rem; } @@ -1166,9 +1170,8 @@ video { width: 2rem; } -.w-min { - width: -webkit-min-content; - width: min-content; +.w-14 { + width: 3.5rem; } .w-1\/3 { @@ -1191,6 +1194,11 @@ video { width: 2.75rem; } +.w-min { + width: -webkit-min-content; + width: min-content; +} + .w-max { width: -webkit-max-content; width: max-content;