Skip to content

Repository files navigation

card-api

Service web des fiches card : extraction de variables hydroclimatiques sur les débits de la Banque Hydro (via Hub'Eau) et diagnostic de stationnarité Mann-Kendall / pente de Sen (via stase).

Documentation interactive : https://card-api.riverly.inrae.fr/docs (essai des requĂȘtes dans le navigateur, schĂ©mas de rĂ©ponse).

Service public de recherche (INRAE, UR RiverLy). Ouvert, sans inscription ; code GPL-3, données Hub'Eau en Licence Ouverte. Déploiement et développement : INSTALL.md.

Comment les piĂšces s'emboĂźtent

flowchart LR
  CARD["card<br/>fiches YAML<br/><i>quoi calculer</i>"] --> API
  STASE["stase<br/>moteur<br/><i>comment calculer</i>"] --> API
  HE["Hub'Eau<br/>eaufrance<br/><i>débits observés</i>"] --> API
  API["card-api<br/>ce service"] --> OUT["résultat JSON<br/>provenance + droits"]
Loading

card ne calcule rien, stase ne connaßt pas les fiches, et la donnée vient d'ailleurs. C'est pourquoi chaque réponse porte la version des trois, et les droits qui vont avec.

Quelle porte prendre

Votre cas La porte Pour voir
Quelques stations, ponctuellement ce service la doc interactive
Des milliers de stations, ou vos propres données la bibliothÚque Python, en local, sans quota dépÎt card
Les mĂȘmes, mais en R le paquet R, qui appelle card sans le réécrire dĂ©pĂŽt card4r
Comprendre ce qu'une fiche calcule la fiche dessinée VCN10 en figure
Savoir quels filtres existent le vocabulaire de classification /v1/vocabulary
Brancher un site web l'API depuis le navigateur (CORS ouvert) les fiches d'étiage
Publier un résultat citer la fiche par son swhid, présent dans la réponse CITATION.cff
Une variable qui n'existe pas copier une fiche, l'adapter, l'exécuter chez vous développer sa propre fiche

Les endpoints

Endpoint RĂŽle
GET / racine : liens typés vers le contrat, la doc et la version courante
GET /v1 point d'entrée : ce qu'est le service, ce qu'il relie, droits
GET /v1/cards catalogue des fiches CARD, filtrable par facettes ; ids rend la sĂ©lection prĂȘte Ă  coller
GET /v1/cards/{id} détail d'une fiche (fr/en) et liens vers sa définition
GET /v1/cards/{id}/figure la fiche dessinée (texte) : sa chaßne de calcul, lang=fr ou en
GET /v1/vocabulary valeurs valides des facettes, donc les filtres acceptés
GET /v1/stations recherche de stations ; codes rend la sĂ©lection prĂȘte Ă  coller
GET /v1/extract chroniques Hub'Eau → variables CARD
GET /v1/extract.csv les mĂȘmes sĂ©ries, en CSV pour le tableur
GET /v1/trend extraction + test de Mann-Kendall et pente de Sen
GET /v1/trend.csv les mĂȘmes diagnostics, en CSV pour le tableur
GET /v1/trend/figure le diagnostic dessiné (texte) : sens, ampleur, verdict
POST /v1/jobs grosses demandes en file de calcul (202 + ticket)
GET /v1/jobs/{id} statut et progression ; /result : résultat gelé
GET /v1/health santé du service (file de calcul, disque)
/docs documentation interactive (OpenAPI)

Chaque rĂ©ponse est en JSON et se suffit Ă  elle-mĂȘme : data (les rĂ©sultats), meta (unitĂ©s, noms français et anglais, classification), la source des donnĂ©es et les versions des logiciels. Deux formats au choix : orient=records (dĂ©faut, une liste d'objets, comme Hub'Eau) ou orient=columns (colonnaire, {colonne: [valeurs]}, plus compact).

Préparer sa demande

La station

La recherche interroge le référentiel hydrométrique Hub'Eau par nom, code ou département, et renvoie pour chaque station son code, son libellé, ses coordonnées et son état de service. C'est aussi le moyen de retrouver le code actuel d'une station connue sous son ancien code Banque Hydro.

curl "https://card-api.riverly.inrae.fr/v1/stations?libelle=Austerlitz"
# → F700000103 | La Seine à Paris - Austerlitz [>2006]
curl "https://card-api.riverly.inrae.fr/v1/stations?departement=07&size=100"

La réponse porte un champ codes : tous les codes trouvés, déjà séparés par des virgules, à coller tel quel dans le paramÚtre stations d'/v1/extract ou d'/v1/trend. Vingt stations ne se recopient pas une par une.

Les fiches

Chaque fiche définit une variable calculable sur la chronique de débit : module, étiages, crues, saisonnalité... Le catalogue se filtre par facettes de classification (domain, phenomenon, season, output...) ou par texte libre, en français ou en anglais :

curl "https://card-api.riverly.inrae.fr/v1/cards?phenomenon=basses%20eaux&output=série"
curl "https://card-api.riverly.inrae.fr/v1/cards?statistic=change&search=VCN"
curl "https://card-api.riverly.inrae.fr/v1/cards/VCN10?lang=fr"      # détail d'une fiche

MĂȘme chose cĂŽtĂ© fiches : le champ ids rend la sĂ©lection filtrĂ©e prĂȘte Ă  coller dans le paramĂštre cards. Attention, l'identifiant d'une fiche est le nom de son fichier (colonne id), pas sa variable : ETPMA_month produit ETPMA_jan Ă  ETPMA_dec, et c'est le premier qu'attendent les endpoints.

Cas d'usage

Le mĂȘme fil en Python puis en R : extraire des indicateurs annuels, en tracer un, puis diagnostiquer sa tendance et superposer points et droite de Sen. Les indicateurs annuels se tracent en points (une valeur par an), pas en ligne continue.

Deux paramÚtres méritent un mot :

  • sampling=preferred fige la fenĂȘtre annuelle de calcul sur celle que chaque fiche dĂ©clare (par exemple l'annĂ©e hydrologique 09-01 pour les crues). Par dĂ©faut, les fiches d'Ă©tiage et de crue adaptent leur fenĂȘtre Ă  chaque station ; preferred rend les rĂ©sultats directement comparables entre stations et reproductibles.
  • series=true sur /v1/trend joint Ă  la rĂ©ponse, sous series, les sĂ©ries extraites sur lesquelles la tendance a Ă©tĂ© calculĂ©e : points et diagnostic issus du mĂȘme calcul, sans second appel.
  • /v1/trend/figure rend le mĂȘme rĂ©sultat en table lisible, avec les mĂȘmes paramĂštres : sens, ampleur, p-value et verdict en clair. De quoi lire une rĂ©ponse sans traverser le JSON, dans un terminal comme dans /docs.
  • /v1/extract.csv et /v1/trend.csv rendent les mĂȘmes donnĂ©es en fichier ouvrable au tableur, virgule et point dĂ©cimal. La provenance (versions, SWHID, source, empreinte, droits) voyage en lignes # en tĂȘte, que read_csv(comment="#") et read.csv(comment.char="#") sautent d'eux-mĂȘmes : un fichier enregistrĂ© sait toujours dire d'oĂč il vient. Le nom proposĂ© au tĂ©lĂ©chargement porte l'analyse, card-api_trend_F700000103_QA-VCN10_AR1_2005-2026_ac9c7eed.csv.
  • stations_meta=true joint les fiches du rĂ©fĂ©rentiel Hub'Eau des stations demandĂ©es (libellĂ©, coordonnĂ©es, Ă©tat de service). Un rĂ©sultat devient autoportant : tracer une carte ne demande plus d'aller chercher les positions ailleurs.

Quand la demande part en file de calcul

Une grosse demande reçoit un ticket (202) au lieu d'une rĂ©ponse immĂ©diate. Ce qui dĂ©cide n'est pas le nombre de stations mais le nombre de stations Ă  tĂ©lĂ©charger : rapatrier une chronique coĂ»te environ trente fois plus que la calculer. Vingt stations dĂ©jĂ  lues rĂ©pondent donc en direct, les mĂȘmes vingt Ă  froid partent en file. La mĂȘme URL peut par consĂ©quent donner un ticket au premier appel et un rĂ©sultat au second, et c'est voulu.

Le résultat d'un job se récupÚre dans la représentation de votre choix, quelle que soit celle demandée au dépÎt :

route rend
/v1/jobs/{id}/result le JSON gelé, avec son bloc de provenance
/v1/jobs/{id}/result.csv le mĂȘme rĂ©sultat en CSV, mĂȘmes colonnes qu'en direct
/v1/jobs/{id}/result/figure le mĂȘme rĂ©sultat dessinĂ© (jobs de tendance)

Ce qui limite le choix n'est pas la porte d'entrée mais ce que le résultat contient : une extraction n'a pas de verdict de stationnarité à dessiner. Le ticket rendu par un .csv porte directement l'adresse de son CSV, il n'y a rien à deviner.

Quand une station n'a rien Ă  donner

Toutes les stations du référentiel hydrométrique ne publient pas de débit journalier. Une échelle limnimétrique, par exemple, mesure une hauteur d'eau : sans courbe de tarage elle n'a pas de série de débit, et rien dans le référentiel ne l'annonce (ni type_station, ni en_service, qui vaut d'ailleurs false pour beaucoup de stations fermées dont l'historique est intact et parfaitement utilisable). La demander est le seul moyen de le savoir.

Une telle station est donc écartée du calcul, pas fatale : les autres sont calculées, et le résultat dit exactement ce qu'il contient.

champ contenu
stations les stations réellement calculées, celles que data contient
stations_requested celles que vous avez demandées
stations_omitted les écartées, avec reason et detail

reason vaut no_series (aucune chronique publiĂ©e), no_data_in_period (chronique prĂ©sente, mais rien dans la fenĂȘtre demandĂ©e) ou ambiguous_site (code de site dont plusieurs stations mesurent en parallĂšle). Le bloc est toujours prĂ©sent, vide quand tout va bien : un client n'a pas Ă  tester son existence.

Deux garde-fous. Si aucune station n'a de série, la demande est refusée en 404 plutÎt que rendue vide, un résultat sans lignes se laissant lire comme un résultat. Et une panne Hub'Eau reste une erreur 504 : elle n'est jamais transformée en omission, sans quoi un incident passager produirait des résultats discrÚtement amputés.

L'information voyage dans toutes les reprĂ©sentations : bloc JSON, lignes # station Ă©cartĂ©e en tĂȘte des .csv, mention en clair dans /v1/trend/figure, et gelĂ©e dans le rĂ©sultat d'un job.

En Python

Extraction : module (QA) et étiage (VCN10) de la Seine à Paris.

import requests, pandas as pd

r = requests.get("https://card-api.riverly.inrae.fr/v1/extract", params={
    "stations": "F700000103",
    "cards": "QA,VCN10",
    "start": "1990-01-01",
    "orient": "columns",              # directement ingérable par pandas
}).json()

r["data"]["VCN10"]
# {"code_station": ["F700000103", "F700000103", ...],
#  "date":         ["2005-02-01", "2006-02-01", ...],
#  "VCN10":        [None, 104.6066, ...]}          # la 1re année est incomplÚte

r["meta"][1]
# {"variable_en": "VCN10", "unit_fr": "m^{3}.s^{-1}",
#  "name_fr": "Minimum annuel de la moyenne sur 10 jours du débit journalier", ...}

Figure, avec l'unité lue dans les métadonnées :

import matplotlib.pyplot as plt

vcn10 = pd.DataFrame(r["data"]["VCN10"])
meta = pd.DataFrame(r["meta"])
unit = meta.loc[meta.variable_en == "VCN10", "unit_fr"].iloc[0]
vcn10.plot(x="date", y="VCN10", style="o", ylabel=f"VCN10 [{unit}]")
plt.show()

Tendance du VCN10 : une ligne par station (H : tendance significative ? p-value, pente de Sen absolue a et relative).

r = requests.get("https://card-api.riverly.inrae.fr/v1/trend", params={
    "stations": "F700000103",
    "cards": "VCN10",
    "sampling": "preferred",
    "series": "true",
}).json()

tr = pd.DataFrame(r["data"]["VCN10"]).iloc[0]
# code_station    F700000103        h                False
# level                  0.1        p             0.339880
# a                -0.776973        b           150.192329
# a_relative       -0.678864        mean_period 114.451837

h est le verdict au seuil level, p la p-value, a la pente de Sen dans l'unitĂ© de la variable et par an, a_relative la mĂȘme en pourcentage de la moyenne de la pĂ©riode. Ici la baisse apparente n'est pas significative, et le service le dit plutĂŽt que de la laisser croire.

Points et droite de Sen sur la mĂȘme figure :

s = pd.DataFrame(r["series"]["VCN10"])
dates = pd.to_datetime(s["date"])
years = (dates - pd.Timestamp("1970-01-01")).dt.days / 365.25
plt.plot(dates, s["VCN10"], "o")
plt.plot(dates, tr["a"] * years + tr["b"], "--")
plt.show()

En R

Extraction : module (QA) et étiage (VCN10) de la Seine à Paris (format records par défaut : fromJSON en fait des data.frame).

library(jsonlite)

r <- fromJSON(paste0("https://card-api.riverly.inrae.fr/v1/extract?stations=F700000103",
                     "&cards=QA,VCN10&start=1990-01-01"))

head(r$data$VCN10, 3)
#   code_station       date    VCN10
# 1   F700000103 2005-02-01       NA
# 2   F700000103 2006-02-01 104.6066
# 3   F700000103 2007-02-01 139.7335

Ces exemples appellent le service par HTTP, ce qui est le bon geste pour quelques stations Hub'Eau. Pour vos propres donnĂ©es, ou pour du volume sans quota, card4r appelle le mĂȘme recueil en local depuis R.

Figure, avec l'unité lue dans les métadonnées :

vcn10 <- r$data$VCN10
unit <- r$meta$unit_fr[r$meta$variable_en == "VCN10"]
plot(as.Date(vcn10$date), vcn10$VCN10,
     ylab = paste0("VCN10 [", unit, "]"))

Tendance du VCN10 : une ligne par station (H : tendance significative ? p-value, pente de Sen absolue a et relative).

r <- fromJSON(paste0("https://card-api.riverly.inrae.fr/v1/trend?stations=F700000103",
                     "&cards=VCN10&sampling=preferred&series=true"))
tr <- r$data$VCN10[1, ]

Points et droite de Sen sur la mĂȘme figure :

s <- r$series$VCN10
dates <- as.Date(s$date)
years <- as.numeric(dates) / 365.25
plot(dates, s$VCN10)
lines(dates, tr$a * years + tr$b, lty = 2)

Grosses demandes : les jobs

Au-dessus de 10 stations ou 20 fiches, la demande devient un job, sans inscription : la réponse 202 donne un ticket, le calcul se fait en file, le résultat reste téléchargeable plusieurs jours avec un bloc de provenance (paramÚtres, versions, date des données) qui le rend citable et reproductible.

job = requests.post("https://card-api.riverly.inrae.fr/v1/jobs", json={
    "endpoint": "trend",
    "stations": liste_de_codes,       # jusqu'Ă  100
    "cards": ["QA", "VCN10"],
    "sampling": "preferred",
}).json()
# suivre job["status_url"] (queued -> running -> done, avec progression)
# puis récupérer job["result_url"]

Les appels GET /v1/extract et /v1/trend trop gros basculent automatiquement sur ce circuit (réponse 202 au lieu d'un refus).

Quotas et clés de priorité

Le service est public avec un quota par IP et par minute ; en cas de dĂ©passement (429), l'en-tĂȘte Retry-After indique quand rĂ©essayer. Les chroniques sont mises en cache 24 h cĂŽtĂ© serveur : rĂ©pĂ©ter une requĂȘte ne re-tĂ©lĂ©charge rien depuis Hub'Eau.

Si vous atteignez le quota, c'est presque toujours qu'une boucle appelle le service une fois par station. Ce n'est pas la bonne forme : stations prend une liste (stations=A,B,C), un appel rapporte tout, et au-delĂ  des plafonds synchrones la demande bascule d'elle-mĂȘme en file de calcul. Le quota est calculĂ© large pour ne gĂȘner personne en usage normal, y compris tout un Ă©tablissement derriĂšre une mĂȘme adresse publique.

Pour un besoin massif ou rĂ©current (centaines de stations, chaĂźnes de traitement), demandez une clĂ© de prioritĂ© gratuite par courriel Ă  louis.heraut@inrae.fr : qui vous ĂȘtes, l'usage prĂ©vu, et le projet associĂ© si vous voulez. Attribution manuelle, rĂ©ponse rapide. Le jeton vous revient par le mĂȘme canal, ce qui est la raison de ce choix : une issue GitHub est publique, le jeton ne peut pas y transiter. Elle se passe en en-tĂȘte X-API-Key (de prĂ©fĂ©rence Ă  key=, qui laisse la clĂ© dans les logs web) : quotas par minute levĂ©s, plafonds relevĂ©s (jusqu'Ă  1000 stations par job), jobs en tĂȘte de file, et GET /v1/jobs liste vos jobs dĂ©posĂ©s avec la clĂ© (tickets compris : pratique pour retrouver un rĂ©sultat dont le ticket est Ă©garĂ©).

Le jeton n'est communiqué qu'une fois, à la création (le serveur n'en garde qu'un hachage) : conservez-le, un jeton perdu se remplace. Le journal du service ne stocke jamais votre nom, seulement le préfixe du jeton.

Savoir ce qui a produit un résultat

Chaque rĂ©ponse porte de quoi refaire le calcul plus tard, ou expliquer pourquoi il ne redonne pas la mĂȘme chose :

Champ Ce qu'il dit
api_version la version du service qui a répondu
card_version, card_commit, card_swhid le corpus de fiches employé
stase_version, stase_commit, stase_swhid le moteur de calcul employé
meta[].version, meta[].swhid la définition de chaque variable, fiche par fiche
data_fetched_at quand les chroniques Hub'Eau ont été lues
data_fingerprint ce qu'elles contenaient

Les identifiants swh: s'ouvrent en collant https://archive.softwareheritage.org/ devant : ils donnent le code et les fiches tels qu'ils étaient, indépendamment de GitHub.

data_fingerprint demande une explication, parce qu'il ne se recalcule pas de votre cĂŽtĂ© : c'est un jeton de comparaison, pas une somme de contrĂŽle. Il rĂ©sume les chroniques employĂ©es, entiĂšres, avant tout filtre de pĂ©riode. Deux rĂ©sultats qui portent la mĂȘme valeur reposent sur la mĂȘme donnĂ©e ; deux valeurs diffĂ©rentes signalent que Hub'Eau a rĂ©visĂ© sa donnĂ©e entre les deux appels, ce qui arrive rĂ©guliĂšrement et explique alors l'Ă©cart sans qu'il faille chercher du cĂŽtĂ© du calcul. Le prĂ©fixe v1: est la version de l'algorithme : s'il change un jour, vous saurez que deux empreintes ne sont plus comparables.

Pour un job, le résultat gelé porte en plus data_fingerprints, le détail station par station : quand un lot de 200 stations change, il dit laquelle.

PérimÚtre

Le service ne fournit que des débits journaliers (fiches à entrée Q) ; le diagnostic de tendance ne s'applique qu'aux fiches de forme series (la tendance d'un scalaire ou d'une courbe n'a pas de sens).

L'écosystÚme

card le recueil de fiches, en Python
stase le moteur d'agrégation et de tendance
card4r le mĂȘme recueil, appelĂ© depuis R
card-api le service web, sur les dĂ©bits Hub'Eau (vous ĂȘtes ici)
CARD-R · EXstat les paquets R historiques, remplacés

Citer

Les métadonnées de citation sont dans CITATION.cff (bouton « Cite this repository » de GitHub) et codemeta.json (moissonné par Software Heritage et HAL ; identifiant pérenne à venir par ce canal). Dans une publication, citez aussi la source des données (Hub'Eau hydrométrie, eaufrance, Licence Ouverte) et ce qui a produit votre résultat : chaque réponse porte les versions et les identifiants Software Heritage du corpus et du moteur, la version de chaque fiche employée, la date de lecture des données et leur empreinte. Le détail est dans « Savoir ce qui a produit un résultat » plus haut ; le résultat gelé d'un job les rassemble dans un bloc de provenance avec les paramÚtres de l'appel.

Mentions légales

Éditeur. INRAE (Institut national de recherche pour l'agriculture, l'alimentation et l'environnement), Ă©tablissement public Ă  caractĂšre scientifique et technologique, 147 rue de l'UniversitĂ©, 75338 Paris Cedex 07. Service dĂ©veloppĂ© et exploitĂ© par l'unitĂ© de recherche RiverLy, 5 rue de la Doua, CS 20244, 69625 Villeurbanne Cedex.

Responsable de la publication. Louis Héraut (INRAE, UR RiverLy), louis.heraut@inrae.fr.

Hébergement. Machine virtuelle des centres de données d'INRAE.

Propriété intellectuelle. Le code du service, du corpus de définitions card et du moteur stase est sous GPL-3.0-or-later. Les observations hydrométriques proviennent de Hub'Eau (eaufrance) et restent sous Licence Ouverte / Etalab 2.0. Chaque réponse du service porte ces droits dans son bloc rights.

Données personnelles

Le service est public, sans inscription et sans compte : le consulter et l'interroger ne demande aucune identité. Deux traitements existent néanmoins, de natures trÚs différentes.

1. Le journal d'usage, anonymisĂ©. Une ligne par requĂȘte de calcul : horodatage, endpoint appelĂ©, nombre de stations et variables demandĂ©es, et un identifiant technique dĂ©rivĂ© de l'adresse IP. L'adresse IP elle-mĂȘme n'est jamais Ă©crite : seul en est conservĂ© un condensat SHA-256 salĂ© et tronquĂ©, dont le sel est propre au dĂ©ploiement. Il permet de compter des visiteurs distincts sans permettre de remonter Ă  quiconque. Aucun traceur, aucun cookie, aucune mesure d'audience tierce.

FinalitĂ© : mesurer l'usage du service, ce qui constitue la preuve d'impact attendue dans les dossiers de financement de la recherche publique. Base lĂ©gale : l'exĂ©cution de la mission d'intĂ©rĂȘt public d'INRAE. Conservation : journal segmentĂ© par annĂ©e, les fichiers anciens sont supprimĂ©s. Ces donnĂ©es Ă©tant anonymisĂ©es, elles ne permettent pas de vous identifier et aucune demande individuelle ne peut y ĂȘtre rattachĂ©e.

2. Les clĂ©s de prioritĂ©, nominatives. Une clĂ© est attribuĂ©e Ă  une personne identifiĂ©e, et le fichier de clĂ©s du serveur conserve son nom et son organisme, associĂ©s au prĂ©fixe public du jeton et Ă  sa date de crĂ©ation. Le jeton lui-mĂȘme n'est pas conservĂ©, seul son condensat l'est.

FinalitĂ© : attribuer et rĂ©voquer les clĂ©s, et distinguer les usages massifs dans les statistiques. Base lĂ©gale : l'exĂ©cution de la mission d'intĂ©rĂȘt public d'INRAE. Conservation : jusqu'Ă  la rĂ©vocation de la clĂ©, qui efface le lien entre le prĂ©fixe et la personne. Diffusion : le journal d'usage ne reçoit que le prĂ©fixe, jamais le nom.

Une clĂ© se demande par courriel, et non par une issue publique : votre nom, votre organisme et votre projet n'ont pas Ă  ĂȘtre exposĂ©s, et le jeton lui-mĂȘme ne peut voyager que par un canal privĂ©.

Vos droits. Sur les données nominatives des clés, vous disposez d'un droit d'accÚs, de rectification, d'opposition pour motifs légitimes, de limitation et d'effacement. Pour les exercer, ou pour toute question sur ces traitements, écrire à louis.heraut@inrae.fr, ou au délégué à la protection des données d'INRAE : cil-dpo@inrae.fr, INRAE, 24 chemin de Borde Rouge, Auzeville, CS 52627, 31326 Castanet-Tolosan Cedex.

En cas de désaccord persistant, vous pouvez saisir la CNIL, 3 place de Fontenoy, TSA 80715, 75334 Paris Cedex 07.

Politique de protection des données d'INRAE : https://science-ouverte.inrae.fr/fr/donnees-personnelles.

About

🎮 Public API for the card collection: hydroclimatic variables and trends (Mann-Kendall, Sen slope) on Hub'Eau discharge data.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages