Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

gis: added GEOSGeometry, a ctypes wrapper for the GEOS library, provi…

…ding:

  (1) an interface with the GEOS C API, using only python and ctypes (can be accessed w/get_GEOM_geos)
  (2) independence from the GEOS Python SWIG module that is deprecated and no longer maintained
  (3) portability, currently works on both Linux and Win32 platforms


git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5008 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 39f9d48e4e60a1d82a16f831f3c6153ac21f9148 1 parent fdbbd65
@jbronn jbronn authored
View
7 django/contrib/gis/db/models/GeoMixin.py
@@ -1,5 +1,5 @@
# GEOS Routines
-from django.contrib.gis.geos import hex_to_wkt, centroid, area
+from django.contrib.gis.geos import GEOSGeometry, hex_to_wkt, centroid, area
# Until model subclassing is a possibility, a mixin class is used to add
# the necessary functions that may be contributed for geographic objects.
@@ -8,6 +8,10 @@ class GeoMixin:
# A subclass of Model is specifically needed so that these geographic
# routines are present for instantiations of the models.
+ def _get_GEOM_geos(self, field):
+ "Gets a GEOS Python object for the geometry."
+ return GEOSGeometry(getattr(self, field.attname), 'hex')
+
def _get_GEOM_wkt(self, field):
"Gets the WKT of the geometry."
hex = getattr(self, field.attname)
@@ -19,6 +23,7 @@ def _get_GEOM_centroid(self, field):
return centroid(hex)
def _get_GEOM_area(self, field):
+ "Gets the area of the geometry, in projected units."
hex = getattr(self, field.attname)
return area(hex)
View
3  django/contrib/gis/db/models/__init__.py
@@ -4,6 +4,9 @@
# The GeoManager
from django.contrib.gis.db.models.manager import GeoManager
+# The GeoQ object
+from django.contrib.gis.db.models.query import GeoQ
+
# The various PostGIS/OpenGIS enabled fields.
from django.contrib.gis.db.models.fields import \
GeometryField, PointField, LineStringField, PolygonField, \
View
1  django/contrib/gis/db/models/fields/__init__.py
@@ -60,6 +60,7 @@ def contribute_to_class(self, cls, name):
super(GeometryField, self).contribute_to_class(cls, name)
# Adding the WKT accessor function for geometry
+ setattr(cls, 'get_%s_geos' % self.name, curry(cls._get_GEOM_geos, field=self))
setattr(cls, 'get_%s_wkt' % self.name, curry(cls._get_GEOM_wkt, field=self))
setattr(cls, 'get_%s_centroid' % self.name, curry(cls._get_GEOM_centroid, field=self))
setattr(cls, 'get_%s_area' % self.name, curry(cls._get_GEOM_area, field=self))
View
2  django/contrib/gis/db/models/query.py
@@ -1,4 +1,4 @@
-from django.db.models.query import Q, QNot, QuerySet
+from django.db.models.query import Q, QuerySet
from django.contrib.gis.db.models.postgis import parse_lookup
import operator
View
663 django/contrib/gis/geos/GEOSGeometry.py
@@ -0,0 +1,663 @@
+# Copyright (c) 2007, Justin Bronn
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of GEOSGeometry nor the names of its contributors may be used
+# to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# Trying not to pollute the namespace.
+from ctypes import \
+ CDLL, CFUNCTYPE, byref, string_at, create_string_buffer, \
+ c_char_p, c_double, c_float, c_int, c_uint, c_size_t, c_ubyte
+import os, sys
+
+"""
+ The goal of this module is to be a ctypes wrapper around the GEOS library
+ that will work on both *NIX and Windows systems. Specifically, this uses
+ the GEOS C api.
+
+ I have several motivations for doing this:
+ (1) The GEOS SWIG wrapper is no longer maintained, and requires the
+ installation of SWIG.
+ (2) The PCL implementation is over 2K+ lines of C and would make
+ PCL a requisite package for the GeoDjango application stack.
+ (3) Windows compatibility becomes substantially easier, and does not require the
+ additional compilation of PCL or GEOS and SWIG -- all that is needed is
+ a Win32 compiled GEOS C library (dll) in a location that Python can read
+ (e.g. C:\Python25).
+
+ In summary, I wanted to wrap GEOS in a more maintainable and portable way using
+ only Python and the excellent ctypes library (now standard in Python 2.5).
+
+ In the spirit of loose coupling, this library does not require Django or
+ GeoDjango. Only the GEOS C library and ctypes are needed for the platform
+ of your choice.
+
+ For more information about GEOS:
+ http://geos.refractions.net
+
+ For more info about PCL and the discontinuation of the Python GEOS
+ library see Sean Gillies' writeup (and subsequent update) at:
+ http://zcologia.com/news/150/geometries-for-python/
+ http://zcologia.com/news/429/geometries-for-python-update/
+"""
+
+# Setting the appropriate name for the GEOS-C library, depending on which
+# platform we're running.
+if os.name == 'nt':
+ # Windows NT library
+ lib_name = 'libgeos_c-1.dll'
+else:
+ # Linux shared library
+ lib_name = 'libgeos_c.so'
+
+# Getting the GEOS C library. The C interface (CDLL) is used for
+# both *NIX and Windows.
+# See the GEOS C API source code for more details on the library function calls:
+# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
+lgeos = CDLL(lib_name)
+
+# The notice and error handlers
+# Supposed to mimic the GEOS message handler (C below):
+# "typedef void (*GEOSMessageHandler)(const char *fmt, ...);"
+NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
+def notice_h(fmt, list):
+ sys.stdout.write((fmt + '\n') % list)
+notice_h = NOTICEFUNC(notice_h)
+
+ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
+def error_h(fmt, list):
+ if not list:
+ sys.stderr.write(fmt)
+ else:
+ sys.stderr.write('ERROR: %s' % str(list))
+error_h = ERRORFUNC(error_h)
+
+# The initGEOS routine should be called first, however, that routine takes
+# the notice and error functions as parameters. Here is the C code that
+# we need to wrap:
+# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);"
+lgeos.initGEOS(notice_h, error_h)
+
+class GEOSException:
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return repr(self.msg)
+
+class GEOSGeometry:
+ "A class that, generally, encapsulates a GEOS geometry."
+
+ #### Python 'magic' routines ####
+ def __init__(self, input, geom_type='wkt'):
+ "Takes an input and the type of the input for initialization."
+ if geom_type == 'wkt':
+ # If the geometry is in WKT form
+ self._g = lgeos.GEOSGeomFromWKT(c_char_p(input))
+ elif geom_type == 'hex':
+ # If the geometry is in EWHEX form.
+ sz = c_size_t(len(input))
+ buf = create_string_buffer(input)
+ self._g = lgeos.GEOSGeomFromHEX_buf(buf, sz)
+ elif geom_type == 'geos':
+ # When the input is a C pointer (Python integer)
+ self._g = input
+ else:
+ # Invalid geometry type.
+ raise GEOSException, 'Improper geometry input type!'
+
+ # Setting the class type (e.g. 'Point', 'Polygon', etc.)
+ self.__class__ = GEO_CLASSES[self.geom_type]
+
+ # If the geometry pointer is NULL (0), then raise an exception.
+ if not self._g:
+ raise GEOSException, 'Could not initialize on input!'
+
+ def __del__(self):
+ "This cleans up the memory allocated for the geometry."
+ lgeos.GEOSGeom_destroy(self._g)
+
+ def __str__(self):
+ "WKT is used for the string representation."
+ return self.wkt
+
+ def __eq__(self, other):
+ "Equivalence testing."
+ return self.equals(other)
+
+ #### Geometry Info ####
+ @property
+ def geom_type(self):
+ "Returns a string representing the geometry type, e.g. 'Polygon'"
+ return string_at(lgeos.GEOSGeomType(self._g))
+
+ @property
+ def geom_typeid(self):
+ "Returns an integer representing the geometry type."
+ return lgeos.GEOSGeomTypeId(self._g)
+
+ @property
+ def num_geom(self):
+ "Returns the number of geometries in the geometry."
+ n = lgeos.GEOSGetNumGeometries(self._g)
+ if n == -1: raise GEOSException, 'Error getting number of geometries!'
+ else: return n
+
+ @property
+ def num_coords(self):
+ "Returns the number of coordinates in the geometry."
+ n = lgeos.GEOSGetNumCoordinates(self._g)
+ if n == -1: raise GEOSException, 'Error getting number of coordinates!'
+ else: return n
+
+ @property
+ def dims(self):
+ "Returns the dimension of this Geometry (0=point, 1=line, 2=surface)."
+ return lgeos.GEOSGeom_getDimensions(self._g)
+
+ @property
+ def coord_seq(self):
+ "Returns the coordinate sequence for the geometry."
+
+ # Only these geometries can return coordinate sequences
+ if self.geom_type not in ['LineString', 'LinearRing', 'Point']:
+ return None
+
+ # Getting the coordinate sequence for the geometry
+ cs = lgeos.GEOSGeom_getCoordSeq(self._g)
+
+ # Cloning the coordinate sequence (if the original is returned,
+ # and it is garbage-collected we will get a segmentation fault!)
+ clone = lgeos.GEOSCoordSeq_clone(cs)
+ return GEOSCoordSeq(clone, z=self.hasz)
+
+ def normalize(self):
+ "Converts this Geometry to normal form (or canonical form).Converts this Geometry to normal form (or canonical form)."
+ status = lgeos.GEOSNormalize(self._g)
+ if status == -1: raise GEOSException, 'failed to normalize geometry'
+
+ def _predicate(self, val):
+ "Checks the result, 2 for exception, 1 on true, 0 on false."
+ if val == 0:
+ return False
+ elif val == 1:
+ return True
+ else:
+ raise GEOSException, 'Predicate exception occurred!'
+
+ ### Unary predicates ###
+ @property
+ def empty(self):
+ "Returns a boolean indicating whether the set of points in this geometry are empty."
+ return self._predicate(lgeos.GEOSisEmpty(self._g))
+
+ @property
+ def valid(self):
+ "This property tests the validity of this geometry."
+ return self._predicate(lgeos.GEOSisValid(self._g))
+
+ @property
+ def simple(self):
+ "Returns false if the Geometry not simple."
+ return self._predicate(lgeos.GEOSisSimple(self._g))
+
+ @property
+ def ring(self):
+ "Returns whether or not the geometry is a ring."
+ return self._predicate(lgeos.GEOSisRing(self._g))
+
+ @property
+ def hasz(self):
+ "Returns whether the geometry has a 3D dimension."
+ return self._predicate(lgeos.GEOSHasZ(self._g))
+
+ #### Binary predicates. ####
+ def relate_pattern(self, other, pattern):
+ """Returns true if the elements in the DE-9IM intersection matrix for
+ the two Geometrys match the elements in pattern."""
+ if len(pattern) > 9:
+ raise GEOSException, 'invalid intersection matrix pattern'
+ pat = create_string_buffer(pattern)
+ return self._predicate(lgeos.GEOSRelatePattern(self._g, other._g, pat))
+
+ def disjoint(self, other):
+ "Returns true if the DE-9IM intersection matrix for the two Geometrys is FF*FF****."
+ return self._predicate(lgeos.GEOSDisjoint(self._g, other._g))
+
+ def touches(self, other):
+ "Returns true if the DE-9IM intersection matrix for the two Geometrys is FT*******, F**T***** or F***T****."
+ return self._predicate(lgeos.GEOSTouches(self._g, other._g))
+
+ def intersects(self, other):
+ "Returns true if disjoint returns false."
+ return self._predicate(lgeos.GEOSIntersects(self._g, other._g))
+
+ def crosses(self, other):
+ """Returns true if the DE-9IM intersection matrix for the two Geometrys is T*T****** (for a point and a curve,
+ a point and an area or a line and an area) 0******** (for two curves)."""
+ return self._predicate(lgeos.GEOSCrosses(self._g, other._g))
+
+ def within(self, other):
+ "Returns true if the DE-9IM intersection matrix for the two Geometrys is T*F**F***."
+ return self._predicate(lgeos.GEOSWithin(self._g, other._g))
+
+ def contains(self, other):
+ "Returns true if other.within(this) returns true."
+ return self._predicate(lgeos.GEOSContains(self._g, other._g))
+
+ def overlaps(self, other):
+ """Returns true if the DE-9IM intersection matrix for the two Geometrys is T*T***T** (for two points
+ or two surfaces) 1*T***T** (for two curves)."""
+ return self._predicate(lgeos.GEOSOverlaps(self._g, other._g))
+
+ def equals(self, other):
+ "Returns true if the DE-9IM intersection matrix for the two Geometrys is T*F**FFF*."
+ return self._predicate(lgeos.GEOSEquals(self._g, other._g))
+
+ def equals_exact(self, other, tolerance=0):
+ "Returns true if the two Geometrys are exactly equal, up to a specified tolerance."
+ tol = c_double(tolerance)
+ return self._predicate(lgeos.GEOSEqualsExact(self._g, other._g, tol))
+
+ #### SRID Routines ####
+ @property
+ def srid(self):
+ "Gets the SRID for the geometry, returns None if no SRID is set."
+ s = lgeos.GEOSGetSRID(self._g)
+ if s == 0:
+ return None
+ else:
+ return lgeos.GEOSGetSRID(self._g)
+
+ def set_srid(self, srid):
+ "Sets the SRID for the geometry."
+ lgeos.GEOSSetSRID(self._g, c_int(srid))
+
+ #### Output Routines ####
+ @property
+ def wkt(self):
+ "Returns the WKT of the Geometry."
+ return string_at(lgeos.GEOSGeomToWKT(self._g))
+
+ @property
+ def hex(self):
+ "Returns the WKBHEX of the Geometry."
+ sz = c_size_t()
+ h = lgeos.GEOSGeomToHEX_buf(self._g, byref(sz))
+ return string_at(h, sz.value)
+
+ #### Topology Routines ####
+ def buffer(self, width, quadsegs=8):
+ """Returns a geometry that represents all points whose distance from this
+ Geometry is less than or equal to distance. Calculations are in the
+ Spatial Reference System of this Geometry. The optional third parameter sets
+ the number of segment used to approximate a quarter circle (defaults to 8).
+ [Text from PostGIS documentation at ch. 6.2.2 <-- verify]
+ """
+ if type(width) != type(0.0):
+ raise TypeError, 'width parameter must be a float'
+ if type(quadsegs) != type(0):
+ raise TypeError, 'quadsegs parameter must be an integer'
+ b = lgeos.GEOSBuffer(self._g, c_float(width), c_int(quadsegs))
+ return GEOSGeometry(b, 'geos')
+
+ @property
+ def envelope(self):
+ "Return the geometries bounding box."
+ e = lgeos.GEOSEnvelope(self._g)
+ return GEOSGeometry(e, 'geos')
+
+ @property
+ def centroid(self):
+ """The centroid is equal to the centroid of the set of component Geometrys
+ of highest dimension (since the lower-dimension geometries contribute zero
+ "weight" to the centroid)."""
+ g = lgeos.GEOSGetCentroid(self._g)
+ return GEOSGeometry(g, 'geos')
+
+ @property
+ def boundary(self):
+ "Returns the boundary as a newly allocated Geometry object."
+ g = lgeos.GEOSBoundary(self._g)
+ return GEOSGeometry(g, 'geos')
+
+ @property
+ def convex_hull(self):
+ "Returns the smallest convex Polygon that contains all the points in the Geometry."
+ g = lgeos.GEOSConvexHull(self._g)
+ return GEOSGeometry(g, 'geos')
+
+ @property
+ def point_on_surface(self):
+ "Computes an interior point of this Geometry."
+ g = lgeos.GEOSPointOnSurface(self._g)
+ return GEOSGeometry(g, 'geos')
+
+ def relate(self, other):
+ "Returns the DE-9IM intersection matrix for this geometry and the other."
+ return string_at(lgeos.GEOSRelate(self._g, other._g))
+
+ def difference(self, other):
+ """Returns a Geometry representing the points making up this Geometry
+ that do not make up other."""
+ d = lgeos.GEOSDifference(self._g, other._g)
+ return GEOSGeometry(d, 'geos')
+
+ def sym_difference(self, other):
+ """Returns a set combining the points in this Geometry not in other,
+ and the points in other not in this Geometry."""
+ d = lgeos.GEOSSymDifference(self._g, other._g)
+ return GEOSGeometry(d, 'geos')
+
+ def intersection(self, other):
+ "Returns a Geometry representing the points shared by this Geometry and other."
+ i = lgeos.GEOSIntersection(self._g, other._g)
+ return GEOSGeometry(i, 'geos')
+
+ def union(self, other):
+ "Returns a Geometry representing all the points in this Geometry and other."
+ u = lgeos.GEOSUnion(self._g, other._g)
+ return GEOSGeometry(u, 'geos')
+
+ #### Other Routines ####
+ @property
+ def area(self):
+ "Returns the area of the Geometry."
+ a = c_double()
+ status = lgeos.GEOSArea(self._g, byref(a))
+ if not status:
+ return None
+ else:
+ return a.value
+
+class GEOSCoordSeq:
+ "The internal representation of a list of coordinates inside a Geometry."
+
+ def __init__(self, ptr, z=False):
+ "Initializes from a GEOS pointer."
+ self._cs = ptr
+ self._z = z
+
+ def __del__(self):
+ lgeos.GEOSCoordSeq_destroy(self._cs)
+
+ def __iter__(self):
+ for i in xrange(self.size):
+ yield self.__getitem__(i)
+
+ def __len__(self):
+ return self.size
+
+ def __str__(self):
+ "The string representation of the coordinate sequence."
+ rep = []
+ for i in xrange(self.size):
+ rep.append(self.__getitem__(i))
+ return str(tuple(rep))
+
+ def _checkindex(self, index):
+ "Checks the index."
+ sz = self.size
+ if (sz < 1) or (index < 0) or (index >= sz):
+ raise IndexError, 'index out of range'
+
+ def _checkdim(self, dim):
+ "Checks the given dimension."
+ if dim < 0 or dim > 2:
+ raise GEOSException, 'invalid ordinate dimension "%d"' % dim
+
+ def __getitem__(self, index):
+ "Can use the index [] operator to get coordinate sequence at an index."
+ coords = [self.getX(index), self.getY(index)]
+ if self.dims == 3 and self._z:
+ coords.append(self.getZ(index))
+ return tuple(coords)
+
+ def __setitem__(self, index, value):
+ "Can use the index [] operator to set coordinate sequence at an index."
+ if self.dims == 3 and self._z:
+ n_args = 3
+ set_3d = True
+ else:
+ n_args = 2
+ set_3d = False
+ if len(value) != n_args:
+ raise GEOSException, 'Improper value given!'
+ self.setX(index, value)
+ self.setY(index, value)
+ if set_3d: self.setZ(index, value)
+
+ # Getting and setting the X coordinate for the given index.
+ def getX(self, index):
+ return self.getOrdinate(0, index)
+
+ def setX(self, index, value):
+ self.setOrdinate(0, index, value)
+
+ # Getting and setting the Y coordinate for the given index.
+ def getY(self, index):
+ return self.getOrdinate(1, index)
+
+ def setY(self, index):
+ self.setOrdinate(1, index)
+
+ # Getting and setting the Z coordinate for the given index
+ def getZ(self, index):
+ return self.getOrdinate(2, index)
+
+ def setZ(self, index):
+ self.setOrdinate(2, index)
+
+ def getOrdinate(self, dimension, index):
+ "Gets the value for the given dimension and index."
+ self._checkindex(index)
+ self._checkdim(dimension)
+
+ # Wrapping the dimension, index
+ dim = c_uint(dimension)
+ idx = c_uint(index)
+
+ # 'd' is the value of the point
+ d = c_double()
+ status = lgeos.GEOSCoordSeq_getOrdinate(self._cs, idx, dim, byref(d))
+ if status == 0:
+ raise GEOSException, 'Could not get the ordinate for (dim, index): (%d, %d)' % (dimension, index)
+ return d.value
+
+ def setOrdinate(self, dimension, index, value):
+ "Sets the value for the given dimension and index."
+ self._checkindex(idnex)
+ self._checkdim(dimension)
+
+ # Wrapping the dimension, index
+ dim = c_uint(dimension)
+ idx = c_uint(index)
+
+ # 'd' is the value of the point
+ d = c_double(value)
+ status = lgeos.GEOSCoordSeq_getOrdinate(self._cs, idx, dim, byref(d))
+ if status == 0:
+ raise GEOSException, 'Could not set the ordinate for (dim, index): (%d, %d)' % (dimension, index)
+
+ ### Dimensions ###
+ @property
+ def size(self):
+ "Returns the size of this coordinate sequence."
+ n = c_uint(0)
+ status = lgeos.GEOSCoordSeq_getSize(self._cs, byref(n))
+ if status == 0:
+ raise GEOSException, 'Could not get CoordSeq size!'
+ return n.value
+
+ @property
+ def dims(self):
+ "Returns the dimensions of this coordinate sequence."
+ n = c_uint(0)
+ status = lgeos.GEOSCoordSeq_getDimensions(self._cs, byref(n))
+ if status == 0:
+ raise GEOSException, 'Could not get CoordSeq dimensoins!'
+ return n.value
+
+ @property
+ def hasz(self):
+ "Inherits this from the parent geometry."
+ return self._z
+
+ ### Other Methods ###
+ @property
+ def tuple(self):
+ n = self.size
+ if n == 1:
+ return self.__getitem__(0)
+ else:
+ return tuple(self.__getitem__(i) for i in xrange(n))
+
+# Factory coordinate sequence Function
+def createCoordSeq(size, dims):
+ return GEOSCoordSeq(lgeos.GEOSCoordSeq_create(c_uint(size), c_uint(dims)))
+
+class Point(GEOSGeometry):
+
+ def _cache_cs(self):
+ "Caches the coordinate sequence."
+ if not hasattr(self, '_cs'): self._cs = self.coord_seq
+
+ def _getOrdinate(self, dim, idx):
+ "The coordinate sequence getOrdinate() wrapper."
+ self._cache_cs()
+ return self._cs.getOrdinate(dim, idx)
+
+ @property
+ def x(self):
+ "Returns the X component of the Point."
+ return self._getOrdinate(0, 0)
+
+ @property
+ def y(self):
+ "Returns the Y component of the Point."
+ return self._getOrdinate(1, 0)
+
+ @property
+ def z(self):
+ "Returns the Z component of the Point."
+ if self.hasz:
+ return self._getOrdinate(2, 0)
+ else:
+ return None
+
+ @property
+ def tuple(self):
+ "Returns a tuple of the point."
+ self._cache_cs()
+ return self._cs.tuple
+
+class LineString(GEOSGeometry):
+
+ def _cache_cs(self):
+ "Caches the coordinate sequence."
+ if not hasattr(self, '_cs'): self._cs = self.coord_seq
+
+ @property
+ def tuple(self):
+ "Returns a tuple version of the geometry from the coordinate sequence."
+ self._cache_cs()
+ return self._cs.tuple
+
+class LinearRing(LineString):
+ pass
+
+class Polygon(GEOSGeometry):
+
+ #### Polygon Routines ####
+ @property
+ def num_interior_rings(self):
+ "Returns the number of interior rings."
+
+ # Getting the number of rings
+ n = lgeos.GEOSGetNumInteriorRings(self._g)
+
+ # -1 indicates an exception occurred
+ if n == -1: raise GEOSException, 'Error getting the number of interior rings!'
+ else: return n
+
+ def get_interior_ring(self, ring_i):
+ "Gets the interior ring at the specified index."
+
+ # Making sure the ring index is within range
+ if ring_i >= self.num_interior_rings:
+ raise GEOSException, 'Invalid ring index.'
+
+ # Getting a clone of the ring geometry at the given ring index.
+ r = lgeos.GEOSGeom_clone(lgeos.GEOSGetInteriorRingN(self._g, c_int(ring_i)))
+ return GEOSGeometry(r, 'geos')
+
+ @property
+ def exterior_ring(self):
+ "Gets the exterior ring of the Polygon."
+
+ # Getting a clone of the ring geometry
+ r = lgeos.GEOSGeom_clone(lgeos.GEOSGetExteriorRing(self._g))
+ return GEOSGeometry(r, 'geos')
+
+class GeometryCollection(GEOSGeometry):
+
+ def _checkindex(self, index):
+ "Checks the given geometry index."
+ if index < 0 or index >= self.num_geom:
+ raise IndexError, 'index out of range'
+
+ def __iter__(self):
+ "For iteration on the multiple geometries."
+ for i in xrange(self.num_geom):
+ yield self.__getitem__(i)
+
+ def __getitem__(self, index):
+ "For indexing on the multiple geometries."
+ self._checkindex(index)
+ item = lgeos.GEOSGeom_clone(lgeos.GEOSGetGeometryN(self._g, c_int(index)))
+ return GEOSGeometry(item, 'geos')
+
+ def __len__(self):
+ return self.num_geom
+
+class MultiPoint(GeometryCollection):
+ pass
+
+class MultiLineString(GeometryCollection):
+ pass
+
+class MultiPolygon(GeometryCollection):
+ pass
+
+# Class mapping dictionary
+GEO_CLASSES = {'Point' : Point,
+ 'Polygon' : Polygon,
+ 'LineString' : LineString,
+ 'LinearRing' : LinearRing,
+ 'GeometryCollection' : GeometryCollection,
+ 'MultiPoint' : MultiPoint,
+ 'MultiLineString' : MultiLineString,
+ 'MultiPolygon' : MultiPolygon,
+ }
View
23 django/contrib/gis/geos/__init__.py
@@ -1,19 +1,18 @@
-from geos import geomFromWKT, geomToWKT, geomFromHEX, geomToHEX
+from GEOSGeometry import GEOSGeometry, GEOSException
def hex_to_wkt(hex):
- "Converts EWKBHEX into WKT."
- return geomToWKT(geomFromHEX(hex))
+ "Converts HEXEWKB into WKT."
+ return GEOSGeometry(hex, 'hex').wkt
def wkt_to_hex(wkt):
- "Converts WKT into EWKBHEX."
- return geomToHEX(geomFromWKT(wkt))
+ "Converts WKT into HEXEWKB."
+ return GEOSGeometry(wkt, 'wkt').hex
-def centroid(hex):
- "Returns the centroid of the geometry (given in EWKBHEX)."
- center = (geomFromHEX(hex)).getCentroid()
- return geomToWKT(center)
+def centroid(input, geom_type='hex'):
+ "Returns the centroid of the geometry (given in HEXEWKB)."
+ return GEOSGeometry(input, geom_type).centroid.wkt
-def area(hex):
- "Returns the area of the geometry (given in EWKBHEX)."
- return (geomFromHEX(hex)).area()
+def area(input, geom_type='hex'):
+ "Returns the area of the geometry (given in HEXEWKB)."
+ return GEOSGeometry(input, geom_type).area
View
0  django/contrib/gis/tests/__init__.py
No changes.
View
123 django/contrib/gis/tests/geometries.py
@@ -0,0 +1,123 @@
+import re
+
+wkt_regex = re.compile(r'^(?P<type>[A-Z]+) ?\(')
+
+class TestGeom:
+ "The Test Geometry class container."
+ def __init__(self, wkt, **kwargs):
+ self.wkt = wkt
+
+ m = wkt_regex.match(wkt)
+ if not m:
+ raise Exception, 'Improper WKT: "%s"' % wkt
+ self.geo_type = m.group('type')
+
+
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
+# For the old tests
+swig_geoms = (TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))', ncoords=5),
+ TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 10 90, 90 90, 90 10, 10 10) ))', ncoords=10),
+ )
+
+# Testing WKT & HEX
+hex_wkt = (TestGeom('POINT(0 1)', hex='01010000000000000000000000000000000000F03F'),
+ TestGeom('LINESTRING(0 1, 2 3, 4 5)', hex='0102000000030000000000000000000000000000000000F03F0000000000000040000000000000084000000000000010400000000000001440'),
+ TestGeom('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))', hex='010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000'),
+ TestGeom('MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0)', hex='010400000005000000010100000000000000000000000000000000000000010100000000000000000024400000000000000000010100000000000000000024400000000000002440010100000000000000000000000000000000002440010100000000000000000000000000000000000000'),
+ TestGeom('MULTILINESTRING((0 0, 10 0, 10 10, 0 10),(20 20, 30 20))', hex='01050000000200000001020000000400000000000000000000000000000000000000000000000000244000000000000000000000000000002440000000000000244000000000000000000000000000002440010200000002000000000000000000344000000000000034400000000000003E400000000000003440'),
+ TestGeom('MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)),((20 20, 20 30, 30 30, 30 20, 20 20),(25 25, 25 26, 26 26, 26 25, 25 25)))', hex='010600000002000000010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000010300000002000000050000000000000000003440000000000000344000000000000034400000000000003E400000000000003E400000000000003E400000000000003E40000000000000344000000000000034400000000000003440050000000000000000003940000000000000394000000000000039400000000000003A400000000000003A400000000000003A400000000000003A40000000000000394000000000000039400000000000003940'),
+ TestGeom('GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0, 10 0, 10 10, 0 10, 0 0)),((20 20, 20 30, 30 30, 30 20, 20 20),(25 25, 25 26, 26 26, 26 25, 25 25))),MULTILINESTRING((0 0, 10 0, 10 10, 0 10),(20 20, 30 20)),MULTIPOINT(0 0, 10 0, 10 10, 0 10, 0 0))', hex='010700000003000000010600000002000000010300000001000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000010300000002000000050000000000000000003440000000000000344000000000000034400000000000003E400000000000003E400000000000003E400000000000003E40000000000000344000000000000034400000000000003440050000000000000000003940000000000000394000000000000039400000000000003A400000000000003A400000000000003A400000000000003A4000000000000039400000000000003940000000000000394001050000000200000001020000000400000000000000000000000000000000000000000000000000244000000000000000000000000000002440000000000000244000000000000000000000000000002440010200000002000000000000000000344000000000000034400000000000003E400000000000003440010400000005000000010100000000000000000000000000000000000000010100000000000000000024400000000000000000010100000000000000000024400000000000002440010100000000000000000000000000000000002440010100000000000000000000000000000000000000'),
+ )
+
+# WKT Output
+wkt_out = (TestGeom('POINT(110 130)', ewkt='POINT (110.0000000000000000 130.0000000000000000)'),
+ TestGeom('LINESTRING(40 40, 50 130, 130 130)', ewkt='LINESTRING (40.0000000000000000 40.0000000000000000, 50.0000000000000000 130.0000000000000000, 130.0000000000000000 130.0000000000000000)'),
+ TestGeom('POLYGON((150 150, 410 150, 280 20, 20 20, 150 150),(170 120, 330 120, 260 50, 100 50, 170 120))', ewkt='POLYGON ((150.0000000000000000 150.0000000000000000, 410.0000000000000000 150.0000000000000000, 280.0000000000000000 20.0000000000000000, 20.0000000000000000 20.0000000000000000, 150.0000000000000000 150.0000000000000000), (170.0000000000000000 120.0000000000000000, 330.0000000000000000 120.0000000000000000, 260.0000000000000000 50.0000000000000000, 100.0000000000000000 50.0000000000000000, 170.0000000000000000 120.0000000000000000))'),
+ TestGeom('MULTIPOINT(10 80, 110 170, 110 120)', ewkt='MULTIPOINT (10.0000000000000000 80.0000000000000000, 110.0000000000000000 170.0000000000000000, 110.0000000000000000 120.0000000000000000)'),
+ TestGeom('MULTILINESTRING((110 100, 40 30, 180 30),(170 30, 110 90, 50 30))', ewkt='MULTILINESTRING ((110.0000000000000000 100.0000000000000000, 40.0000000000000000 30.0000000000000000, 180.0000000000000000 30.0000000000000000), (170.0000000000000000 30.0000000000000000, 110.0000000000000000 90.0000000000000000, 50.0000000000000000 30.0000000000000000))'),
+ TestGeom('MULTIPOLYGON(((110 110, 70 200, 150 200, 110 110),(110 110, 100 180, 120 180, 110 110)),((110 110, 150 20, 70 20, 110 110),(110 110, 120 40, 100 40, 110 110)))', ewkt='MULTIPOLYGON (((110.0000000000000000 110.0000000000000000, 70.0000000000000000 200.0000000000000000, 150.0000000000000000 200.0000000000000000, 110.0000000000000000 110.0000000000000000), (110.0000000000000000 110.0000000000000000, 100.0000000000000000 180.0000000000000000, 120.0000000000000000 180.0000000000000000, 110.0000000000000000 110.0000000000000000)), ((110.0000000000000000 110.0000000000000000, 150.0000000000000000 20.0000000000000000, 70.0000000000000000 20.0000000000000000, 110.0000000000000000 110.0000000000000000), (110.0000000000000000 110.0000000000000000, 120.0000000000000000 40.0000000000000000, 100.0000000000000000 40.0000000000000000, 110.0000000000000000 110.0000000000000000)))'),
+ TestGeom('GEOMETRYCOLLECTION(POINT(110 260), LINESTRING(110 0, 110 60))', ewkt='GEOMETRYCOLLECTION (POINT (110.0000000000000000 260.0000000000000000), LINESTRING (110.0000000000000000 0.0000000000000000, 110.0000000000000000 60.0000000000000000))'),
+ )
+
+# Polygons
+polygons = (TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 10 90, 90 90, 90 10, 10 10) ))',
+ n_i=1, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0))
+ ),
+ TestGeom('POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))',
+ n_i=0, ext_ring_cs=((0, 0), (0, 100), (100, 100), (100, 0), (0, 0))
+ ),
+ TestGeom('POLYGON ((-95.3848703124799471 29.7056021479768511, -95.3851905195191847 29.7046588196500281, -95.3859356966379011 29.7025053545605502, -95.3860723000647539 29.7020963367038391, -95.3871517697222089 29.6989779021280995, -95.3865578518265522 29.6990856888057202, -95.3862634205175226 29.6999471753441782, -95.3861991779541967 29.6999591988978615, -95.3856773799358137 29.6998323107113578, -95.3856209915427229 29.6998005235473741, -95.3855833545501639 29.6996619391729801, -95.3855776331865002 29.6996232659570047, -95.3850162731712885 29.6997236706530536, -95.3831047357410284 29.7000847603095082, -95.3829800724914776 29.7000676365023502, -95.3828084594470909 29.6999969684031200, -95.3828131504821499 29.6999090511531065, -95.3828022942979601 29.6998152117366025, -95.3827893930918833 29.6997790953076759, -95.3825174668099862 29.6998267772748825, -95.3823521544804862 29.7000451723151606, -95.3820491918785223 29.6999682034582335, -95.3817932841505893 29.6999640407204772, -95.3815438924600443 29.7005983712500630, -95.3807812390843424 29.7007538492921590, -95.3778578936435935 29.7012966201172048, -95.3770817300034679 29.7010555145969093, -95.3772763716395957 29.7004995005932031, -95.3769891024414420 29.7005797730360186, -95.3759855007185990 29.7007754783987821, -95.3759516423090474 29.7007305400669388, -95.3765252155960042 29.6989549173240874, -95.3766842746727832 29.6985134987163164, -95.3768510987262914 29.6980530300744938, -95.3769198676258014 29.6977137204527573, -95.3769616670751930 29.6973351617272172, -95.3770309229297766 29.6969821084304186, -95.3772352596880637 29.6959751305871613, -95.3776232419333354 29.6945439060847463, -95.3776849628727064 29.6943364710766069, -95.3779699491714723 29.6926548349458947, -95.3781945479573494 29.6920088336742545, -95.3785807118394189 29.6908279316076005, -95.3787441368896651 29.6908846275832197, -95.3787903214163890 29.6907152912461640, -95.3791765069353659 29.6893335376821526, -95.3794935959513026 29.6884781789101595, -95.3796592071232112 29.6880066681407619, -95.3799788182090111 29.6873687353035081, -95.3801545516183893 29.6868782380716993, -95.3801258908302145 29.6867756621337762, -95.3801104284899566 29.6867229678809572, -95.3803803523746154 29.6863753372986459, -95.3821028558287622 29.6837392961470421, -95.3827289584682205 29.6828097375216160, -95.3827494698109035 29.6790739156259278, -95.3826022014838486 29.6776502228345507, -95.3825047356438063 29.6765773006280753, -95.3823473035336917 29.6750405250369127, -95.3824540163482055 29.6750076408228587, -95.3838984230304305 29.6745679207378679, -95.3916547074937426 29.6722459226508377, -95.3926154662749468 29.6719609085105489, -95.3967246645118081 29.6707316485589736, -95.3974588054406780 29.6705065336410989, -95.3978523748756828 29.6703795547846845, -95.3988598162279970 29.6700874981900853, -95.3995628600665952 29.6698505300412414, -95.4134721665944170 29.6656841279906232, -95.4143262068232616 29.6654291174019278, -95.4159685142480214 29.6649750989232288, -95.4180067396277565 29.6643253024318021, -95.4185886692196590 29.6641482768691063, -95.4234155309609662 29.6626925393704788, -95.4287785503196346 29.6611023620959706, -95.4310287312749352 29.6604222580752648, -95.4320295629628959 29.6603361318136720, -95.4332899683975739 29.6600560661713608, -95.4342675748811047 29.6598454934599900, -95.4343110414310871 29.6598411486215490, -95.4345576779282538 29.6598147020668499, -95.4348823041721630 29.6597875803673112, -95.4352827715209457 29.6597762346946681, -95.4355290431309982 29.6597827926562374, -95.4359197997999331 29.6598014511782715, -95.4361907884752156 29.6598444333523368, -95.4364608955807228 29.6598901433108217, -95.4367250147512323 29.6599494499910712, -95.4364898759758091 29.6601880616540186, -95.4354501111810691 29.6616378572201107, -95.4381459623171224 29.6631265631655126, -95.4367852490863129 29.6642266600024023, -95.4370040894557263 29.6643425389568769, -95.4367078350812648 29.6645492592343238, -95.4366081749871285 29.6646291473027297, -95.4358539359938192 29.6652308742342932, -95.4350327668927889 29.6658995989314462, -95.4350580905272921 29.6678812477895271, -95.4349710541447536 29.6680054925936965, -95.4349500440473548 29.6671410080890006, -95.4341492724148850 29.6678790545191688, -95.4340248868274728 29.6680353198492135, -95.4333227845797438 29.6689245624945990, -95.4331325652123326 29.6691616138940901, -95.4321314741096955 29.6704473333237253, -95.4320435792664341 29.6702578985411982, -95.4320147929883547 29.6701800936425109, -95.4319764538662980 29.6683246590817085, -95.4317490976340679 29.6684974372577166, -95.4305958185342718 29.6694049049170374, -95.4296600735653016 29.6701723430938493, -95.4284928989940937 29.6710931793380972, -95.4274630532378580 29.6719378813640091, -95.4273056811974811 29.6720684984625791, -95.4260554084574864 29.6730668861566969, -95.4253558063699643 29.6736342467365724, -95.4249278826026028 29.6739557343648919, -95.4248648873821423 29.6745400910786152, -95.4260016131471929 29.6750987014005858, -95.4258567183010911 29.6753452063069929, -95.4260238081486847 29.6754322077221353, -95.4258707374502393 29.6756647377294307, -95.4257951755816691 29.6756407098663360, -95.4257701599566985 29.6761077719536068, -95.4257726684792260 29.6761711204603955, -95.4257980187195614 29.6770219651929423, -95.4252712669032519 29.6770161558853758, -95.4249234392992065 29.6770068683962300, -95.4249574272905789 29.6779707498635759, -95.4244725881033702 29.6779825646764159, -95.4222269476429545 29.6780711474441716, -95.4223032371999267 29.6796029391538809, -95.4239133706588945 29.6795331493690355, -95.4224579084327331 29.6813706893847780, -95.4224290108823965 29.6821953228763924, -95.4230916478977349 29.6822130268724109, -95.4222928279595521 29.6832041816675343, -95.4228763710016352 29.6832087677714505, -95.4223401691637179 29.6838987872753748, -95.4211655906087088 29.6838784024852984, -95.4201984153205558 29.6851319258758082, -95.4206156387716362 29.6851623398125319, -95.4213438084897660 29.6851763011334739, -95.4212071118618752 29.6853679931624974, -95.4202651399651245 29.6865313962980508, -95.4172061157659783 29.6865816431043932, -95.4182217951255183 29.6872251197301544, -95.4178664826439160 29.6876750901471631, -95.4180678442928780 29.6877960336377207, -95.4188763472917572 29.6882826379510938, -95.4185374500596311 29.6887137897831934, -95.4182121713132290 29.6885097429738813, -95.4179857231741551 29.6888118367840086, -95.4183106010563620 29.6890048676118212, -95.4179489865331334 29.6894546700979056, -95.4175581746284820 29.6892323606815438, -95.4173439957341571 29.6894990139807007, -95.4177411199311081 29.6897435034738422, -95.4175789200209721 29.6899207529979208, -95.4170598559864800 29.6896042165807508, -95.4166733682539814 29.6900891174451367, -95.4165941362704331 29.6900347214235047, -95.4163537218065301 29.6903529467753238, -95.4126843270708775 29.6881086357212780, -95.4126604121378392 29.6880942378803496, -95.4126672298953338 29.6885951670109982, -95.4126680884821923 29.6887052446594275, -95.4158080137241882 29.6906382377959339, -95.4152061403821961 29.6910871045531586, -95.4155842583188161 29.6917382915894308, -95.4157426793520358 29.6920726941677096, -95.4154520563662203 29.6922052332446427, -95.4151389936167078 29.6923261661269571, -95.4148649784384872 29.6924343866430256, -95.4144051352401590 29.6925623927348106, -95.4146792019416665 29.6926770338507744, -95.4148824479948985 29.6928117893696388, -95.4149851734360226 29.6929823719519774, -95.4140436551925291 29.6929626643100946, -95.4140465993023241 29.6926545917254892, -95.4137269186733334 29.6927395764256090, -95.4137372859685513 29.6935432485666624, -95.4135702836218655 29.6933186678088283, -95.4133925235973237 29.6930415229852152, -95.4133017035615580 29.6928685062036166, -95.4129588921634593 29.6929391128977862, -95.4125107395559695 29.6930481664661485, -95.4102647423187307 29.6935850183258019, -95.4081931340840157 29.6940907430947760, -95.4078783596459772 29.6941703429951609, -95.4049213975000043 29.6948723732981961, -95.4045944244127071 29.6949626434239207, -95.4045865139788134 29.6954109019001358, -95.4045953345484037 29.6956972800496963, -95.4038879332535146 29.6958296089365490, -95.4040366394459340 29.6964389004769842, -95.4032774779020798 29.6965643341263892, -95.4026066501239853 29.6966646227683881, -95.4024991226393837 29.6961389766619703, -95.4011781398631911 29.6963566063186377, -95.4011524097636112 29.6962596176762190, -95.4018184046368276 29.6961399466727336, -95.4016995838361908 29.6956442609415099, -95.4007100753964608 29.6958900524002978, -95.4008032469935188 29.6962639900781404, -95.3995660267125487 29.6965636449370329, -95.3996140564775601 29.6967877962763644, -95.3996364430014410 29.6968901984825280, -95.3984003269631842 29.6968679634805746, -95.3981442026887265 29.6983660679730335, -95.3980178461957706 29.6990890276252415, -95.3977097967130163 29.7008526152273049, -95.3962347157626027 29.7009697553607630, -95.3951949050136250 29.7004740386619019, -95.3957564950617183 29.6990281830553187, -95.3965927101519924 29.6968771129030706, -95.3957496517238184 29.6970800358387095, -95.3957720559467361 29.6972264611230727, -95.3957391586571788 29.6973548894558732, -95.3956286413405365 29.6974949857280883, -95.3955111053256957 29.6975661086270186, -95.3953215342724121 29.6976022763384790, -95.3951795558443365 29.6975846977491038, -95.3950369632041060 29.6975175779330200, -95.3949401089966500 29.6974269267953304, -95.3948740281415581 29.6972903308506346, -95.3946650813866910 29.6973397326847923, -95.3947654059391112 29.6974882560192022, -95.3949627316619768 29.6980355864961858, -95.3933200807862249 29.6984590863712796, -95.3932606497523494 29.6984464798710839, -95.3932983699113350 29.6983154306484352, -95.3933058014696655 29.6982165816983610, -95.3932946347785133 29.6981089778195759, -95.3931780601756287 29.6977068906794841, -95.3929928222970602 29.6977541771878180, -95.3930873169846478 29.6980676264932946, -95.3932743746374570 29.6981249406449663, -95.3929512584706316 29.6989526513922222, -95.3919850280655197 29.7014358632108646, -95.3918950918929056 29.7014169320765724, -95.3916928317890296 29.7019232352846423, -95.3915424614970959 29.7022988712928289, -95.3901530441668939 29.7058519502930061, -95.3899656322116698 29.7059156823562418, -95.3897628748670883 29.7059900058266777, -95.3896062677805787 29.7060738276384946, -95.3893941800512266 29.7061891695242046, -95.3892150365492455 29.7062641292949436, -95.3890502563035199 29.7063339729630940, -95.3888717930715586 29.7063896908080736, -95.3886925428988945 29.7064453871994978, -95.3885376849411983 29.7064797304524149, -95.3883284158984139 29.7065153575050189, -95.3881046767627794 29.7065368368267357, -95.3878809284696132 29.7065363048447537, -95.3876046356120924 29.7065288525102424, -95.3873060894974714 29.7064822806001452, -95.3869851943158409 29.7063993367575350, -95.3865967896568065 29.7062870572919202, -95.3861785624983156 29.7061492099008184, -95.3857375009733488 29.7059887337478798, -95.3854573290902152 29.7058683664514618, -95.3848703124799471 29.7056021479768511))',
+ n_i=0, ext_ring_cs=False
+ ),
+
+ )
+
+# MultiPolygons
+multipolygons = (TestGeom('MULTIPOLYGON (((100 20, 180 20, 180 100, 100 100, 100 20)), ((20 100, 100 100, 100 180, 20 180, 20 100)), ((100 180, 180 180, 180 260, 100 260, 100 180)), ((180 100, 260 100, 260 180, 180 180, 180 100)))', valid=True, n_p=4),
+ TestGeom('MULTIPOLYGON (((60 300, 320 220, 260 60, 60 100, 60 300)), ((60 300, 320 220, 260 60, 60 100, 60 300)))', valid=False),
+ TestGeom('MULTIPOLYGON (((180 60, 240 160, 300 60, 180 60)), ((80 80, 180 60, 160 140, 240 160, 360 140, 300 60, 420 100, 320 280, 120 260, 80 80)))', valid=True, n_p=2),
+ )
+
+
+# Points
+points = (TestGeom('POINT (5 23)', x=5.0, y=23.0, centroid=(5.0, 23.0)),
+ TestGeom('POINT (-95.338492 29.723893)', x=-95.338492, y=29.723893, centroid=(-95.338492, 29.723893)),
+ TestGeom('POINT(1.234 5.678)', x=1.234, y=5.678, centroid=(1.234, 5.678)),
+ TestGeom('POINT(4.321 8.765)', x=4.321, y=8.765, centroid=(4.321, 8.765)),
+ TestGeom('POINT(10 10)', x=10, y=10, centroid=(10., 10.)),
+ TestGeom('POINT (5 23 8)', x=5.0, y=23.0, z=8.0, centroid=(5.0, 23.0)),
+ )
+
+# MultiPoints
+multipoints = (TestGeom('MULTIPOINT(10 10, 20 20 )', points=((10., 10.), (20., 20.)), centroid=(15., 15.)),
+ TestGeom('MULTIPOINT(10 10, 20 20, 10 20, 20 10)',
+ points=((10., 10.), (20., 20.), (10., 20.), (20., 10.)),
+ centroid=(15., 15.)),
+ )
+
+# LineStrings
+linestrings = (TestGeom('LINESTRING (60 180, 120 100, 180 180)', centroid=(120, 140)),
+# TestGeom('LINESTRING (80 0, 80 120, 120 120, 120 0))', centroid=(100, 69)),
+ TestGeom('LINESTRING (0 0, 5 5, 10 5, 10 10)', centroid=(6.1611652351681556, 4.6966991411008934)),
+ )
+
+# MultiLineStrings
+multilinestrings = (TestGeom('MULTILINESTRING ((0 0, 0 100), (100 0, 100 100))', centroid=(50, 50)),
+# TestGeom('MULTILINESTRING ((0 0, 0 200, 200 200, 200 0, 0 0), (60 180, 20 180, 20 140, 60 140, 60 180))', centroid=(90, 100)),
+ TestGeom('MULTILINESTRING ((20 20, 60 60), (20 -20, 60 -60), (-20 -20, -60 -60), (-20 20, -60 60), (-80 0, 0 80, 80 0, 0 -80, -80 0), (-40 20, -40 -20), (-20 40, 20 40), (40 20, 40 -20), (20 -40, -20 -40))',
+ centroid=(0, 0)),
+ )
+
+# ====================================================
+# Topology Operations
+
+topology_geoms = ( (TestGeom('POLYGON ((-5.0 0.0, -5.0 10.0, 5.0 10.0, 5.0 0.0, -5.0 0.0))'),
+ TestGeom('POLYGON ((0.0 -5.0, 0.0 5.0, 10.0 5.0, 10.0 -5.0, 0.0 -5.0))')
+ ),
+ )
+
+intersect_geoms = ( TestGeom('POLYGON ((5 5,5 0,0 0,0 5,5 5))'),
+ )
+
+union_geoms = ( TestGeom('POLYGON ((-5 0,-5 10,5 10,5 5,10 5,10 -5,0 -5,0 0,-5 0))'),
+ )
+
+diff_geoms = ( TestGeom('POLYGON ((-5 0,-5 10,5 10,5 5,0 5,0 0,-5 0))'),
+ )
+
+
+relate_geoms = ( (TestGeom('MULTIPOINT(80 70, 20 20, 200 170, 140 120)'),
+ TestGeom('MULTIPOINT(80 170, 140 120, 200 80, 80 70)'),
+ '0F0FFF0F2', True,),
+ (TestGeom('POINT(20 20)'), TestGeom('POINT(40 60)'),
+ 'FF0FFF0F2', True,),
+ (TestGeom('POINT(110 110)'), TestGeom('LINESTRING(200 200, 110 110, 200 20, 20 20, 110 110, 20 200, 200 200)'),
+ '0FFFFF1F2', True,),
+ (TestGeom('MULTILINESTRING((20 20, 90 20, 170 20), (90 20, 90 80, 90 140))'),
+ TestGeom('MULTILINESTRING((90 20, 170 100, 170 140), (130 140, 130 60, 90 20, 20 90, 90 20))'),
+ 'FF10F0102', True,),
+ )
+#((TestGeom('POLYGON ((545 317, 617 379, 581 321, 545 317))')),
+# (TestGeom('POLYGON ((484 290, 558 359, 543 309, 484 290))'))),
View
207 django/contrib/gis/tests/test_geos.py
@@ -0,0 +1,207 @@
+import unittest
+from copy import copy
+from django.contrib.gis.geos import GEOSGeometry, GEOSException
+from geometries import *
+
+class GeosTest2(unittest.TestCase):
+
+ def test010_wkt(self):
+ "Testing WKT output."
+ for g in wkt_out:
+ geom = GEOSGeometry(g.wkt)
+ self.assertEqual(g.ewkt, geom.wkt)
+
+ def test011_hex(self):
+ "Testing HEX output."
+ for g in hex_wkt:
+ geom = GEOSGeometry(g.wkt)
+ self.assertEqual(g.hex, geom.hex)
+
+ def test02_points(self):
+ "Testing Point objects."
+ prev = GEOSGeometry('POINT(0 0)')
+
+ for p in points:
+ pnt = GEOSGeometry(p.wkt)
+ self.assertEqual(pnt.geom_type, 'Point')
+ self.assertEqual(pnt.geom_typeid, 0)
+ self.assertEqual(p.x, pnt.x)
+ self.assertEqual(p.y, pnt.y)
+ self.assertEqual(True, pnt == GEOSGeometry(p.wkt))
+ self.assertEqual(False, pnt == prev)
+ prev = pnt
+
+ self.assertAlmostEqual(p.x, pnt.tuple[0], 9)
+ self.assertAlmostEqual(p.y, pnt.tuple[1], 9)
+ if hasattr(p, 'z'):
+ self.assertEqual(True, pnt.hasz)
+ self.assertEqual(p.z, pnt.z)
+ self.assertEqual(p.z, pnt.tuple[2], 9)
+ else:
+ self.assertEqual(False, pnt.hasz)
+ self.assertEqual(None, pnt.z)
+
+
+ self.assertEqual(p.centroid, pnt.centroid.tuple)
+
+ def test02_multipoints(self):
+ "Testing MultiPoint objects."
+ for mp in multipoints:
+ mpnt = GEOSGeometry(mp.wkt)
+ self.assertEqual(mpnt.geom_type, 'MultiPoint')
+ self.assertEqual(mpnt.geom_typeid, 4)
+
+ self.assertAlmostEqual(mp.centroid[0], mpnt.centroid.tuple[0], 9)
+ self.assertAlmostEqual(mp.centroid[1], mpnt.centroid.tuple[1], 9)
+
+ self.assertEqual(mp.centroid, mpnt.centroid.tuple)
+ self.assertEqual(mp.points, tuple(m.tuple for m in mpnt))
+ for p in mpnt:
+ self.assertEqual(p.geom_type, 'Point')
+ self.assertEqual(p.geom_typeid, 0)
+ self.assertEqual(p.empty, False)
+ self.assertEqual(p.valid, True)
+
+ def test03_polygons(self):
+ "Testing Polygon objects."
+
+ prev = GEOSGeometry('POINT(0 0)')
+
+ for p in polygons:
+ poly = GEOSGeometry(p.wkt)
+ self.assertEqual(poly.geom_type, 'Polygon')
+ self.assertEqual(poly.geom_typeid, 3)
+ self.assertEqual(poly.empty, False)
+ self.assertEqual(poly.ring, False)
+ self.assertEqual(p.n_i, poly.num_interior_rings)
+
+ # Testing the geometry equivalence
+ self.assertEqual(True, poly == GEOSGeometry(p.wkt))
+ self.assertEqual(False, poly == prev)
+ prev = poly
+
+ # Testing the exterior ring
+ ring = poly.exterior_ring
+ self.assertEqual(ring.geom_type, 'LinearRing')
+ self.assertEqual(ring.geom_typeid, 2)
+ if p.ext_ring_cs:
+ self.assertEqual(p.ext_ring_cs, ring.tuple)
+
+ def test03_multipolygons(self):
+ "Testing MultiPolygon objects."
+
+ prev = GEOSGeometry('POINT (0 0)')
+
+ for mp in multipolygons:
+ mpoly = GEOSGeometry(mp.wkt)
+ self.assertEqual(mpoly.geom_type, 'MultiPolygon')
+ self.assertEqual(mpoly.geom_typeid, 6)
+ self.assertEqual(mp.valid, mpoly.valid)
+
+ if mp.valid:
+ self.assertEqual(mp.n_p, mpoly.num_geom)
+ self.assertEqual(mp.n_p, len(mpoly))
+ for p in mpoly:
+ self.assertEqual(p.geom_type, 'Polygon')
+ self.assertEqual(p.geom_typeid, 3)
+ self.assertEqual(p.valid, True)
+
+ def test04_linestring(self):
+ "Testing LineString objects."
+
+ prev = GEOSGeometry('POINT(0 0)')
+
+ for l in linestrings:
+ ls = GEOSGeometry(l.wkt)
+ self.assertEqual(ls.geom_type, 'LineString')
+ self.assertEqual(ls.geom_typeid, 1)
+ self.assertEqual(ls.empty, False)
+ self.assertEqual(ls.ring, False)
+ self.assertEqual(l.centroid, ls.centroid.tuple)
+ self.assertEqual(True, ls == GEOSGeometry(l.wkt))
+ self.assertEqual(False, ls == prev)
+ prev = ls
+
+ def test04_multilinestring(self):
+ "Testing MultiLineString objects."
+
+ prev = GEOSGeometry('POINT(0 0)')
+
+ for l in multilinestrings:
+ ml = GEOSGeometry(l.wkt)
+ self.assertEqual(ml.geom_type, 'MultiLineString')
+ self.assertEqual(ml.geom_typeid, 5)
+
+ self.assertAlmostEqual(l.centroid[0], ml.centroid.x, 9)
+ self.assertAlmostEqual(l.centroid[1], ml.centroid.y, 9)
+
+
+ self.assertEqual(True, ml == GEOSGeometry(l.wkt))
+ self.assertEqual(False, ml == prev)
+ prev = ml
+
+ for ls in ml:
+ self.assertEqual(ls.geom_type, 'LineString')
+ self.assertEqual(ls.geom_typeid, 1)
+ self.assertEqual(ls.empty, False)
+
+ #def test05_linearring(self):
+ # "Testing LinearRing objects."
+ # pass
+
+ def test09_relate_pattern(self):
+ "Testing relate() and relate_pattern()."
+
+ g = GEOSGeometry('POINT (0 0)')
+ self.assertRaises(GEOSException, g.relate_pattern, 0, 'invalid pattern, yo')
+
+ for i in xrange(len(relate_geoms)):
+ g_tup = relate_geoms[i]
+ a = GEOSGeometry(g_tup[0].wkt)
+ b = GEOSGeometry(g_tup[1].wkt)
+ pat = g_tup[2]
+ result = g_tup[3]
+ self.assertEqual(result, a.relate_pattern(b, pat))
+ self.assertEqual(g_tup[2], a.relate(b))
+
+ def test10_intersection(self):
+ "Testing intersects() and intersection()."
+
+ for i in xrange(len(topology_geoms)):
+ g_tup = topology_geoms[i]
+ a = GEOSGeometry(g_tup[0].wkt)
+ b = GEOSGeometry(g_tup[1].wkt)
+ i1 = GEOSGeometry(intersect_geoms[i].wkt)
+
+ self.assertEqual(True, a.intersects(b))
+ i2 = a.intersection(b)
+ self.assertEqual(i1, i2)
+
+ def test11_union(self):
+ "Testing union()."
+ for i in xrange(len(topology_geoms)):
+ g_tup = topology_geoms[i]
+ a = GEOSGeometry(g_tup[0].wkt)
+ b = GEOSGeometry(g_tup[1].wkt)
+ u1 = GEOSGeometry(union_geoms[i].wkt)
+ u2 = a.union(b)
+ self.assertEqual(u1, u2)
+
+ def test12_difference(self):
+ "Testing difference()."
+ for i in xrange(len(topology_geoms)):
+ g_tup = topology_geoms[i]
+ a = GEOSGeometry(g_tup[0].wkt)
+ b = GEOSGeometry(g_tup[1].wkt)
+ d1 = GEOSGeometry(diff_geoms[i].wkt)
+ d2 = a.difference(b)
+ self.assertEqual(d1, d2)
+
+
+def suite():
+ s = unittest.TestSuite()
+ s.addTest(unittest.makeSuite(GeosTest2))
+ return s
+
+def run(verbosity=2):
+ unittest.TextTestRunner(verbosity=verbosity).run(suite())
View
67 django/contrib/gis/tests/test_with_swig.py
@@ -0,0 +1,67 @@
+import unittest
+from django.contrib.gis.geos import GEOSGeometry, hex_to_wkt, wkt_to_hex, centroid
+from geos import geomToWKT, geomToHEX, geomFromWKT, geomFromHEX
+from geometries import swig_geoms as geos_geoms
+
+class GeosTest(unittest.TestCase):
+
+ def test001_hex_to_wkt(self):
+ "Testing HEX to WKT conversion."
+ for g in geos_geoms:
+ wkt1 = geomToWKT(geomFromWKT(g.wkt))
+ wkt2 = hex_to_wkt(GEOSGeometry(g.wkt, 'wkt').hex)
+ self.assertEqual(wkt1, wkt2)
+
+ def test002_wkt_to_hex(self):
+ "Testing WKT to HEX conversion."
+ for g in geos_geoms:
+ self.assertEqual(geomToHEX(geomFromWKT(g.wkt)), wkt_to_hex(g.wkt))
+
+ def test003_centroid(self):
+ "Testing the centroid property."
+ for g in geos_geoms:
+ wkt1 = (centroid(g.wkt, geom_type='wkt')).wkt
+ wkt2 = geomToWKT((geomFromWKT(g.wkt)).getCentroid())
+ self.assertEqual(wkt1, wkt2)
+
+ def test004_area(self):
+ "Testing the area property."
+ for g in geos_geoms:
+ g1 = geomFromWKT(g.wkt)
+ g2 = GEOSGeometry(g.wkt, 'wkt')
+ self.assertEqual(g1.area(), g2.area)
+
+ def test005_geom_type(self):
+ "Testing the geom_type property."
+ for g in geos_geoms:
+ g1 = geomFromWKT(g.wkt)
+ g2 = GEOSGeometry(g.wkt, 'wkt')
+ self.assertEqual(g1.geomType(), g2.geom_type)
+
+ def test005_geom_id(self):
+ "Testing the geom_typeid property."
+ for g in geos_geoms:
+ g1 = geomFromWKT(g.wkt)
+ g2 = GEOSGeometry(g.wkt, 'wkt')
+ self.assertEqual(g1.typeId(), g2.geom_typeid)
+
+ def test006_ngeom(self):
+ "Testing the num_geom property."
+ for g in geos_geoms:
+ g1 = geomFromWKT(g.wkt)
+ g2 = GEOSGeometry(g.wkt, 'wkt')
+ self.assertEqual(g1.getNumGeometries(), g2.num_geom)
+
+ def test007_ncoords(self):
+ "Testing the num_coords property."
+ for g in geos_geoms:
+ g2 = GEOSGeometry(g.wkt, 'wkt')
+ self.assertEqual(g.ncoords, g2.num_coords)
+
+def suite():
+ s = unittest.TestSuite()
+ s.addTest(unittest.makeSuite(GeosTest))
+ return s
+
+def run(verbosity=2):
+ unittest.TextTestRunner(verbosity=verbosity).run(suite())
Please sign in to comment.
Something went wrong with that request. Please try again.