Source code for pyprom.lib.containers.multipoint

"""
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 Multipoint
type location objects as well as a number of functions.
"""

from math import hypot
from ..locations.base_coordinate import BaseCoordinate
from ..locations.base_gridpoint import  BaseGridPoint
from ..locations.gridpoint import GridPoint
from .perimeter import Perimeter


[docs]class MultiPoint: """ | A MultiPoint Container. This is a special kind of feature which contains | multiple :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint`s. | These points are all of equal elevation and neighbor each other | Orthogonally or Diagonally. | | [4][3][4][6][4] | [3][5][5][5][6] | [2][3][5][6][3] | [3][5][4][3] | [6][7][6] | | In the above example, all [5] points represent a valid MultiPoint. | All non [5] points are Perimeter Points, that is, they neighbor | MultiPoint members Diagonally or Orthogonally. | | ``[5][5][5]`` | ``[5] <- Without Perimeter`` | ``[5]`` | | | ``[4][3][4][6][4]`` | ``[3] [6] <- Just the Perimeter.`` | ``[2][3] [6][3]`` | ``[3] [4][3]`` | ``[6][7][6]`` """
[docs] def __init__(self, points, elevation, datamap, perimeter=None): """ :param points: list of :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` objects. These are the inside points that make up a Multipoint. :type points: list(:class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint`) :param elevation: elevation in meters :type elevation: int, float :param datamap: datamap which this MultiPoint uses. :type datamap: :class:`pyprom.lib.datamap.DataMap` :param perimeter: Perimeter of MultiPoint. These are the points that make up the orthogonally/diagonally connected border of the multipoint outside of the multipoint. :type perimeter: :class:`pyprom.lib.containers.perimeter.Perimeter` """ self.points = points # BaseGridPoint Objects. self.elevation = elevation self.datamap = datamap # data analysis object. self.perimeter = perimeter
[docs] def to_dict(self): """ Create the dictionary representation of this object. :return: dict() representation of :class:`MultiPoint` :rtype: dict() """ multipointDict = dict() multipointDict['points'] = self.points multipointDict['perimeter'] = self.perimeter.to_dict() multipointDict['elevation'] = self.elevation return multipointDict
[docs] @classmethod def from_dict(cls, multipointDict, datamap=None): """ Create this object from dictionary representation :param multipointDict: dict() representation of this :class:`MultiPoint`. :param datamap: datamap which this MultiPoint uses. :type datamap: :class:`pyprom.lib.datamap.DataMap` :return: a new Multipoint. :rtype: :class:`MultiPoint` """ points = [(pt[0], pt[1]) for pt in multipointDict['points']] perimeter = Perimeter.from_dict(multipointDict['perimeter'], datamap) elevation = multipointDict['elevation'] return cls(points, elevation, datamap, perimeter=perimeter)
@property def pointsLatLong(self): """ Returns list() of Container :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` as :class:`pyprom.lib.locations.base_coordinate.BaseCoordinate` :return: List of All blob points with lat/long instead of x/y :rtype: list(:class:`pyprom.lib.locations.base_coordinate.BaseCoordinate`) """ return [BaseCoordinate(*self.datamap.xy_to_latlong(coord[0], coord[1])) for coord in self.points]
[docs] def append(self, point): """ Add a BaseGridPoint to this container. :param point: BaseGridPoint or tuple to add. :type point: :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` :type point: tuple :raises: TypeError if point not of :class:`BaseGridPoint` or tuple :raises: Exception if tuple length != 2 """ incoming = self._check_and_return_incoming_point_type(point) self.points.append(incoming)
[docs] def closestPoint(self, gridPoint, asSpotElevation=False): """ Returns the closest point in this container to the GridPoint passed in. :param gridPoint: GridPoint to check. :type gridPoint: :class:`pyprom.lib.locations.gridpoint.GridPoint` :param bool asSpotElevation: If True, returns SpotElevation object. :return: GridPoint if asSpotElevation == False SpotElevation if asSpotElevation == True :rtype :class:`pyprom.lib.locations.gridpoint.GridPoint`, :class:`pyprom.lib.locations.spot_elevation.SpotElevation` """ distanceCalc = lambda themX, themY, usX, usY: hypot((themX - usX), (themY - usY)) themX, themY = gridPoint.x, gridPoint.y closestDistance = distanceCalc(themX, themY, *self.points[0]) closest = self.points[0] for point in self.points[1:]: distance = distanceCalc(themX, themY, point[0], point[1]) # well, can't get closer than that. mark it and bail. if distance == 0: closest = point break if distance < closestDistance: closest = point closestDistance = distance gp = GridPoint(closest[0], closest[1], self.elevation) if asSpotElevation: return gp.toSpotElevation(self.datamap) return gp
[docs] def points_with_elevation(self): """ Returns list of tuples of member points with (x, y, elevation) :return: list(tuple(x, y, ele))) """ return [(x[0], x[1], self.elevation) for x in self.points]
[docs] def __len__(self): """ :return: number of items in `self.points` :rtype: int """ return len(self.points)
[docs] def __setitem__(self, idx, point): """ Gives MultiPoint list like set capabilities :param int idx: index value :param point: BaseGridPoint or tuple for setitem. :type point: :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` :type point: tuple() """ incoming = self._check_and_return_incoming_point_type(point) self.points[idx] = incoming
[docs] def __getitem__(self, idx): """ Gives MultiPoint list like get capabilities :param int idx: index value :return: :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` self.point at idx """ return BaseGridPoint.from_tuple(self.points[idx])
[docs] def __eq__(self, other): """ Determines if this object is equal to another. :param other: other :class:`MultiPoint` to check. :type: :class:`MultiPoint` :return: equality :rtype: bool :raises: TypeError if other not of :class:`MultiPoint` """ _isMultiPoint(other) return sorted([x for x in self.points]) == \ sorted([x for x in other.points])
[docs] def __ne__(self, other): """ Determines if this object is not equal to another. :param other: other :class:`MultiPoint` to check. :type: :class:`MultiPoint` :return: inequality :rtype: bool :raises: TypeError if other not of :class:`MultiPoint` """ _isMultiPoint(other) return sorted([x for x in self.points]) != \ sorted([x for x in other.points])
[docs] def __iter__(self): """ :return: `self.points` as iterator """ for point in self.points: yield point
[docs] def __repr__(self): """ :return: String representation of this object """ return "<Multipoint> elevation(m): {}, points {}". \ format(self.elevation, len(self.points))
[docs] def _check_and_return_incoming_point_type(self, point): """ Make sure incoming point type is a tuple with length 2, or explicitly a :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` :param point: tuple, or :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint :raises: TypeError if point not of :class:`pyprom.lib.locations.base_gridpoint.BaseGridPoint` or tuple :raises: Exception if tuple length != 2 :return: tuple(x,y) """ if isinstance(point, BaseGridPoint): incoming = point.to_tuple() elif isinstance(point, tuple): incoming = point else: raise TypeError("point must be tuple, or BaseGridPoint. Got: {}".format(type(point))) if len(incoming) != 2: raise Exception("tuple must have length of 2, or object must be BaseGridPoint and not inherited type.") return incoming
__unicode__ = __str__ = __repr__
def _isMultiPoint(mp): """ Check if passed in object is a :class:`MultiPoint` :param mp: object under scrutiny :raises: TypeError if other not of :class:`MultiPoint` """ if not isinstance(mp, MultiPoint): raise TypeError("MultiPoint expected")