"""
pyProm: Copyright 2016
This software is distributed under a license that is described in
the LICENSE file that accompanies it.
This library contains a class for storing Saddle data.
"""
from .spot_elevation import SpotElevation
from .base_gridpoint import BaseGridPoint
from ..containers.multipoint import MultiPoint
from ..containers.gridpoint import GridPointContainer
from ..containers.linker import isLinker
from ..util import randomString
[docs]class Saddle(SpotElevation):
"""
| Saddle object stores relevant saddle data.
| A Saddle is by definition a point, or set of equal height points
| (MultiPoint) which have at least 2 non contiguous sets of points
| around the Perimeter that are higher than the point or Multipoint.
| These are called "High Shores". A Saddle is a Child object of
| :class:`pyprom.lib.locations.spot_elevation.SpotElevation`
|
| Examples:
|
| Single Point Saddle:
| ``v------high shore``
| ``[0][2][0]``
| ``[0][1][0] [1] = Saddle``
| ``[0][3][0]``
| ``^------high shore``
|
| MultiPoint Saddle:
| ``v--------high shore``
| ``[0][2][0][0]``
| ``[0][1][1][0] [1][1] = Saddle``
| ``[0][3][0][0]``
| ``^-----high shore``
|
"""
[docs] def __init__(self, latitude, longitude, elevation, *args, **kwargs):
"""
:param latitude: latitude in dotted decimal
:type latitude: int, float
:param longitude: longitude in dotted decimal
:type longitude: int, float
:param elevation: elevation in meters
:type elevation: int, float
:param multiPoint: MultiPoint object
:type multiPoint: :class:`pyprom.lib.containers.multipoint.MultiPoint`,
None
:param highShores: list of GridPointContainers representing a highShore
:type highShores:
list(:class:`pyprom.lib.containers.gridPoint.GridPointContainer`)
:param bool edge: Does this :class:`Saddle` have an edge
Effect?
:param str id: kwarg for id
:param list children: list of child Saddles. These are :class:`Saddle`
derived from this :class:`Saddle`
:param bool singleSummit: kwarg for Saddles disqualified for being
linked to a Single Summit.
:param bool basinSaddle: kwarg for Saddles disqualified for being
a Basin Saddle.
:param bool disqualified: kwarg for a generic disqualified Saddle.
"""
super(Saddle, self).__init__(latitude, longitude,
elevation, *args, **kwargs)
self.multiPoint = kwargs.get('multiPoint', [])
self.highShores = kwargs.get('highShores', [])
self.id = kwargs.get('id', 'sa:' + randomString())
# List of linkers to summits
self.summits = []
# If this is set, this saddle was spun out of another
# Saddle with less data.
self.parent = None # Parent
# Saddles that have been spawned off of this one.
self.children = kwargs.get('children', [])
# All Edges lead to One summit.
self.singleSummit = kwargs.get('singleSummit', False)
# redundant saddle, but too low.
self.basinSaddle = kwargs.get('basinSaddle', False)
# alternative basin saddles
self.basinSaddleAlternatives = []
# Non specific disqualification
self._disqualified = kwargs.get('disqualified', None)
self.lprBoundary = []
[docs] def addSummitLinker(self, linker):
"""
Add a :class:`pyprom.lib.containers.linker.Linker` to this Saddle.
This in effect links a :class:`pyprom.lib.locations.summit.Summit`
to this Saddle.
:param linker: linker to add.
:type linker: :class:`pyprom.lib.containers.linker.Linker`
"""
isLinker(linker)
self.summits.append(linker)
[docs] def feature_neighbors(self):
"""
:return: returns all linked Summits.
This is, in effect, an interface.
:rtype: list(:class:`pyprom.lib.locations.summit.Summit`)
"""
return [feature.summit for feature in self.summits]
@property
def neighbors(self, filterDisqualified=True):
"""
neighbors will return all neighboring saddles by way of the
connected summit.
This function will filter out redundant neighbors.
:param bool filterDisqualified: Filter out disqualified linkers.
:return: list of unique neighboring saddles by way of
neighboring summits excluding self.
:rtype: list(:class:`Saddle`)
"""
neighborSet = set(self.all_neighbors(filterDisqualified))
neighborSet.discard(self)
return list(neighborSet)
[docs] def all_neighbors(self, filterDisqualified=True):
"""
all_neighbors will return all neighboring saddles by way of the
connected summit.
This function deliberately makes no effort to filter out redundant
neighbors.
:param bool filterDisqualified: Filter out disqualified linkers.
:return: list of neighboring saddles by way of neighboring summits.
:rtype: list(:class:`Saddle`)
"""
neighbors = []
if filterDisqualified:
[neighbors.extend(linker.saddles_connected_via_summit())
for linker in self.summits if not linker.disqualified]
else:
[neighbors.extend(linker.saddles_connected_via_summit(
skipDisqualified=False))
for linker in self.summits]
return neighbors
@property
def summits_set(self):
"""
:return: set of linked Summits
:rtype: set(:class:`pyprom.lib.locations.summit.Summit`)
"""
return set([x.summit for x in self.summits])
@property
def disqualified(self):
"""
:return: if any values that indicate disqualification are set,
return True.
:rtype: bool
"""
# Allow for a manual override from user.
if self._disqualified in [True, False]:
return self._disqualified
else:
return self.singleSummit | self.basinSaddle
@disqualified.setter
def disqualified(self, value):
"""
:param bool value: Override Saddle disqualification
"""
self._disqualified = value
[docs] def disqualify_self_and_linkers(self, basinSaddle=False,
singleSummit=False):
"""
Disqualify this :class:`Saddle` and linked
:class:`pyprom.lib.containers.linker.Linker` s
:param bool basinSaddle: set basinSaddle
:param bool singleSummit: set singleSummit
"""
if basinSaddle:
self.basinSaddle = basinSaddle
if singleSummit:
self.singleSummit = singleSummit
if not (basinSaddle | singleSummit):
self._disqualified = True
for linker in self.summits:
linker.disqualified = True
[docs] def to_dict(self, referenceById=True):
"""
Create the dictionary representation of this object.
:param bool referenceById: reference Summits by ID.
:return: dict() representation of :class:`Saddle`
:rtype: dict()
"""
to_dict = {'lat': self.latitude,
'lon': self.longitude,
'ele': self.elevation,
'edge': self.edgeEffect,
'edgepoints': [x.to_dict() for x in self.edgePoints],
'id': self.id}
if self.singleSummit:
to_dict['singlesummit'] = self.singleSummit
if self.basinSaddle:
to_dict['basinSaddle'] = self.basinSaddle
if self._disqualified:
to_dict['disqualified'] = self._disqualified
# TODO: lprBoundary will be needed someday.
if self.multiPoint:
to_dict['multipoint'] = self.multiPoint.to_dict()
if self.highShores:
to_dict['highShores'] = list()
for shore in self.highShores:
hs = shore.to_dict()
to_dict['highShores'].append(hs)
# These values are not unloaded by from_dict()
if referenceById:
to_dict['children'] =\
[x.id for x in self.children] # saddles by ID
to_dict['summits'] =\
[x.id for x in self.summits] # linker by ID
if self.parent:
to_dict['parent'] = self.parent.id
return to_dict
[docs] @classmethod
def from_dict(cls, saddleDict, datamap=None):
"""
Create this object from dictionary representation
:param dict saddleDict: dict representation of this object.
:param datamap: Datamap to build this object from.
:type datamap: :class:`pyprom.lib.datamap.DataMap`
:return: a new Saddle object.
:rtype: :class:`Saddle`
"""
lat = saddleDict['lat']
long = saddleDict['lon']
elevation = saddleDict['ele']
edge = saddleDict['edge']
edgePoints = [BaseGridPoint(pt['x'], pt['y'])
for pt in saddleDict['edgepoints']]
id = saddleDict['id']
singleSummit = saddleDict.get('singleSummit', False)
basinSaddle = saddleDict.get('basinSaddle', False)
disqualified = saddleDict.get('disqualified', None)
multipoint = saddleDict.get('multipoint', [])
if multipoint:
multipoint = MultiPoint.from_dict(multipoint, datamap=datamap)
highshores = saddleDict.get('highShores', [])
if highshores:
highshores = [GridPointContainer.from_dict(x) for x in highshores]
return cls(lat, long, elevation,
multiPoint=multipoint,
highShores=highshores,
edge=edge,
edgePoints=edgePoints,
id=id,
singleSummit=singleSummit,
basinSaddle=basinSaddle,
disqualified=disqualified)
[docs] def __hash__(self):
"""
hash takes into account the lat, long, and elevation of the saddle
As well as a hash of all the highShores. This is a costly calculation
and should probably be avoided if possible.
:return: Hash representation of this object
:rtype: str
"""
masterHash = super(SpotElevation, self).__hash__()
pointsTuple = tuple(self.highShores)
return hash((masterHash, hash(pointsTuple)))
[docs] def __repr__(self):
"""
:return: String representation of this object
"""
return "<Saddle> lat {} long {} {}ft {}m MultiPoint {}".format(
self.latitude,
self.longitude,
self.feet,
self.elevation,
bool(self.multiPoint))
__unicode__ = __str__ = __repr__
def isSaddle(saddle):
"""
Check if passed in object is a :class:`Saddle`
:param saddle: object under scrutiny
:raises: TypeError if other not of :class:`Saddle`
"""
if not isinstance(saddle, Saddle):
raise TypeError("Expected Saddle Object.")