# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# This file is part of Polyfemos.
#
# Polyfemos is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or any later version.
#
# Polyfemos 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License and
# GNU General Public License along with Polyfemos. If not, see
# <https://www.gnu.org/licenses/>.'
#
# Author: Henrik Jänkävaara
# -----------------------------------------------------------------------------
"""
Contains classes for handling station information
:copyright:
2019, University of Oulu, Sodankyla Geophysical Observatory
:license:
GNU Lesser General Public License v3.0 or later
(https://spdx.org/licenses/LGPL-3.0-or-later.html)
"""
import itertools
import operator
from polyfemos.parser import typeoperator as to
from polyfemos.almanac.ordinal import Ordinal
[docs]def get_id(network_code, station_code):
"""
:type network_code: str
:param network_code:
:type station_code: str
:param station_code:
:rtype: str
:return: ``network_code + "." + station_code``, e.g. "FN.MSF"
"""
return "{}.{}".format(network_code, station_code)
[docs]class Station(object):
"""
Class for handling information of one station
"""
[docs] def __init__(self, network_code="", station_code="", location_code="",
locy=0., locx=0., epsg="", digitizer="", sensor="",
starttime="", endtime=""):
"""
:type network_code: str
:param network_code: Network code, e.g. "FN"
:type station_code: str
:param station_code: Code of the station, e.g. "MSF"
:type location_code: str
:param location_code: Location of the station, e.g. "00" or empty
:type locy: float
:param locy: Y coordinate
:type locx: float
:param locx: X coordinate
:type epsg: str
:param epsg: EPSG number for ``locx`` and ``locy`` coordinates
:type digitizer: str
:param digitizer: Model/name of the station's digitizer
:type sensor: str
:param sensor: Model/name of the station's sensor
:type starttime: :class:`~polyfemos.almanac.ordinal.Ordinal`
:param starttime:
:type endtime: :class:`~polyfemos.almanac.ordinal.Ordinal`
:param endtime:
"""
self.network_code = network_code
self.station_code = station_code
self.code = station_code
self.location_code = \
"" if to.isStrNaN(location_code) else location_code
self.locy = locy
self.locx = locx
self.epsg = epsg
self.digitizer = digitizer
self.sensor = sensor
if endtime.timestamp < starttime.timestamp:
starttime, endtime = endtime, starttime
self.starttime = starttime
self.endtime = endtime
self.parameters = []
self.id = self.get_id()
self.header = None
[docs] def get_id(self):
"""
:rtype: str
:return: e.g. "FN.MSF"
"""
return get_id(self.network_code, self.station_code)
[docs] def covers_time(self, t0):
"""
:type t0: :class:`~polyfemos.almanac.ordinal.Ordinal`
:param t0:
:rtype: bool
:return: ``True`` if ``t0`` is between ``self.starttime`` and
``self.endtime``
"""
return self.starttime.timestamp <= t0 <= self.endtime.timestamp
[docs] def add_parameter(self, parameter):
"""
:type parameter: :class:`~polyfemos.back.parameter.Parameter`
:param parameter:
"""
self.parameters.append(parameter)
[docs] def filter_parameters(self, **kwargs):
"""
Filters ``self.parameters`` according to given ``kwargs``.
The values of the kwargs should be list of values, or one value.
If given keyword argument is attribute of the
:class:`~polyfemos.back.parameter.Parameter` class,
and the given value(s)
are not equal to the the parameter, the parameter is excluded.
"""
for key, value in kwargs.items():
if not isinstance(value, list):
value = [value]
if len(value) <= 0 \
or all(map(to.isStrNaN, value)) \
or not any(value):
continue
self.parameters = \
[par for par in self.parameters if par[key] in value]
[docs] def _compare(self, op, other):
"""
Compares starttimes of the stations ``self`` and ``other``.
If station ids do not match, always returns ``False``.
:type op: func
:param op: comparison operation, e.g. :func:`~operator.lt`
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return:
"""
if self != other:
return False
return op(self.starttime.timestamp, other.starttime.timestamp)
[docs] def __lt__(self, other):
"""
see :meth:`~polyfemos.back.station.Station._compare` for more info
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return:
"""
return self._compare(operator.lt, other)
[docs] def __gt__(self, other):
"""
see :meth:`~polyfemos.back.station.Station._compare` for more info
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return:
"""
return self._compare(operator.gt, other)
[docs] def __le__(self, other):
"""
see :meth:`~polyfemos.back.station.Station._compare` for more info
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return:
"""
return self._compare(operator.le, other)
[docs] def __ge__(self, other):
"""
see :meth:`~polyfemos.back.station.Station._compare` for more info
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return:
"""
return self._compare(operator.ge, other)
[docs] def __eq__(self, other):
"""
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return: returns ``True`` if ids of ``self`` and ``other``
are identical
"""
return self.id == other.id
[docs] def __ne__(self, other):
"""
:type other: :class:`~polyfemos.back.station.Station`
:param other:
:rtype: bool
:return: opposite of :meth:`~polyfemos.back.station.Station.__eq__`
"""
return not self == other
[docs] def __str__(self):
"""
:rtype: str
:return: somekind of string to represent the station
"""
return "{:8}{:28}{:28}".format(self.id, self.starttime, self.endtime)
[docs] def __len__(self):
"""
:rtype: int
:return: How many parameters the station has
"""
return len(self.parameters)
[docs]class Stations(object):
"""
Class for containing and handling multiple
:class:`~polyfemos.back.station.Station`
instances
"""
[docs] def __init__(self):
"""
"""
self.__stations = {}
self.__last_id = ""
[docs] def add_station(self, station):
"""
Adds ``station`` to ``self.__stations``.
If successsfully added, the id of the ``station`` is saved as
``self.__last_id``. ``self.__last_id`` will always contain the is of
the last successfully added station.
:type station: :class:`~polyfemos.back.station.Station`
:param station:
:rtype: bool
:return: ``True`` if ``station`` was successfully added to the
``self.__stations``
"""
id_ = station.id
if id_ not in self.__stations:
self.__stations[id_] = []
else:
# Check if the given start or endtime are already present
# with the given id
prod = itertools.product(
self.__stations[id_],
[station.starttime, station.endtime]
)
if any(x[0].covers_time(x[1]) for x in prod):
return False
self.__stations[id_].append(station)
self.__last_id = id_
return True
[docs] def get_station(self, network_code, station_code, t0):
"""
:type network_code: str
:param network_code:
:type station_code: str
:param station_code:
:type t0: :class:`~polyfemos.almanac.ordinal.Ordinal`
:param t0:
:rtype: :class:`~polyfemos.back.station.Station`
:return: returns station instances matching the given ``network_code``
and ``station_code``, and including ``t0`` within it's time
interval
"""
id_ = get_id(network_code, station_code)
if id_ not in self.__stations:
return None
for station in self.__stations[id_]:
if station.covers_time(t0):
return station
return None
[docs] def add_parameter(self, parameter):
"""
Adds ``parameter`` to the last station with id ``self.__last_id``
:type parameter: :class:`~polyfemos.back.parameter.Parameter`
:param parameter:
"""
self.__stations[self.__last_id][-1].add_parameter(parameter)
if __name__ == "__main__":
station1 = Station(
station_code="D",
starttime=Ordinal(1970, 1, 1), endtime=Ordinal(1975, 1, 1))
station2 = Station(
station_code="D",
starttime=Ordinal(1975, 1, 1), endtime=Ordinal(2001, 1, 1))
station3 = Station(
station_code="D",
starttime=Ordinal(1970, 1, 1), endtime=Ordinal(2001, 1, 1))
stations = Stations()
print(stations.add_station(station1))
print(stations.add_station(station2))
print(stations.add_station(station3))
print(station1 < station2)
print(station1 <= station2)
print(station1 > station2)
print(station1 >= station2)
print(station1 < station3)
print(station1 <= station3)
print(station1 > station3)
print(station1 >= station3)
print(station1 == station2)
print(station1 == station3)
print(station1 != station2)
print(station1 != station3)