"""
pyProm: Copyright 2016.
This software is distributed under a license that is described in
the LICENSE file that accompanies it.
This library contains a container class for storing SpotElevation
type location objects.
"""
from ..locations.summit import Summit
from ..locations.spot_elevation import isSpotElevation
from ..constants import METERS_TO_FEET, FEET_IN_MILES
from .base import _Base
from geopy.distance import vincenty
[docs]class SpotElevationContainer(_Base):
"""
Container for Spot Elevation type lists.
Allows for various list transformations.
This is intended to be inherited from, namely for:
:class:`pyprom.lib.containers.saddles.SaddlesContainer`,
:class:`pyprom.lib.containers.summits.SummitsContainer`,
and :class:`pyprom.lib.containers.runoffs.RunOffsContainer`
"""
[docs] def __init__(self, spotElevationList):
"""
:param spotElevationList: list of SpotElevation objects
which will reside in this container.
:type spotElevationList:
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
"""
super(SpotElevationContainer, self).__init__()
self.points = spotElevationList
self.fast_lookup = {point.id: point for point in self.points}
@property
def lowest(self):
"""
:return: list of lowest
:class:`pyprom.lib.locations.spot_elevation.SpotElevation` object(s)
found in this container
:rtype:
list(:class:`pyprom.lib.locations.spot_elevation.SpotElevation`)
"""
low = self.points[0].elevation
lowest = list()
for spot_elevation in self.points:
if spot_elevation.elevation < low:
low = spot_elevation.elevation
lowest = list()
lowest.append(spot_elevation)
elif spot_elevation.elevation == low:
lowest.append(spot_elevation)
return lowest
@property
def highest(self):
"""
:return: list of highest
:class:`pyprom.lib.locations.spot_elevation.SpotElevation` object(s)
found in this container
:rtype:
list(:class:`pyprom.lib.locations.spot_elevation.SpotElevation`)
"""
high = self.points[0].elevation
highest = list()
for spot_elevation in self.points:
if spot_elevation.elevation > high:
high = spot_elevation.elevation
highest = list()
highest.append(spot_elevation)
elif spot_elevation.elevation == high:
highest.append(spot_elevation)
return highest
[docs] def by_id(self, id):
"""
Returns member SpotElevation derivative by ID if it exists.
:param string id: string ID of SpotElevation derivative
:return: SpotElevation derivative object
"""
# We want to throw an exception if it's not there.
return self.fast_lookup[id]
[docs] def radius(self, lat, long, value, unit='m'):
"""
Returns all members of this container within a certain radius.
:param lat: latitude of center in dotted decimal
:type lat: float, int
:param long: longitude of center in dotted decimal
:type long: float, int
:param value: number of units of distance
:type value: float, int
:param str unit: type of unit (m, km, mi, ft)
:return: SpotElevationContainer loaded with results.
:rtype: :class:`SpotElevationContainer`
"""
unit = unit.lower()
# convert our units to meters so we only have to deal with one unit
# type.
if unit in ['meters', 'meter', 'm']:
convertedDist = value
elif unit in ['kilometers', 'kilometer', 'km']:
convertedDist = value * 1000
elif unit in ['feet', 'foot', 'ft']:
convertedDist = METERS_TO_FEET * value
elif unit in ['miles', 'mile', 'mi']:
convertedDist = METERS_TO_FEET * value * FEET_IN_MILES
else:
raise ValueError('No unit value specified')
positive = list()
# iterate through points and collect only points within the specified
# distance using the vincenty algorithm.
for point in self.points:
distance = vincenty((lat, long),
(point.latitude, point.longitude)).meters
if distance < convertedDist:
positive.append(point)
return self.__class__(positive)
[docs] def rectangle(self, lat1, long1, lat2, long2):
"""
Returns all members of this container in a rectangle of
(lat1, long1) - (lat2, long2)
:param lat1: latitude of point 1
:type lat1: float, int
:param long1: longitude of point 1
:type long1: float, int
:param lat2: latitude of point 2
:type lat2: float, int
:param long2: longitude of point 2
:type long2: float, int
:return: SpotElevationContainer loaded with all points within
(inclusive) the specified rectangle.
:rtype: :class:`SpotElevationContainer`
"""
upperlat = max(lat1, lat2)
upperlong = max(long1, long2)
lowerlat = min(lat1, lat2)
lowerlong = min(long1, long2)
return self.__class__(
[x for x in self.points if lowerlat < x.latitude < upperlat and
lowerlong < x.longitude < upperlong])
[docs] def elevationRange(self, lower=-100000, upper=100000):
"""
Returns all members of this container within a certain elevation
range in feet
:param lower: lower limit in feet
:type lower: int, float
:param upper: upper limit in feet
:type upper: int, float
:return: all points in range between lower and upper (exclusive)
:rtype: :class:`SpotElevationContainer`
"""
return self.__class__([x for x in self.points if
x.feet > lower and x.feet < upper])
[docs] def elevationRangeMetric(self, lower=-100000, upper=100000):
"""
Returns all members of this container within a certain elevation range
using meters
:param lower: lower limit in meters
:type lower: int, float
:param upper: upper limit in meters
:type upper: int, float
:return: all points in range between lower and upper (exclusive)
:rtype: :class:`SpotElevationContainer`
"""
return self.__class__([x for x in self.points if
upper > x.elevation > lower])
[docs] def to_dict(self):
"""
Create the dictionary representation of this object.
:return: dict() representation of :class:`SpotElevationContainer`
:rtype: dict()
"""
return {"spotelevations": [x for x in self.points.to_dict()]}
[docs] @classmethod
def from_dict(cls, spotElevationContainerDict, datamap=None):
"""
Create this object from dictionary representation
:param dict spotElevationContainerDict: dict() representation of
:class:`SpotElevationContainer`.
:param datamap: datamap which MultiPoint style SpotElevations use.
:type datamap: :class:`pyprom.lib.datamap.DataMap`
:return: a new SpotElevationContainer
:rtype: :class:`SpotElevationContainer`
"""
spotelevations = []
for spotelevation in spotElevationContainerDict['spotelevations']:
spotelevations.append(Summit.from_dict(spotelevation, datamap))
spotElevationContainer = cls(spotelevations)
return spotElevationContainer
[docs] def append(self, spotElevation):
"""
Append a :class:`pyprom.lib.locations.spot_elevation.SpotElevation`
to this container.
:param spotElevation: SpotElevation to append.
:type spotElevation:
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
:raises: TypeError if point not of
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
"""
isSpotElevation(spotElevation)
self.points.append(spotElevation)
self.fast_lookup[spotElevation.id] = spotElevation
[docs] def extend(self, spotElevations):
"""
Extend a list of :class:`pyprom.lib.locations.spot_elevation.SpotElevation`
to this container.
:param spotElevations: list of SpotElevations to append.
:type list(spotElevation):
list(:class:`pyprom.lib.locations.spot_elevation.SpotElevation`)
:raises: TypeError if point not of
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
"""
for se in spotElevations:
isSpotElevation(se)
self.points.extend(spotElevations)
for se in spotElevations:
self.fast_lookup[se.id] = se
[docs] def index(self, spotElevation):
"""
Returns the index that this
:class:`pyprom.lib.locations.spot_elevation.SpotElevation` or child
object occurs.
if none, return None
:param gridPoint: Gridpoint to find index of.
:type gridPoint:
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
:return: index in points list where this spotElevation exists
:rtype: int, None
"""
try:
return self.points.index(spotElevation)
except:
return None
[docs] def __len__(self):
"""
:return: number of items in `self.points`
:rtype: int
"""
return len(self.points)
[docs] def __repr__(self):
"""
:return: String representation of this object
"""
return "<SpotElevationContainer> {} Objects".format(self.__len__())
[docs] def __setitem__(self, idx, spotElevation):
"""
Gives :class:`SpotElevationContainer` list like set capabilities
:param int idx: index value
:param spotElevation: Spot Elevation object to set.
:type spotElevation:
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
:raises: TypeError if spotElevation not of
:class:`pyprom.lib.locations.spot_elevation.SpotElevation`
"""
isSpotElevation(spotElevation)
self.points[idx] = spotElevation
[docs] def __getitem__(self, idx):
"""
Gives :class:`SpotElevationContainer` list like get capabilities
:param int idx: index value
:return: :class:`pyprom.lib.locations.spot_elevation.SpotElevation`
self.point at idx
"""
return self.points[idx]
[docs] def __eq__(self, other):
"""
Determines if :class:`SpotElevationContainer` is equal to another.
:param other: other :class:`SpotElevationContainer` to check.
:type: :class:`SpotElevationContainer`
:return: equality
:rtype: bool
:raises: TypeError if other not of :class:`SpotElevationContainer`
"""
_isSpotElevationContainer(other)
return sorted([x for x in self.points]) == \
sorted([x for x in other.points])
[docs] def __ne__(self, other):
"""
Determines if :class:`SpotElevationContainer` is not equal to another.
:param other: other :class:`SpotElevationContainer` to check.
:type: :class:`SpotElevationContainer`
:return: equality
:rtype: bool
:raises: TypeError if other not of :class:`SpotElevationContainer`
"""
_isSpotElevationContainer(other)
return sorted([x for x in self.points]) != \
sorted([x for x in other.points])
__unicode__ = __str__ = __repr__
def _isSpotElevationContainer(spotElevationContainer):
"""
Check if passed in object is a :class:`SpotElevationContainer`
:param spotElevationContainer: object under scrutiny
:raises: TypeError if other not of :class:`SpotElevationContainer`
"""
if not isinstance(spotElevationContainer, SpotElevationContainer):
raise TypeError("SpotElevationContainer expected")