"""
pyProm: Copyright 2018.
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 Saddle
type location objects.
"""
from .spot_elevation import SpotElevationContainer
from ..logic.internal_saddle_network import InternalSaddleNetwork
from ..logic.tuple_funcs import highest
from ..locations.saddle import Saddle, isSaddle
from ..locations.gridpoint import GridPoint
from ..logic.shortest_path_by_points import find_closest_points
[docs]class SaddlesContainer(SpotElevationContainer):
"""
Container for Saddles.
Allows for various list transformations.
"""
[docs] def __init__(self, saddleList):
"""
:param saddleList: list of Saddle objects to reside in this container.
:type saddleList: list(:class:`pyprom.lib.locations.saddle.Saddle`)
"""
if len([x for x in saddleList if not isinstance(x, Saddle)]):
raise TypeError("saddleList passed to SaddlesContainer"
" can only contain Saddle objects.")
super(SaddlesContainer, self).__init__(saddleList)
[docs] def rebuildSaddles(self, datamap):
"""
Uses the saddles contained in this container and rebuilds any saddle
which contains >= 2 high edges as (n-1) new saddles where n
is the number of high edges. The old saddle is tossed out unless it
is an edge effect saddle, in which it is kept and disqualified.
Saddles with 2 high edges are added back in. This in effect deals
with waterbodies and other similiar features.
:param datamap: datamap to use while rebuilding.
:type datamap: :class:`pyprom.lib.datamap.DataMap`
:return: New SaddlesContainer
:rtype: :class:`SaddlesContainer`
"""
new_saddles = list()
for saddle in self.points:
# insufficient highEdges. Re-add to the list an move on.
if len(saddle.highShores) < 2:
new_saddles.append(saddle)
continue
# More than 2 high shores? build the network.
if len(saddle.highShores) > 2:
nw = InternalSaddleNetwork(saddle, datamap)
new_saddles += nw.generate_child_saddles()
# if we've just got 2 high shores, find all the highest points in
# the highShores, and find the midpoint between the first two if
# its a multipoint
if len(saddle.highShores) == 2:
highShores = []
for highShore in saddle.highShores:
highShores.append(highest(highShore))
# if multipoint use first of each of the highest high shores
# and find the mid point for both. Then find the point within
# the multipoint that is closest to that midpoint. Disregard
# high shores.
if saddle.multipoint:
hs0, hs1, distance =\
find_closest_points(saddle.highShores[0], saddle.highShores[1], datamap)
# find the middle GP for the 2 closest opposing shore
# points.
# Note, in some cases this might be outside the multipoint
middleGP = GridPoint(int((hs0[0] +
hs1[0]) / 2),
int((hs0[1] +
hs1[1]) / 2),
saddle.elevation)
# reconcile any points which might be outside the
# multipoint by finding the closest point inside the
# multipoint.
middleSpotElevation =\
saddle.multipoint.closestPoint(middleGP,
asSpotElevation=True)
newSaddle = Saddle(middleSpotElevation.latitude,
middleSpotElevation.longitude,
middleSpotElevation.elevation)
# if not multipoint, just use that point.
else:
newSaddle = Saddle(saddle.latitude,
saddle.longitude,
saddle.elevation)
newSaddle.highShores = [highShores[0], highShores[1]]
new_saddles.append(newSaddle)
if saddle.edgeEffect:
newSaddle.parent = saddle
saddle.children.append(newSaddle)
# If its an edgeEffect, we need to disqualify it and stash that
# away for later.
if saddle.edgeEffect:
saddle.disqualified = True
new_saddles.append(saddle)
return SaddlesContainer(new_saddles)
@property
def saddles(self):
"""
Getter alias for self.points
:return: all Saddles in the container
:rtype: list
"""
return self.points
[docs] def append(self, saddle):
"""
Append a :class:`pyprom.lib.locations.saddle.Saddle` to this container.
:param saddle: Saddle to append.
:type saddle: :class:`pyprom.lib.locations.saddle.Saddle`
:raises: TypeError if not of type
:class:`pyprom.lib.locations.saddle.Saddle`
"""
isSaddle(saddle)
self.points.append(saddle)
self.fast_lookup[saddle.id] = saddle
[docs] def extend(self, saddles):
"""
Extend a list of :class:`pyprom.lib.locations.saddle.Saddle`
to this container.
:param saddles: list of Saddles to append.
:type list(saddles):
list(:class:`pyprom.lib.locations.saddle.Saddle`)
:raises: TypeError if point not of
:class:`pyprom.lib.locations.saddle.Saddle`
"""
for sa in saddles:
isSaddle(sa)
self.points.extend(saddles)
for sa in saddles:
self.fast_lookup[sa.id] = sa
[docs] def to_dict(self):
"""
:return: dict() representation of :class:`SaddlesContainer`
"""
return {'saddles': [x.to_dict() for x in self.points]}
[docs] @classmethod
def from_dict(cls, saddleContainerDict, datamap=None):
"""
Load this object and child objects from a dict.
:param saddleContainerDict: dict() representation of this object.
:param datamap: datamap which MultiPoint style Saddles use.
:type datamap: :class:`Datamap` object.
:return: New SaddlesContainer
:rtype: :class:`SaddlesContainer`
"""
saddles = []
for saddle in saddleContainerDict['saddles']:
saddleObj = Saddle.from_dict(saddle, datamap)
saddles.append(saddleObj)
sc = cls(saddles)
for saddle in saddleContainerDict['saddles']:
sid = saddle['id']
children = saddle.get('children', None)
if children:
for child in children:
sc.by_id(sid).children.append(sc.by_id(child))
parent = saddle.get('parent', None)
if parent:
sc.by_id(sid).parent = sc.by_id(parent)
basinSaddleAlternatives =\
saddle.get('basinSaddleAlternatives', None)
if basinSaddleAlternatives:
for bsa in basinSaddleAlternatives:
sc.by_id(sid).basinSaddleAlternatives.append(
sc.by_id(bsa))
return sc
@property
def disqualified(self):
"""
:return: list of all disqualified
:class:`pyprom.lib.locations.saddle.Saddle` in this container.
:rtype: list(:class:`pyprom.lib.locations.saddle.Saddle`)
"""
return [x for x in self.points if x.disqualified]
@property
def multipoints(self):
"""
Returns list of all multipoint
:class:`pyprom.lib.locations.saddle.Saddle` within container
:return: list(:class:`pyprom.lib.locations.saddle.Saddle`)
"""
return [pt for pt in self.points if pt.multipoint]
[docs] def __repr__(self):
"""
:return: String representation of this object
"""
return "<SaddlesContainer> {} Objects".format(len(self.points))
[docs] def __setitem__(self, idx, saddle):
"""
Gives :class:`SaddlesContainer` list like set capabilities
:param int idx: index
:param saddle: Saddle object to add.
:type saddle: :class:`pyprom.lib.locations.saddle.Saddle`
"""
isSaddle(saddle)
self.points[idx] = saddle
__unicode__ = __str__ = __repr__