Source code for geojson.geometry

from decimal import Decimal
from numbers import Number, Real

from geojson.base import GeoJSON


DEFAULT_PRECISION = 6


class Geometry(GeoJSON):
    """
    Represents an abstract base class for a WGS84 geometry.
    """

    def __init__(self, coordinates=None, validate=False, precision=None, **extra):
        """
        Initialises a Geometry object.

        :param coordinates: Coordinates of the Geometry object.
        :type coordinates: tuple or list of tuple
        :param validate: Raise exception if validation errors are present?
        :type validate: boolean
        :param precision: Number of decimal places for lat/lon coords.
        :type precision: integer
        """
        super().__init__(**extra)
        if precision is None:
            precision = DEFAULT_PRECISION
        self["coordinates"] = self.clean_coordinates(
            coordinates or [], precision)

        if validate:
            errors = self.errors()
            if errors:
                raise ValueError(f'{errors}: {coordinates}')

    @classmethod
    def clean_coordinates(cls, coords, precision):
        if isinstance(coords, cls):
            return coords['coordinates']

        new_coords = []
        if isinstance(coords, Geometry):
            coords = [coords]
        for coord in coords:
            if isinstance(coord, (list, tuple)):
                new_coords.append(cls.clean_coordinates(coord, precision))
            elif isinstance(coord, Geometry):
                new_coords.append(coord['coordinates'])
            elif isinstance(coord, (Real, Decimal)):
                new_coords.append(round(coord, precision))
            else:
                raise ValueError("%r is not a JSON compliant number" % coord)
        return new_coords


class GeometryCollection(GeoJSON):
    """
    Represents an abstract base class for collections of WGS84 geometries.
    """

    def __init__(self, geometries=None, **extra):
        super().__init__(**extra)
        self["geometries"] = geometries or []

[docs] def errors(self): errors = [geom.errors() for geom in self['geometries']] return [err for err in errors if err]
def __getitem__(self, key): try: return self.get("geometries", ())[key] except (KeyError, TypeError, IndexError): return super(GeoJSON, self).__getitem__(key) # Marker classes. def check_point(coord): if not isinstance(coord, list): return 'each position must be a list' if len(coord) not in (2, 3): return 'a position must have exactly 2 or 3 values' for number in coord: if not isinstance(number, Number): return 'a position cannot have inner positions' class Point(Geometry):
[docs] def errors(self): return check_point(self['coordinates'])
class MultiPoint(Geometry):
[docs] def errors(self): return self.check_list_errors(check_point, self['coordinates'])
def check_line_string(coord): if not isinstance(coord, list): return 'each line must be a list of positions' if len(coord) < 2: return ('the "coordinates" member must be an array of ' 'two or more positions') for pos in coord: error = check_point(pos) if error: return error class LineString(MultiPoint):
[docs] def errors(self): return check_line_string(self['coordinates'])
class MultiLineString(Geometry):
[docs] def errors(self): return self.check_list_errors(check_line_string, self['coordinates'])
def check_polygon(coord): if not isinstance(coord, list): return 'Each polygon must be a list of linear rings' if not all(isinstance(elem, list) for elem in coord): return "Each element of a polygon's coordinates must be a list" lengths = all(len(elem) >= 4 for elem in coord) if lengths is False: return 'Each linear ring must contain at least 4 positions' isring = all(elem[0] == elem[-1] for elem in coord) if isring is False: return 'Each linear ring must end where it started' class Polygon(Geometry):
[docs] def errors(self): return check_polygon(self['coordinates'])
class MultiPolygon(Geometry):
[docs] def errors(self): return self.check_list_errors(check_polygon, self['coordinates'])
class Default: """ GeoJSON default object. """