Code source de pyspc.io.sandre.reader

#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2021  R. Marty
#   (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""Bibliothèque pyspc du projet pyspc - IO - Sandre - read."""
import collections
from datetime import datetime as dt, timedelta as td
import pandas as pnd

from pyspc.convention.sandre import (
    ASSOC, RATIO_UNITS, TRENDS, VARNAMES, ELABVARNAMES)
import pyspc.core.exception as _exception
from pyspc.data.sandre import ComSimul, Sandre
from pyspc.metadata.sandre import Sandre as SandreMeta
from pyspc.core.config import Config
from pyspc.core.keyseries import str2tuple, tuple2str
from pyspc.core.location import Location, Locations
from pyspc.core.parameter import Parameter
from pyspc.core.ratingcurve import RatingCurve, RatingCurves
from pyspc.core.serie import Serie
from pyspc.core.series import Series


[docs] def read_Sandre(filename=None, datatype=None, codes=None, models=None, scens=None, levelcor=None, flowmes=None, warning=False): """ Créer une collection à partir d'un fichier xml Sandre. Parameters ---------- filename : str Nom du fichier XML-Sandre datatype : str Type du fichier XML-Sandre Voir pyspc.convention.sandre.DATATYPES codes : list Liste des codes à considérer models : list Liste des modèles à considérer, si datatype = 'data_fcst_hydro" scens : list Liste des scénarios à considérer, si datatype = 'data_fcst_hydro" warning : bool Afficher les avertissements ? défaut: False Other Parameters ---------------- levelcor : str Nom du fichier XML-Sandre - Courbe de correction Utilisé si datatype = 'ratingcurve' flowmes : str Nom du fichier XML-Sandre - Jaugeages Utilisé si datatype = 'ratingcurve' Returns ------- - pyspc.core.location.Locations si datatype parmi ['loc_hydro', 'loc_meteo'] - pyspc.core.ratingcurve.RatingCurves si datatype parmi ['ratingcurve'] - pyspc.core.series.Series si datatype parmi ['data_fcst_hydro', 'data_obs_hydro', 'data_obs_meteo', 'levelcor'] Examples -------- >>> from pyspc.io.sandre import read_Sandre Cas de sites météorologiques >>> f = 'data/metadata/sandre/SiteMeteo.xml' >>> locs = read_Sandre(filename=f, datatype='loc_meteo') >>> locs ************************************* *********** LOCATIONS *************** ************************************* * NOM DE LA COLLECTION = Sandre * NOMBRE DE LIEUX = 3 * ---------------------------------- * LIEU #1 * - CODE = 43130002 * ---------------------------------- * LIEU #2 * - CODE = 43091005 * ---------------------------------- * LIEU #3 * - CODE = 43051003 ************************************* >>> locs['43130002'] ************************************* *********** LOCATION **************** ************************************* * CODE LIEU = 43130002 * NOM LIEU = MAZET-VOLAMONT * NOM COMPLET LIEU = MAZET-VOLAMONT * COURS D'EAU = None * TYPE LIEU = point * COORDONNEES X = 749997.00 m * COORDONNEES Y = 2004520.00 m * ALTITUDE LIEU = 1130.00 m NGF * SURFACE LIEU = -1.00 km2 * COMMUNES = None * TRONCONS = None ************************************* Cas de sites hydrologiques >>> f = 'data/metadata/sandre/SiteHydro.xml' >>> locs = read_Sandre(filename=f, datatype='loc_hydro') >>> locs ************************************* *********** LOCATIONS *************** ************************************* * NOM DE LA COLLECTION = Sandre * NOMBRE DE LIEUX = 2 * ---------------------------------- * LIEU #1 * - CODE = K0550010 * ---------------------------------- * LIEU #2 * - CODE = K0260010 ************************************* >>> locs['K0550010'] ************************************* *********** LOCATION **************** ************************************* * CODE LIEU = K0550010 * NOM LIEU = Bas-en-Basset * NOM COMPLET LIEU = La Loire à Bas-en-Basset * COURS D'EAU = La Loire * TYPE LIEU = basin * COORDONNEES X = 739757.00 m * COORDONNEES Y = 2034330.00 m * ALTITUDE LIEU = 450.00 m NGF * SURFACE LIEU = 3234.00 km2 * COMMUNES = None * TRONCONS = None ************************************* Cas de stations hydrologiques >>> f = 'data/metadata/sandre/StationHydro.xml' >>> locs = read_Sandre(filename=f, datatype='loc_hydro') >>> locs ************************************* *********** LOCATIONS *************** ************************************* * NOM DE LA COLLECTION = Sandre * NOMBRE DE LIEUX = 2 * ---------------------------------- * LIEU #1 * - CODE = K435001020 * ---------------------------------- * LIEU #2 * - CODE = K435001010 ************************************* >>> locs['K435001010'] ************************************* *********** LOCATION **************** ************************************* * CODE LIEU = K435001010 * NOM LIEU = Pont Royals * NOM COMPLET LIEU = La Loire à Orléans - Pont Royals * COURS D'EAU = La Loire * TYPE LIEU = point * COORDONNEES X = 567716.00 m * COORDONNEES Y = 2322086.00 m * ALTITUDE LIEU = -1.00 m NGF * SURFACE LIEU = -1.00 km2 * COMMUNES = None * TRONCONS = None ************************************* Cas de courbes de tarage >>> f = 'data/metadata/sandre/RatingCurves.xml' >>> curves = read_Sandre(filename=f, datatype='ratingcurve') >>> curves ************************************* ********* RATINGCURVES ************** ************************************* * NOM DE LA COLLECTION = Sandre * NOMBRE DE COURBES = 2 * ---------------------------------- * COURBE #1 * - CODE = K055001010 * - NUM = H201416 * - FOURNISSEUR = PHyC * ---------------------------------- * COURBE #2 * - CODE = K055001010 * - NUM = H201620 * - FOURNISSEUR = PHyC ************************************* >>> curves['H201620'] ************************************* ********* RATINGCURVE *************** ************************************* * CODE STATION = K055001010 * CODE COURBE TARAGE = H201620 * FOURNISSEUR = Provider(name='PHyC') * PERIODE VALIDITE = (dt(2016, 11, 23, 2, 30), dt(2020, 1, 1, 0, 0)) * PERIODE TEMPORELLE = [2016-11-23 02:30:00, 2020-01-01] * INTERVALLE VALIDITE = (-1.36, 5.24) * DATE MAJ = 2017-01-13 13:53:15 ************************************* Cas de courbes de correction >>> f = 'data/data/sandre/levelcor.xml' >>> series = read_Sandre(filename=f, datatype='levelcor') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = obs * NOMBRE DE SERIES = 1 * ---------------------------------- * SERIE #1 * - CODE = K055001010 * - VARNAME = HI * - META = levelcor ************************************* >>> series[('K055001010', 'HI', 'levelcor')] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = HI * INTITULE VARIABLE = Hauteur instantanée * IDENTIFIANT = K055001010 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = HI * UNITE = m * SERIE CONTINUE = True * PAS DE TEMPS = None * UNITE DE TEMPS = None * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 8 * PREMIER PAS DE TEMPS = 2014-04-23 13:40:00 * DERNIER PAS DE TEMPS = 2014-08-30 00:00:00 ************************************* Cas de données d'observation météorologique >>> f = 'data/data/sandre/dataobs_meteo.xml' >>> series = read_Sandre(filename=f, datatype='data_obs_meteo') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = obs * NOMBRE DE SERIES = 1 * ---------------------------------- * SERIE #1 * - CODE = 23209001 * - VARNAME = PH * - META = None ************************************* >>> series[('23209001', 'PH', None)] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = PH * INTITULE VARIABLE = Précipitation horaire * IDENTIFIANT = 23209001 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = PH * UNITE = mm * SERIE CONTINUE = True * PAS DE TEMPS = 1:00:00 * UNITE DE TEMPS = hour * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 19 * PREMIER PAS DE TEMPS = 2016-04-15 12:00:00 * DERNIER PAS DE TEMPS = 2016-04-16 06:00:00 ************************************* Cas de données hydrométriques (Q) >>> f = 'data/data/sandre/dataobs_hydro_Q.xml' >>> series = read_Sandre(filename=f, datatype='data_obs_hydro') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = obs * NOMBRE DE SERIES = 1 * ---------------------------------- * SERIE #1 * - CODE = K5183020 * - VARNAME = QH * - META = None ************************************* >>> series[('K5183020', 'QH', None)] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = QH * INTITULE VARIABLE = Débit horaire * IDENTIFIANT = K5183020 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = QH * UNITE = m3/s * SERIE CONTINUE = True * PAS DE TEMPS = 1:00:00 * UNITE DE TEMPS = hour * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 47 * PREMIER PAS DE TEMPS = 2016-04-17 01:00:00 * DERNIER PAS DE TEMPS = 2016-04-18 23:00:00 ************************************* Cas de données hydrométriques (H) >>> f = 'data/data/sandre/dataobs_hydro_H.xml' >>> series = read_Sandre(filename=f, datatype='data_obs_hydro') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = obs * NOMBRE DE SERIES = 1 * ---------------------------------- * SERIE #1 * - CODE = K518302001 * - VARNAME = HH * - META = None ************************************* >>> series[('K518302001', 'HH', None)] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = HH * INTITULE VARIABLE = Hauteur horaire * IDENTIFIANT = K518302001 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = HH * UNITE = m * SERIE CONTINUE = True * PAS DE TEMPS = 1:00:00 * UNITE DE TEMPS = hour * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 47 * PREMIER PAS DE TEMPS = 2016-04-17 01:00:00 * DERNIER PAS DE TEMPS = 2016-04-18 23:00:00 ************************************* Cas de données hydrométriques élaborées (Q) >>> f = 'data/data/sandre/K0550010_198009060000_198010060000_Q_obs.xml' >>> series = read_Sandre(filename=f, datatype='data_obs_hydro') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = obs * NOMBRE DE SERIES = 1 * ---------------------------------- * SERIE #1 * - CODE = K0550010 * - VARNAME = QJ * - META = None ************************************* >>> series[('K0550010', 'QJ', None)] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = QJ * INTITULE VARIABLE = Débit moyen journalier * IDENTIFIANT = K0550010 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = QJ * UNITE = m3/s * SERIE CONTINUE = True * PAS DE TEMPS = 1 day, 0:00:00 * UNITE DE TEMPS = days * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 31 * PREMIER PAS DE TEMPS = 1980-09-06 00:00:00 * DERNIER PAS DE TEMPS = 1980-10-06 00:00:00 ************************************* Cas de prévisions hydrométriques (Q) >>> f = 'data/data/sandre/spcmo.xml' >>> series = read_Sandre(filename=f, datatype='data_fcst_hydro') >>> series ************************************* ********** SERIES ******************* ************************************* * NOM DE LA COLLECTION = Sandre * TYPE DE COLLECTION = fcst * NOMBRE DE SERIES = 12 * ---------------------------------- * SERIE #1 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:00, 11gGRPd130, ctl, 10 * ---------------------------------- * SERIE #2 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:00, 11gGRPd130, ctl, 50 * ---------------------------------- * SERIE #3 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:00, 11gGRPd130, ctl, 90 * ---------------------------------- * SERIE #4 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:04, 11gGRPd130, mem, 10 * ---------------------------------- * SERIE #5 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:04, 11gGRPd130, mem, 50 * ---------------------------------- * SERIE #6 * - CODE = Y2100020 * - VARNAME = QH * - META = 2019-11-19 06:00:04, 11gGRPd130, mem, 90 * ---------------------------------- * SERIE #7 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, ctl, 10 * ---------------------------------- * SERIE #8 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, ctl, 50 * ---------------------------------- * SERIE #9 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, ctl, 90 * ---------------------------------- * SERIE #10 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, mem, 10 * ---------------------------------- * SERIE #11 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, mem, 50 * ---------------------------------- * SERIE #12 * - CODE = Y2100020 * - VARNAME = QI * - META = 2019-11-19 05:55:00, 11sPLA0001, mem, 90 ************************************* >>> series[('Y2100020', 'QH', ... (dt(2019, 11, 19, 6), '11gGRPd130', 'ctl', '50'))] ************************************* *********** SERIE ******************* ************************************* * NOM VARIABLE SPC = QH * INTITULE VARIABLE = Débit horaire * IDENTIFIANT = Y2100020_2019111906_11gGRPd130_ctl_50 * FOURNISSEUR = Provider(name='Sandre') * NOM VARIABLE = QH * UNITE = m3/s * SERIE CONTINUE = True * PAS DE TEMPS = 1:00:00 * UNITE DE TEMPS = hour * FUSEAU HORAIRE = UTC * PROFONDEUR SERIE = 7 * PREMIER PAS DE TEMPS = 2019-11-22 00:00:00 * DERNIER PAS DE TEMPS = 2019-11-22 06:00:00 ************************************* """ # ------------------------------------------------------------------------- # 0- Contrôles # ------------------------------------------------------------------------- _exception.check_str(filename) _exception.check_str(datatype) if codes is not None: _exception.check_listlike(codes) if flowmes is not None: raise NotImplementedError( "La donnée de type 'flowmes' n'est pas implémentée") # ------------------------------------------------------------------------- # 1- Méta-données # ------------------------------------------------------------------------- if datatype in SandreMeta.get_types(): sandre = SandreMeta(filename=filename) try: content = sandre.read() except ValueError: return None # --------------------------------------------------------------------- # 1.1- Locations # --------------------------------------------------------------------- if datatype.startswith('loc'): return _sandre_locations(content, codes=codes) # --------------------------------------------------------------------- # 1.2- RatingCurves # --------------------------------------------------------------------- if datatype.startswith('ratingcurve'): return _sandre_ratingcurves(content, codes=codes, levelcor=levelcor, flowmes=flowmes) # --------------------------------------------------------------------- # 1.3- User # --------------------------------------------------------------------- if datatype.startswith('user'): return _sandre_user(content) # ------------------------------------------------------------------------- # 2- Séries de données # ------------------------------------------------------------------------- if datatype in Sandre.get_types(): sandre = Sandre(filename=filename) content = sandre.read() # --------------------------------------------------------------------- # 2.1- Series - levelcor # --------------------------------------------------------------------- if datatype == 'levelcor': return _sandre_levelcor(content, codes=codes, warning=warning) # --------------------------------------------------------------------- # 2.2- Series - data_obs_meteo # --------------------------------------------------------------------- if datatype == 'data_obs_meteo': return _sandre_dataobsmeteo(content, codes=codes, warning=warning) # --------------------------------------------------------------------- # 2.3- Series - data_obs_hydro # --------------------------------------------------------------------- if datatype == 'data_obs_hydro': return _sandre_dataobshydro(content, codes=codes, warning=warning) # --------------------------------------------------------------------- # 2.4- Series - data_fcst_hydro # --------------------------------------------------------------------- if datatype == 'data_fcst_hydro': return _sandre_datafcsthydro( content, codes=codes, models=models, scens=scens, warning=warning) raise ValueError(f"Type de données '{datatype}' incorrect")
def _sandre_datafcsthydro(content, codes=None, models=None, scens=None, warning=False): """ Convertir un contenu Sandre en une collection de prévisions hydro """ series = Series(datatype='fcst', name='Sandre') for s in content.simulations: if s.grandeur not in VARNAMES: continue if isinstance(codes, list) and s.entite.code not in codes: continue if isinstance(models, list) and s.modeleprevision.code not in models: continue df = s.previsions_tend.drop('incertdte', axis=1).reset_index( level=['tend']).pivot(columns='tend') df.columns = df.columns.droplevel([0]) df = df.reindex(columns=list(TRENDS.keys())) df.columns = [TRENDS[c] for c in df.columns] df = df.reindex(columns=sorted(df.columns)) df = df * RATIO_UNITS[s.grandeur] target = Parameter.infer_timestep(index=df.index, prefix=s.grandeur) param = Parameter.find(prefix=s.grandeur, timedelta=target) if s.commentaire is None: comment = ComSimul(s.dtprod, 'scen') else: comment = Sandre.process_comsim(s.commentaire) if not isinstance(comment.runtime, dt): comment = comment._replace(runtime=s.dtprod) if isinstance(scens, list) and comment.code not in scens: continue for c in df.columns: key = (s.entite.code, param.spc_varname, (comment.runtime, s.modeleprevision.code, comment.code, c)) keystr = str2tuple(tuple2str(key), forceobs=True)[0] serie = Serie(df[c].to_frame(), code=keystr, varname=param.spc_varname, provider='Sandre', warning=warning) if key in series: series[key].update(serie) else: series.add(serie=serie, code=s.entite.code, meta=key[2]) return series def _sandre_dataobshydro(content, codes=None, warning=False): """ Convertir un contenu Sandre en une collection de séries hydro .. versionchanged:: 3.0.2 Si une balise XML ne contient qu'une valeur, ajout d'une tentative pour la raccrocher à une série existente """ series = Series(datatype='obs', name='Sandre') for s in content.serieshydro: if s.grandeur not in VARNAMES: continue if isinstance(codes, list) and s.entite.code not in codes: continue df = s.observations['res'].to_frame() df = df * RATIO_UNITS[s.grandeur] target = Parameter.infer_timestep(index=df.index, prefix=s.grandeur) if s.grandeur in ['H', 'Q']: target = _infer_timestep_HQ(s, df, target, series) param = Parameter.find(prefix=s.grandeur, timedelta=target) df.columns = [param.spc_varname] serie = Serie(df, code=s.entite.code, varname=param.spc_varname, provider='Sandre', warning=warning) key = (s.entite.code, param.spc_varname, None) if key in series: series[key].update(serie) else: series.add(serie) for s in content.seriesobselab: if s.typegrd not in ELABVARNAMES: continue if isinstance(codes, list) and s.entite.code not in codes: continue df = s.observations['res'].to_frame() df = df * RATIO_UNITS[s.typegrd[0]] target = Parameter.infer_timestep(index=df.index, prefix=s.typegrd[0]) param = Parameter.find(prefix=s.typegrd[0], timedelta=target) df.columns = [param.spc_varname] serie = Serie(df, code=s.entite.code, varname=param.spc_varname, provider='Sandre', warning=warning) # Grandeur élaborée en tant que 'simulation' # key = (s.entite.code, param.spc_varname, None) key = (s.entite.code, param.spc_varname, s.typegrd) if key in series: series[key].update(serie) else: # Grandeur élaborée en tant que 'simulation' # series.add(serie) series.add(serie, meta=s.typegrd) return series def _infer_timestep_HQ(s, df, target, series): """Détermination pas de temps si H, Q.""" if isinstance(target, td) and target < td(hours=1): return None if len(df.index) == 1: # .. versionchanged:: 3.0.4 # Si période de recherche n'est pas horaire, force à None if s.dtdeb.minute != 0 or s.dtfin.minute != 0: return None # .. versionchanged:: 3.0.2 # Si le tableau de données ne contient qu'une valeur # Test si grandeur déjà présente # Sinon, test si grandeur instantanée présente t1 = Parameter.find(prefix=s.grandeur, timedelta=target) k1 = (s.entite.code, t1.spc_varname, None) # .. versionchanged:: 3.0.4 # Si heure ronde, force à horaire if k1 not in series and df.index[0].minute == 0: t2 = Parameter.find(prefix=s.grandeur, timedelta=td(hours=1)) k2 = (s.entite.code, t2.spc_varname, None) if k2 in series: return td(hours=1) if k1 not in series: t2 = Parameter.find(prefix=s.grandeur, timedelta=None) k2 = (s.entite.code, t2.spc_varname, None) if k2 in series: return None return target def _sandre_dataobsmeteo(content, codes=None, warning=False): """ Convertir un contenu Sandre en une collection de séries météo """ series = Series(datatype='obs', name='Sandre') for s in content.seriesmeteo: if s.grandeur.typemesure not in VARNAMES: continue if isinstance(codes, list) and \ s.grandeur.sitemeteo.code[-8:] not in codes: continue prefix = ASSOC[s.grandeur.typemesure] df = s.observations['res'].to_frame() df = df * RATIO_UNITS[s.grandeur.typemesure] target = Parameter.infer_timestep( index=df.index, prefix=prefix, default=s.duree) param = Parameter.find(prefix=prefix, timedelta=target) df.columns = [param.spc_varname] serie = Serie(df, code=s.grandeur.sitemeteo.code[-8:], varname=param.spc_varname, provider='Sandre', warning=warning) key = (serie.code, serie.spc_varname, None) if key in series: series[key].update(serie) else: series.add(serie) # series.add(serie=serie) return series def _sandre_levelcor(content, codes=None, warning=False): """ Convertir un contenu Sandre en une collection de courbes de correction """ series = Series(datatype='obs', name='Sandre') prefix = 'H' for s in content.courbescorrection: if isinstance(codes, list) and s.station.code not in codes: continue df = pnd.DataFrame( {'h': [p.deltah * RATIO_UNITS[prefix] for p in s.pivots]}, index=[p.dte for p in s.pivots] ) target = Parameter.infer_timestep(index=df.index, prefix=prefix) param = Parameter.find(prefix=prefix, timedelta=target) serie = Serie(df, code=s.station.code, varname=param.spc_varname, provider='Sandre', warning=warning) series.add(serie=serie, meta='levelcor') return series def _sandre_locations(content, codes=None): """ Convertir un contenu Sandre en une collection de lieux de mesure """ locs = Locations(name='Sandre') for s in content.sitesmeteo: if isinstance(codes, list) and s.code[-8:] not in codes: continue loc = Location( code=s.code[-8:], name=s.libelle, longname=s.libelleusuel, loctype="point", x=s.coord.x, y=s.coord.y, z=s.altitude.altitude) locs.add(loc) for s in content.siteshydro: if s.libelle is not None and ( codes is None or ( isinstance(codes, list) and s.code in codes)): x, y = _parse_coord(s.coord) loc = Location( code=s.code, name=s.libelleusuel, longname=s.libelle, river=_parse_river(s), area=_parse_bvtopo(s.bvtopo), locality=_parse_locs(s.communes), loctype="basin", x=x, y=y, z=_parse_alti(s.altitude)) locs.add(loc) for s2 in s.stations: if s2.libelle is not None and ( codes is None or ( isinstance(codes, list) and s2.code in codes)): x, y = _parse_coord(s2.coord) loc = Location( code=s2.code, name=s2.libellecomplement, longname=s2.libelle, river=_parse_river(s2), locality=_parse_locs(s2.commune), loctype="point", x=x, y=y) locs.add(loc) for s3 in s2.capteurs: if isinstance(codes, list) and s3.code not in codes: continue x, y = _parse_coord(s2.coord) loc = Location( code=s3.code, name=s3.libelle, longname=s3.libelle, river=_parse_river(s3), locality=_parse_locs(s2.commune), loctype="point", x=x, y=y) locs.add(loc) return locs def _parse_alti(alti): """ Analyser la surface de bassin Sandre Parameters ---------- alti : libhydro.core._composant_site.Altitude Altitude du lieu Returns ------- alti : float Altitude du lieu, -1 si non défini """ if alti is None: return -1 return alti.altitude def _parse_bvtopo(bvtopo): """ Analyser la surface de bassin Sandre Parameters ---------- bvtopo : float, None Surface du bassin Returns ------- area : float Surface du bassin. -1 si non défini """ if bvtopo is None: return -1 return bvtopo def _parse_coord(coord): """ Analyser les coordonnées Sandre Parameters ---------- coord : libhydro.core._composant_site.Coord Coordonnées géographiques Returns ------- x : float Coordonnée X. -1 si non défini y : float Coordonnée Y. -1 si non défini """ if coord is None: return -1, -1 return coord.x, coord.y def _parse_locs(locs): """ Analyser les communes Sandre Parameters ---------- locs : list, libhydro.core._composant_site.Commune Commune ou liste de communes Sandre Returns ------- """ if isinstance(locs, list): return ','.join([loc.code for loc in locs]) if not locs: return None return locs.code def _parse_river(label): """ Analyser le nom du cours d'eau Parameters ---------- label : str, None Libellé de l'entité Returns ------- river : str Libellé du cours d'eau. '' si non défini """ if label is None: return '' try: return label.libelle.split('à')[0].strip() except AttributeError: return '' def _sandre_ratingcurves(content, codes=None, levelcor=None, flowmes=None): """ Convertir un contenu Sandre en une collection de courbes de tarage """ curves = RatingCurves(name='Sandre') dh = 10 for s in content.courbestarage: # --------------------------------------------------------------------- # 1- Information de la courbe de tarage # --------------------------------------------------------------------- if isinstance(codes, list) and s.station.code not in codes: continue if s.limiteinf is None or s.limitesup is None: valid_interval = None else: valid_interval = (s.limiteinf * RATIO_UNITS['H'], s.limitesup * RATIO_UNITS['H']) valid_dt = (s.periodes[0].dtdeb, s.periodes[0].dtfin) hq = [(h * RATIO_UNITS['H'], int(s.debit(h)) * RATIO_UNITS['Q']) for h in range(int(s.limiteinf + 0.5), int(s.limitesup - 0.5), dh)] # --------------------------------------------------------------------- # 2- Information de la courbe de correction # --------------------------------------------------------------------- df_lc = None if levelcor is not None: df_lc = read_Sandre( filename=levelcor, datatype='levelcor', codes=[s.station.code]) if len(df_lc) > 0: df_lc = df_lc.concat() df_lc.columns = [c[1][0].lower() for c in df_lc.columns] else: df_lc = None # --------------------------------------------------------------------- # 3- Courbe de tarage # --------------------------------------------------------------------- curve = RatingCurve( code=s.station.code, num=s.libelle, provider='PHyC', valid_dt=valid_dt, valid_interval=valid_interval, update_dt=s.dtmaj, hq=hq, levelcor=df_lc, flowmes=flowmes) curves.add(curve) return curves def _sandre_user(content): """ Convertir un contenu Sandre en une configuration contenant les informations, soita administratives, soit des lieux """ config = Config() for org in content.intervenants: for contact in org.contacts: config.setdefault(contact.code, collections.OrderedDict()) config[contact.code]['code'] = contact.code config[contact.code]['name'] = contact.nom config[contact.code]['firstname'] = contact.prenom config[contact.code]['orgcode'] = org.code config[contact.code]['orgname'] = org.mnemo config[contact.code]['orglongname'] = org.nom config[contact.code]['email'] = contact.mel config[contact.code]['title'] = contact.civilite config[contact.code]['update_dt'] = contact.dtmaj config[contact.code]['start_dt'] = contact.dtactivation config[contact.code]['end_dt'] = contact.dtdesactivation config[contact.code]['profile'] = contact.profil config[contact.code]['profilestr'] = contact.profilasstr config[contact.code]['profileadminnat'] = contact.profiladminnat config[contact.code]['profilemodel'] = contact.profilmodel config[contact.code]['profileinst'] = contact.profilinst config[contact.code]['profilepub'] = contact.profilpublic for s in content.sitesmeteo: for r in s.roles: config.update(_sandre_rolehydro(r, s.code[-8:])) for s in content.siteshydro: for r in s.roles: config.update(_sandre_rolehydro(r, s.code)) for s2 in s.stations: for r in s2.roles: config.update(_sandre_rolehydro(r, s2.code)) return config def _sandre_rolehydro(r, loc): """ """ data = collections.OrderedDict() k = '_'.join([loc, r.contact.code, r.role]) data.setdefault(k, collections.OrderedDict()) data[k]['contactcode'] = r.contact.code data[k]['loc'] = loc data[k]['role'] = r.role data[k]['label'] = str(r).split('(')[1].split(')')[0] data[k]['update_dt'] = r.dtmaj data[k]['start_dt'] = r.dtdeb data[k]['end_dt'] = r.dtfin return data