diff --git a/index.html b/index.html index bd12541..78f67ec 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,10 @@

Liste de status
+
+
Liste de status
+ +
+ + + + diff --git a/js/config.js b/js/config.js new file mode 100644 index 0000000..8517282 --- /dev/null +++ b/js/config.js @@ -0,0 +1,26 @@ +const config = { + NB_MAX_TAXONS: 10, + STATUS_LABELS: { + worldRedList: "Liste Rouge Mondiale", + europeanRedList: "Liste Rouge europeenne", + nationalRedList: "Liste Rouge nationale", + localRedList: "Liste Rouge locale", + bonnConvention: "Convention de Bonn", + bernConvention: "Convention de Bern", + barcelonaConvention: "Convention de Barcelona", + osparConvention: "Convention d'Ospar", + hffDirective: "Directive Habitats Faune Flore", + birdDirective: "Directive Oiseau", + nationalProtection: "Protection nationale", + regionalProtection: "Protection regionale", + departementalProtection: "Protection départementale", + nationalActionPlan: "Plan d'Action Nationale", + scapNationale: "Stratégie nationale pour les aires protégées", + scapRegionale: "Stratégie régionale pour les aires protégées", + sensibilite: "Sensibilité", + biogeoStatus: "Status biogeo", + reglementation: "Réglementation", + invasiveReglementation: "Réglementation invasive", + prioriteActionPubliqueNationale: "Priorité d'action publique nationale", + }, +}; diff --git a/js/media.js b/js/media.js index 147b328..7b2165e 100644 --- a/js/media.js +++ b/js/media.js @@ -1,20 +1,20 @@ -async function addMedia2Json(speciesDict) { - console.log("addMedia2Json"); - for(key in speciesDict) { - url = 'https://api.gbif.org/v1/species/' + speciesDict[key].gbifId + '/media'; +/** + * Fetch the url of the first image for a given GBIF id. + * @param {number} gbifId - GBIF id of the taxon + * @returns {Promise} - a promise resolving to the url of the first + * image for the given taxon, or undefined if no image is found + */ +function getMedias(gbifId) { + url = "https://api.gbif.org/v1/species/" + gbifId + "/media"; - await fetch(url,{method:'GET'}) - .then(function(response) {return response.json(); }) - .then(function(json) { - // Media checking - if (json.results.length > 0) { - speciesDict[key].media = json.results[0].identifier; - console.log(speciesDict[key].media + "
"); - } - }); - console.log(key + "
"); - console.log(url + "
"); - } - - return speciesDict; + return fetch(url, { method: "GET" }) + .then(function (response) { + return response.json(); + }) + .then(function (json) { + // Media checking + if (json.results.length > 0) { + return json.results[0].identifier; + } + }); } diff --git a/js/status.js b/js/status.js index ff26246..d3b6dc4 100644 --- a/js/status.js +++ b/js/status.js @@ -1,26 +1,16 @@ -const labels ={worldRedList: "Liste Rouge Mondiale", - europeanRedList: "Liste Rouge europeenne", - nationalRedList: "Liste Rouge nationale", - localRedList: "Liste Rouge locale", - bonnConvention: "Convention de Bonn", - bernConvention: "Convention de Bern", - barcelonaConvention: "Convention de Barcelona", - osparConvention: "Convention d'Ospar", - hffDirective: "Directive Habitats Faune Flore", - birdDirective: "Directive Oiseau", - nationalProtection: "Protection nationale", - regionalProtection: "Protection regionale", - departementalProtection: "Protection départementale", - nationalActionPlan: "Plan d'Action Nationale", - scapNationale: "Stratégie nationale pour les aires protégées", - scapRegionale: "Stratégie régionale pour les aires protégées", - sensibilite: "Sensibilité", - biogeoStatus: "Status biogeo", - reglementation: "Réglementation", - invasiveReglementation: "Réglementation invasive", - prioriteActionPubliqueNationale: "Priorité d'action publique nationale",} -async function getStatusForATaxon(taxonData) { - const reponse = await fetch(`https://taxref.mnhn.fr/api/taxa/${taxonData.cd_ref}/status/columns`); - const status = await reponse.json(); - return status._embedded +/** + * Given a taxonData object, fetch its status data from TaxRef API + * @param {Object} taxonData - a taxon data object with a cd_ref property + * @returns {Promise} - a list of status objects + */ +function getStatusForATaxon(taxonData) { + return fetch( + `https://taxref.mnhn.fr/api/taxa/${taxonData.cd_ref}/status/columns` + ) + .then((response) => { + return response.json(); + }) + .then((json) => { + return json._embedded?.status; + }); } diff --git a/js/taxon.js b/js/taxon.js index 80c6d7c..73585b3 100644 --- a/js/taxon.js +++ b/js/taxon.js @@ -1,29 +1,143 @@ -function getGbifTaxon(wkt, speciesList, page) { - console.log("getGbifTaxon", page); - const limit = 300; - const offset = page * limit; - geometry = wkt; - fetch( - "https://api.gbif.org/v1/occurrence/search?geometry=${geometry}&limit=${limit}&offset=${offset}" - ) - .then(function (response) { - return response.json(); - }) - .then(function (data) { - data.results.forEach((element) => { - occCount = (speciesList[element.species] || {})["occCount"] || 0; - speciesList[element.species] = { - gbifId: element.taxonKey, - occCount: occCount + 1, - }; - }); +const DEFAULT_NB_MAX_TAXONS = 10; - console.log(speciesList); - if (data.endOfRecords == false) { - getGbifTaxon(wkt, speciesList, page + 1); - } - }) - .catch(function (err) { - console.log(err); +/** + * Fetch a page of taxon data from GBIF given a WKT string + * @param {string} wkt - Well-Known Text representation of a polygon + * @param {number} limit - number of results to return + * @param {number} offset - offset of the first result + * @returns {Promise} + */ +function fetchApiTaxonGbif(wkt, limit, offset) { + return fetch( + `https://api.gbif.org/v1/occurrence/search?geometry=${wkt}&limit=${limit}&offset=${offset}` + ).then((response) => { + return response.json(); + }); +} + +/** + * Fetch the list of taxons from GBIF given a WKT string + * @param {string} wkt - Well-Known Text representation of a polygon + * @param {number} [nbMaxTaxons=DEFAULT_NB_MAX_TAXONS] - maximum number of results to return + * @param {Object} [params={ limit: 300 }] - parameters to pass to the API + * @returns {Promise>} - a list of taxons with their occurrence count and GBIF id + */ +function getGbifTaxon( + wkt, + nbMaxTaxons = DEFAULT_NB_MAX_TAXONS, + params = { limit: 300 } +) { + return ( + fetch(`https://api.gbif.org/v1/occurrence/search?geometry=${wkt}&limit=1`) + .then((response) => { + return response.json(); + }) + // Get total number of occurrences + .then((data) => { + return data.count; + }) + .then(async function (countOccurrence) { + // Compute the number of pages we need to query + const nbOfPages = Math.ceil(countOccurrence / params.limit); + + // Create a promise for each page + let promises = []; + for (let pageIndex = 0; pageIndex < nbOfPages; pageIndex++) { + const offset = pageIndex * params.limit; + promises.push(fetchApiTaxonGbif(wkt, params.limit, offset)); + } + let speciesList = {}; + // Run all promises and await for the responses + await Promise.all(promises).then((listOfData) => { + listOfData + .map((apiResult) => { + return apiResult.results; + }) + // For each page + .forEach((resultsPage) => { + // For each occurrence retrieve the gbifID and increase occurrence count + resultsPage.forEach((taxonData) => { + occCount = + (speciesList[taxonData.species] || {})["occCount"] || 0; + speciesList[taxonData.species] = { + gbifId: taxonData.taxonKey, + occCount: occCount + 1, + }; + }); + }); + }); + return speciesList; + }) + .then((taxonsData) => { + let data = []; + Object.keys(taxonsData).forEach((value) => { + data.push([value, taxonsData[value]["occCount"]]); + }); + data.sort(function (a, b) { + return b[1] - a[1]; + }); + data = data.slice(0, nbMaxTaxons).map((x) => { + return x[0]; + }); + let newTaxonsData = []; + data.forEach((key) => { + newTaxonsData.push({ ...taxonsData[key], species: key }); + }); + return newTaxonsData; + }) + ); +} + +/** + * Fetch a list of taxon data from the GTSI API given a WKT string + * @param {string} wkt - Well-Known Text representation of a polygon + * @param {number} [nbMaxTaxons=DEFAULT_NB_MAX_TAXONS] - number of results to return + * @returns {Promise} + */ +function getPgRestTaxon(wkt, nbMaxTaxons = DEFAULT_NB_MAX_TAXONS) { + const geometry = wkt; + return ( + fetch( + `https://dev-gtsi.cevennes-parcnational.net/api/rpc/get_taxa_list?in_wkt=${geometry}&in_limit=${nbMaxTaxons}` + ) + .then((response) => { + return response.json(); + }) + // Get total number of occurrences + .then((data) => { + return data.map( + ({ + count_occ: occCount, + species_name: species, + species_key: gbifId, + ...rest + }) => ({ + occCount, + species, + gbifId, + ...rest, + }) + ); + }) + ); +} + +/** + * Get the top N taxons from both the GTSI API and the GBIF API given a WKT string + * @param {string} wkt - Well-Known Text representation of a polygon + * @param {number} [nbMaxTaxons=DEFAULT_NB_MAX_TAXONS] - number of results to return + * @returns {Promise} - a list of taxons with their occurrence count and GBIF id + */ +function getAllTopNTaxon(wkt, nbMaxTaxons = DEFAULT_NB_MAX_TAXONS) { + let promises = [ + getPgRestTaxon(wkt, nbMaxTaxons), + getGbifTaxon(wkt, nbMaxTaxons), + ]; + return Promise.all(promises).then((listOfData) => { + const allData = [...listOfData[0], ...listOfData[1]]; + allData.sort(function (a, b) { + return b["occCount"] - a["occCount"]; }); + return Promise.resolve(allData.slice(0, nbMaxTaxons)); + }); } diff --git a/js/utils.js b/js/utils.js index 71b3822..ac7cb1f 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,18 +1,25 @@ - class SPARQLQueryDispatcher { - constructor( endpoint ) { - this.endpoint = endpoint; - } + /** + * Class that enable to query a SPARQL endpoint + */ + constructor(endpoint) { + this.endpoint = endpoint; + } - query( sparqlQuery ) { - const fullUrl = this.endpoint + '?query=' + encodeURIComponent( sparqlQuery ); - const headers = { 'Accept': 'application/sparql-results+json' }; + query(sparqlQuery) { + const fullUrl = this.endpoint + "?query=" + encodeURIComponent(sparqlQuery); + const headers = { Accept: "application/sparql-results+json" }; - return fetch( fullUrl, { headers } ).then( body => body.json() ); - } + return fetch(fullUrl, { headers }).then((body) => body.json()); + } } - +/** + * Fetch the TaxRef ID for a given GBIF ID using the Wikidata SPARQL endpoint. + * @param {string} gbifID - The GBIF ID of the taxon. + * @returns {Promise} - A promise that resolves to the TaxRef ID, + * or null if no TaxRef ID is found. + */ async function getTaxRefCdNom(gbifID) { const query = `PREFIX wikibase: PREFIX wd: @@ -29,15 +36,138 @@ WHERE { bd:serviceParam wikibase:language "fr" . } } - ` - // query = encodeURIComponent(query) - let sparql_endpoint = "https://query.wikidata.org/sparql" - - - let idTaxRef = null; - const dispatcher = new SPARQLQueryDispatcher(sparql_endpoint); - await dispatcher.query(query).then(data => { - idTaxRef = data?.results?.bindings[0]?.taxrefID.value - }) - return idTaxRef + `; + // query = encodeURIComponent(query) + let sparql_endpoint = "https://query.wikidata.org/sparql"; + + let idTaxRef = null; + const dispatcher = new SPARQLQueryDispatcher(sparql_endpoint); + await dispatcher.query(query).then((data) => { + idTaxRef = data?.results?.bindings[0]?.taxrefID.value; + }); + return idTaxRef; +} + +/** + * Get the parameters from the URL's query string. + * @returns {Object} an object with the following properties: + * - {string} wkt - Well-Known Text representation of a polygon + * - {string} geolocation - the geolocation string + * - {number} x - the x coordinate of the center of the circle + * - {number} y - the y coordinate of the center of the circle + * - {number} radius - the radius of the circle + * - {boolean} use_gbif - whether to use GBIF or not + * - {boolean} use_gn2 - whether to use GN2 or not + * - {string} gn_2_url - the URL of the GN2 API + * - {number} nb_results - the number of results to return + */ +function getQueryParams() { + const params = new URL(location).searchParams; + + return { + wkt: params.get("wkt"), + geolocation: params.get("geolocation"), + x: parseFloat(params.get("x")), + y: parseFloat(params.get("y")), + radius: parseInt(params.get("radius")), + use_gbif: JSON.parse(params.get("use_gbif")), + use_gn2: JSON.parse(params.get("use_gn2")), + gn_2_url: params.get("gn_2_url"), + nb_results: parseInt(params.get("nb_results")), + }; +} + +/** + * Converts a GeoJSON feature to a Well-Known Text (WKT) string. + * @param {Object} feature - A GeoJSON feature object with geometry containing coordinates. + * @returns {string} - A WKT string representing the polygon. + */ +function convertGeoJsonToWkt(feature) { + const coo = feature.geometry.coordinates + .map(function (ring) { + return ( + "(" + + ring + .map(function (p) { + return p[0] + " " + p[1]; + }) + .join(", ") + + ")" + ); + }) + .join(", "); + const wkt_str = "POLYGON(" + coo + ")"; + return wkt_str; +} + +/** + * Given a params object, returns a WKT string representing a localisation. + * If params.wkt is defined, it is returned as is. + * If params.x and params.y are defined, a buffer of radius params.radius + * is created around the point and converted to WKT. + * If neither wkt nor x/y are defined, this function returns undefined. + * @param {Object} params - An object containing localisation parameters. + * @returns {string|undefined} - A WKT string or undefined. + */ +function processLocalisation(params) { + if (params.wkt) { + return params.wkt; + } + if (params.x && params.x) { + const point = turf.point([params.x, params.x]); + const buffered = turf.buffer(point, params.radius | 100, { + units: "meters", + }); + //convert the json-input to WKT + return convertGeoJsonToWkt(buffered); + } +} + +/** + * Completes the data with the status and the picture of each taxon in the provided list. + * @param {Array} taxonsData - An array of taxon data objects. + * @returns {Promise} - A promise that resolves to an array of completed taxon data. + */ +function completeTaxonsData(taxonsData) { + promises = []; + taxonsData.forEach((taxon) => { + promises.push(completeData(taxon)); + }); + console.log(promises); + return Promise.all(promises); +} + +/** + * Completes a taxon data object by adding its TaxRef ID, status data, and + * picture URL. + * @param {Object} taxonData - A taxon data object with a `gbifId` property. + * @returns {Promise} - A promise that resolves to the completed taxon + * data object. + */ +function completeData(taxonData) { + let getCdRefPromise = null; + if ("cdRef" in taxonData) { + getCdRefPromise = Promise.resolve(taxonData["cdRef"]); + } else { + getCdRefPromise = getTaxRefCdNom(taxonData.gbifId); + } + return getCdRefPromise + .then(async (taxrefId) => { + // get cdRef + if (taxrefId) { + taxonData["cdRef"] = taxrefId; + await getStatusForATaxon(taxonData).then((statusData) => { + // use cdRef to get status of the taxon + taxonData["status"] = statusData; + }); + } + }) + .then(async function () { + await getMedias(taxonData.gbifId).then((mediaUrl) => { + taxonData["mediaUrl"] = mediaUrl; + }); + }) + .then(() => { + return taxonData; + }); } diff --git a/media.html b/media.html index 011bd49..25502bb 100644 --- a/media.html +++ b/media.html @@ -8,7 +8,7 @@ "Anthus pratensis": { gbifId: 2490266, occCount: 1 } } -addMedia2Json(speciesObj); +getMedias(speciesObj); \ No newline at end of file