#!/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/>.
#
########################################################################
"""
Objets natifs et convention de pyspc - Lieu(x) de observation/prévision
"""
import collections
import os.path
from pyspc.core.config import Config
import pyspc.core.exception as _exception
from .convention import LOCTYPE
[docs]
class Location():
"""
Lieu des séries
Attributes
----------
code : str
Code du lieu
name : str
Nom du lieu
longname : str
Nom complet du lieu
river : str
Nom du cours d'eau
locality : list
Liste des codes des communes associées
loctype : str
Type de lieu
reach : Reach, str, list
Tronçon de vigilance
x : float
Coordonnée X
y : float
Coordonnée Y
z : float
Altitude du lieu
area : float
Surface du bassin
"""
[docs]
def __init__(self, code=None, name=None, longname=None, river=None,
loctype=None, reach=None, locality=None,
x=-1, y=-1, z=-1, area=-1):
"""
Lieu des séries
Parameters
----------
code : str
Code du lieu
name : str
Nom du lieu
longname : str
Nom complet du lieu
river : str
Nom du cours d'eau
locality : list
Liste des codes des communes associées
loctype : str
Type de lieu
reach : Reach, str
Tronçon de vigilance
x : float
Coordonnée X
y : float
Coordonnée Y
z : float
Altitude du lieu
area : float
Surface du bassin
"""
import pyspc.core.reach as _reach
self._code = code
self._name = name
self._longname = longname
self._river = river
self._locality = locality
if loctype in self.get_loctypes():
self._loctype = loctype
else:
raise ValueError('Type de lieu inconnu')
self._x = float(x)
self._y = float(y)
self._z = float(z)
self._area = float(area)
if isinstance(reach, (_reach.Reach, list, str)) or reach is None:
self._reach = reach
else:
raise ValueError('Tronçon incorrect')
def __str__(self):
"""
Afficher des méta-données de l'instance Location
"""
text = """
*************************************
*********** LOCATION ****************
*************************************
* CODE LIEU = {_code}
* NOM LIEU = {_name}
* NOM COMPLET LIEU = {_longname}
* COURS D'EAU = {_river}
* TYPE LIEU = {_loctype}
* COORDONNEES X = {_x:10.2f} m
* COORDONNEES Y = {_y:10.2f} m
* ALTITUDE LIEU = {_z:10.2f} m NGF
* SURFACE LIEU = {_area:10.2f} km2
* COMMUNES = {_locality}
* TRONCONS = {_reach}
*************************************
"""
return text.format(**vars(self))
@property
def area(self):
"""Surface du bassin"""
return self._area
@property
def code(self):
"""Code du lieu"""
return self._code
@code.setter
def code(self, code):
"""Définir l'identifiant du lieu"""
self._code = code
@property
def locality(self):
"""Codes des communes associées"""
return self._locality
@property
def loctype(self):
"""Type de lieu"""
return self._loctype
@property
def longname(self):
"""Nom complet du lieu"""
return self._longname
@property
def name(self):
"""Nom du lieu"""
return self._name
@property
def reach(self):
"""Tronçon de vigilance"""
return self._reach
@property
def river(self):
"""Nom du cours d'eau"""
return self._river
@property
def x(self):
"""Coordonnée X"""
return self._x
@property
def y(self):
"""Coordonnée Y"""
return self._y
@property
def z(self):
"""Altitude du lieu"""
return self._z
[docs]
@classmethod
def get_loctypes(cls):
"""
Liste des types de lieu
Returns
-------
list
Types de lieu
"""
return sorted(LOCTYPE)
[docs]
class Locations(collections.OrderedDict):
"""
Classe des collections de lieux Location
Attributes
----------
name : str
Nom de la collection
codes : list
Codes des lieux
"""
[docs]
def __init__(self, name='locations'):
"""
Initialiser l'instance de Locations
Parameters
----------
name : str
Nom de la collection
"""
super().__init__()
self._name = name
self._codes = []
def __str__(self):
"""
Afficher les méta-données de l'instance Locations
"""
strlocs = ''
if len(self.keys()) > 0:
counter = 0
for c in self.codes:
counter += 1
strlocs += '\n * ----------------------------------'
strlocs += f'\n * LIEU #{counter}'
strlocs += f'\n * - CODE = {c}'
text = """
*************************************
*********** LOCATIONS ***************
*************************************
* NOM DE LA COLLECTION = {_name}
* NOMBRE DE LIEUX = {length} {strlocs}
*************************************
"""
return text.format(
**vars(self), length=len(self.keys()), strlocs=strlocs)
@property
def codes(self):
"""Codes des lieux"""
return self._codes
@property
def name(self):
"""Nom de la collection"""
return self._name
[docs]
def add(self, loc=None, code=None, overwrite=False):
"""
Ajouter un lieu hydrologique dans la collection
Parameters
----------
code : str
Code du lieu
loc : Location
Instance du lieu
overwrite : bool
Écraser la donnée existante ? défaut: False
"""
_exception.raise_valueerror(
not isinstance(loc, Location),
'Location incorrecte'
)
if code is None:
code = loc.code
_exception.check_str(code)
if overwrite:
self[code] = loc
else:
self.setdefault(code, loc)
self.refresh_codes()
[docs]
def check_locs(self):
"""
Contrôler si la série est une instance Location
"""
return all([isinstance(v, Location) for k, v in self.items()])
[docs]
def refresh_codes(self):
"""
Rafraîchir la liste des codes
"""
self._codes = list(self.keys())
[docs]
@classmethod
def from_Config(cls, config=None):
"""
Créer une instance Locations
à partir d'un fichier de configuration ou d'une instance Config
Parameters
----------
config : str, Config
- Nom du fichier de configuration
- Instance pysoc.core.config.Config
"""
name = os.path.basename(os.path.splitext(config)[0])
locs = Locations(name=name)
if not isinstance(config, Config):
config = Config(filename=config)
config.read()
for section in config:
loc = Location(
code=section,
name=config[section].get('name', None),
longname=config[section].get('longname', None),
loctype=config[section].get('loctype', None),
reach=config[section].get('reach', None),
locality=config[section].get('locality', None),
x=float(config[section].get('x', -1)),
y=float(config[section].get('y', -1)),
z=float(config[section].get('z', -1)),
area=float(config[section].get('area', -1)),
)
locs.add(code=section, loc=loc)
return locs
[docs]
def to_Config(self, filename=None):
"""
Créer une instance Config à partir d'une instance Locations
Parameters
----------
filename : str
Nom du fichier associé à la collection. Si non défini, le nom du
fichier est défini à partir du nom de la collection
Returns
-------
Config
Instance pyspc.core.config.Config
"""
if filename is None:
filename = '{self.name}.txt'
_exception.check_str(filename)
config = Config(filename=filename)
for k, loc in self.items():
config.setdefault(k, collections.OrderedDict())
config[k]['code'] = k
config[k]['name'] = loc.name
config[k]['longname'] = loc.longname
config[k]['loctype'] = loc.loctype
config[k]['reach'] = loc.reach
config[k]['locality'] = loc.locality
config[k]['river'] = loc.river
config[k]['x'] = loc.x
config[k]['y'] = loc.y
config[k]['z'] = loc.z
config[k]['area'] = loc.area
return config