#!/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