summaryrefslogtreecommitdiffstats
path: root/webapp/django/contrib/gis/geos/geometries.py
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/django/contrib/gis/geos/geometries.py')
-rw-r--r--webapp/django/contrib/gis/geos/geometries.py391
1 files changed, 391 insertions, 0 deletions
diff --git a/webapp/django/contrib/gis/geos/geometries.py b/webapp/django/contrib/gis/geos/geometries.py
new file mode 100644
index 0000000000..c5420e93af
--- /dev/null
+++ b/webapp/django/contrib/gis/geos/geometries.py
@@ -0,0 +1,391 @@
+"""
+ This module houses the Point, LineString, LinearRing, and Polygon OGC
+ geometry classes. All geometry classes in this module inherit from
+ GEOSGeometry.
+"""
+from ctypes import c_uint, byref
+from django.contrib.gis.geos.base import GEOSGeometry
+from django.contrib.gis.geos.coordseq import GEOSCoordSeq
+from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
+from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR, HAS_NUMPY
+from django.contrib.gis.geos.prototypes import *
+if HAS_NUMPY: from numpy import ndarray, array
+
+class Point(GEOSGeometry):
+
+ def __init__(self, x, y=None, z=None, srid=None):
+ """
+ The Point object may be initialized with either a tuple, or individual
+ parameters.
+
+ For Example:
+ >>> p = Point((5, 23)) # 2D point, passed in as a tuple
+ >>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters
+ """
+
+ if isinstance(x, (tuple, list)):
+ # Here a tuple or list was passed in under the `x` parameter.
+ ndim = len(x)
+ if ndim < 2 or ndim > 3:
+ raise TypeError('Invalid sequence parameter: %s' % str(x))
+ coords = x
+ elif isinstance(x, (int, float, long)) and isinstance(y, (int, float, long)):
+ # Here X, Y, and (optionally) Z were passed in individually, as parameters.
+ if isinstance(z, (int, float, long)):
+ ndim = 3
+ coords = [x, y, z]
+ else:
+ ndim = 2
+ coords = [x, y]
+ else:
+ raise TypeError('Invalid parameters given for Point initialization.')
+
+ # Creating the coordinate sequence, and setting X, Y, [Z]
+ cs = create_cs(c_uint(1), c_uint(ndim))
+ cs_setx(cs, 0, coords[0])
+ cs_sety(cs, 0, coords[1])
+ if ndim == 3: cs_setz(cs, 0, coords[2])
+
+ # Initializing using the address returned from the GEOS
+ # createPoint factory.
+ super(Point, self).__init__(create_point(cs), srid=srid)
+
+ def __len__(self):
+ "Returns the number of dimensions for this Point (either 0, 2 or 3)."
+ if self.empty: return 0
+ if self.hasz: return 3
+ else: return 2
+
+ def get_x(self):
+ "Returns the X component of the Point."
+ return self._cs.getOrdinate(0, 0)
+
+ def set_x(self, value):
+ "Sets the X component of the Point."
+ self._cs.setOrdinate(0, 0, value)
+
+ def get_y(self):
+ "Returns the Y component of the Point."
+ return self._cs.getOrdinate(1, 0)
+
+ def set_y(self, value):
+ "Sets the Y component of the Point."
+ self._cs.setOrdinate(1, 0, value)
+
+ def get_z(self):
+ "Returns the Z component of the Point."
+ if self.hasz:
+ return self._cs.getOrdinate(2, 0)
+ else:
+ return None
+
+ def set_z(self, value):
+ "Sets the Z component of the Point."
+ if self.hasz:
+ self._cs.setOrdinate(2, 0, value)
+ else:
+ raise GEOSException('Cannot set Z on 2D Point.')
+
+ # X, Y, Z properties
+ x = property(get_x, set_x)
+ y = property(get_y, set_y)
+ z = property(get_z, set_z)
+
+ ### Tuple setting and retrieval routines. ###
+ def get_coords(self):
+ "Returns a tuple of the point."
+ return self._cs.tuple
+
+ def set_coords(self, tup):
+ "Sets the coordinates of the point with the given tuple."
+ self._cs[0] = tup
+
+ # The tuple and coords properties
+ tuple = property(get_coords, set_coords)
+ coords = tuple
+
+class LineString(GEOSGeometry):
+
+ #### Python 'magic' routines ####
+ def __init__(self, *args, **kwargs):
+ """
+ Initializes on the given sequence -- may take lists, tuples, NumPy arrays
+ of X,Y pairs, or Point objects. If Point objects are used, ownership is
+ _not_ transferred to the LineString object.
+
+ Examples:
+ ls = LineString((1, 1), (2, 2))
+ ls = LineString([(1, 1), (2, 2)])
+ ls = LineString(array([(1, 1), (2, 2)]))
+ ls = LineString(Point(1, 1), Point(2, 2))
+ """
+ # If only one argument provided, set the coords array appropriately
+ if len(args) == 1: coords = args[0]
+ else: coords = args
+
+ if isinstance(coords, (tuple, list)):
+ # Getting the number of coords and the number of dimensions -- which
+ # must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
+ ncoords = len(coords)
+ if coords: ndim = len(coords[0])
+ else: raise TypeError('Cannot initialize on empty sequence.')
+ self._checkdim(ndim)
+ # Incrementing through each of the coordinates and verifying
+ for i in xrange(1, ncoords):
+ if not isinstance(coords[i], (tuple, list, Point)):
+ raise TypeError('each coordinate should be a sequence (list or tuple)')
+ if len(coords[i]) != ndim: raise TypeError('Dimension mismatch.')
+ numpy_coords = False
+ elif HAS_NUMPY and isinstance(coords, ndarray):
+ shape = coords.shape # Using numpy's shape.
+ if len(shape) != 2: raise TypeError('Too many dimensions.')
+ self._checkdim(shape[1])
+ ncoords = shape[0]
+ ndim = shape[1]
+ numpy_coords = True
+ else:
+ raise TypeError('Invalid initialization input for LineStrings.')
+
+ # Creating a coordinate sequence object because it is easier to
+ # set the points using GEOSCoordSeq.__setitem__().
+ cs = GEOSCoordSeq(create_cs(ncoords, ndim), z=bool(ndim==3))
+ for i in xrange(ncoords):
+ if numpy_coords: cs[i] = coords[i,:]
+ elif isinstance(coords[i], Point): cs[i] = coords[i].tuple
+ else: cs[i] = coords[i]
+
+ # Getting the correct initialization function
+ if kwargs.get('ring', False):
+ func = create_linearring
+ else:
+ func = create_linestring
+
+ # If SRID was passed in with the keyword arguments
+ srid = kwargs.get('srid', None)
+
+ # Calling the base geometry initialization with the returned pointer
+ # from the function.
+ super(LineString, self).__init__(func(cs.ptr), srid=srid)
+
+ def __getitem__(self, index):
+ "Gets the point at the specified index."
+ return self._cs[index]
+
+ def __setitem__(self, index, value):
+ "Sets the point at the specified index, e.g., line_str[0] = (1, 2)."
+ self._cs[index] = value
+
+ def __iter__(self):
+ "Allows iteration over this LineString."
+ for i in xrange(len(self)):
+ yield self[i]
+
+ def __len__(self):
+ "Returns the number of points in this LineString."
+ return len(self._cs)
+
+ def _checkdim(self, dim):
+ if dim not in (2, 3): raise TypeError('Dimension mismatch.')
+
+ #### Sequence Properties ####
+ @property
+ def tuple(self):
+ "Returns a tuple version of the geometry from the coordinate sequence."
+ return self._cs.tuple
+ coords = tuple
+
+ def _listarr(self, func):
+ """
+ Internal routine that returns a sequence (list) corresponding with
+ the given function. Will return a numpy array if possible.
+ """
+ lst = [func(i) for i in xrange(len(self))]
+ if HAS_NUMPY: return array(lst) # ARRRR!
+ else: return lst
+
+ @property
+ def array(self):
+ "Returns a numpy array for the LineString."
+ return self._listarr(self._cs.__getitem__)
+
+ @property
+ def x(self):
+ "Returns a list or numpy array of the X variable."
+ return self._listarr(self._cs.getX)
+
+ @property
+ def y(self):
+ "Returns a list or numpy array of the Y variable."
+ return self._listarr(self._cs.getY)
+
+ @property
+ def z(self):
+ "Returns a list or numpy array of the Z variable."
+ if not self.hasz: return None
+ else: return self._listarr(self._cs.getZ)
+
+# LinearRings are LineStrings used within Polygons.
+class LinearRing(LineString):
+ def __init__(self, *args, **kwargs):
+ "Overriding the initialization function to set the ring keyword."
+ kwargs['ring'] = True # Setting the ring keyword argument to True
+ super(LinearRing, self).__init__(*args, **kwargs)
+
+class Polygon(GEOSGeometry):
+
+ def __init__(self, *args, **kwargs):
+ """
+ Initializes on an exterior ring and a sequence of holes (both
+ instances may be either LinearRing instances, or a tuple/list
+ that may be constructed into a LinearRing).
+
+ Examples of initialization, where shell, hole1, and hole2 are
+ valid LinearRing geometries:
+ >>> poly = Polygon(shell, hole1, hole2)
+ >>> poly = Polygon(shell, (hole1, hole2))
+
+ Example where a tuple parameters are used:
+ >>> poly = Polygon(((0, 0), (0, 10), (10, 10), (0, 10), (0, 0)),
+ ((4, 4), (4, 6), (6, 6), (6, 4), (4, 4)))
+ """
+ if not args:
+ raise TypeError('Must provide at list one LinearRing instance to initialize Polygon.')
+
+ # Getting the ext_ring and init_holes parameters from the argument list
+ ext_ring = args[0]
+ init_holes = args[1:]
+ n_holes = len(init_holes)
+
+ # If initialized as Polygon(shell, (LinearRing, LinearRing)) [for backward-compatibility]
+ if n_holes == 1 and isinstance(init_holes[0], (tuple, list)) and \
+ (len(init_holes[0]) == 0 or isinstance(init_holes[0][0], LinearRing)):
+ init_holes = init_holes[0]
+ n_holes = len(init_holes)
+
+ # Ensuring the exterior ring and holes parameters are LinearRing objects
+ # or may be instantiated into LinearRings.
+ ext_ring = self._construct_ring(ext_ring, 'Exterior parameter must be a LinearRing or an object that can initialize a LinearRing.')
+ holes_list = [] # Create new list, cause init_holes is a tuple.
+ for i in xrange(n_holes):
+ holes_list.append(self._construct_ring(init_holes[i], 'Holes parameter must be a sequence of LinearRings or objects that can initialize to LinearRings'))
+
+ # Why another loop? Because if a TypeError is raised, cloned pointers will
+ # be around that can't be cleaned up.
+ holes = get_pointer_arr(n_holes)
+ for i in xrange(n_holes): holes[i] = geom_clone(holes_list[i].ptr)
+
+ # Getting the shell pointer address.
+ shell = geom_clone(ext_ring.ptr)
+
+ # Calling with the GEOS createPolygon factory.
+ super(Polygon, self).__init__(create_polygon(shell, byref(holes), c_uint(n_holes)), **kwargs)
+
+ def __getitem__(self, index):
+ """
+ Returns the ring at the specified index. The first index, 0, will
+ always return the exterior ring. Indices > 0 will return the
+ interior ring at the given index (e.g., poly[1] and poly[2] would
+ return the first and second interior ring, respectively).
+ """
+ if index == 0:
+ return self.exterior_ring
+ else:
+ # Getting the interior ring, have to subtract 1 from the index.
+ return self.get_interior_ring(index-1)
+
+ def __setitem__(self, index, ring):
+ "Sets the ring at the specified index with the given ring."
+ # Checking the index and ring parameters.
+ self._checkindex(index)
+ if not isinstance(ring, LinearRing):
+ raise TypeError('must set Polygon index with a LinearRing object')
+
+ # Getting the shell
+ if index == 0:
+ shell = geom_clone(ring.ptr)
+ else:
+ shell = geom_clone(get_extring(self.ptr))
+
+ # Getting the interior rings (holes)
+ nholes = len(self)-1
+ if nholes > 0:
+ holes = get_pointer_arr(nholes)
+ for i in xrange(nholes):
+ if i == (index-1):
+ holes[i] = geom_clone(ring.ptr)
+ else:
+ holes[i] = geom_clone(get_intring(self.ptr, i))
+ holes_param = byref(holes)
+ else:
+ holes_param = None
+
+ # Getting the current pointer, replacing with the newly constructed
+ # geometry, and destroying the old geometry.
+ prev_ptr = self.ptr
+ srid = self.srid
+ self._ptr = create_polygon(shell, holes_param, c_uint(nholes))
+ if srid: self.srid = srid
+ destroy_geom(prev_ptr)
+
+ def __iter__(self):
+ "Iterates over each ring in the polygon."
+ for i in xrange(len(self)):
+ yield self[i]
+
+ def __len__(self):
+ "Returns the number of rings in this Polygon."
+ return self.num_interior_rings + 1
+
+ def _checkindex(self, index):
+ "Internal routine for checking the given ring index."
+ if index < 0 or index >= len(self):
+ raise GEOSIndexError('invalid Polygon ring index: %s' % index)
+
+ def _construct_ring(self, param, msg=''):
+ "Helper routine for trying to construct a ring from the given parameter."
+ if isinstance(param, LinearRing): return param
+ try:
+ ring = LinearRing(param)
+ return ring
+ except TypeError:
+ raise TypeError(msg)
+
+ def get_interior_ring(self, ring_i):
+ """
+ Gets the interior ring at the specified index, 0 is for the first
+ interior ring, not the exterior ring.
+ """
+ self._checkindex(ring_i+1)
+ return GEOSGeometry(geom_clone(get_intring(self.ptr, ring_i)), srid=self.srid)
+
+ #### Polygon Properties ####
+ @property
+ def num_interior_rings(self):
+ "Returns the number of interior rings."
+ # Getting the number of rings
+ return get_nrings(self.ptr)
+
+ def get_ext_ring(self):
+ "Gets the exterior ring of the Polygon."
+ return GEOSGeometry(geom_clone(get_extring(self.ptr)), srid=self.srid)
+
+ def set_ext_ring(self, ring):
+ "Sets the exterior ring of the Polygon."
+ self[0] = ring
+
+ # properties for the exterior ring/shell
+ exterior_ring = property(get_ext_ring, set_ext_ring)
+ shell = exterior_ring
+
+ @property
+ def tuple(self):
+ "Gets the tuple for each ring in this Polygon."
+ return tuple([self[i].tuple for i in xrange(len(self))])
+ coords = tuple
+
+ @property
+ def kml(self):
+ "Returns the KML representation of this Polygon."
+ inner_kml = ''.join(["<innerBoundaryIs>%s</innerBoundaryIs>" % self[i+1].kml
+ for i in xrange(self.num_interior_rings)])
+ return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml)