mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
gis: geos: fully-mutable geometries have landed; fixed GEOSPointer boolean value; equivalence now uses equals_exact (vertex-by-vertex matching); added in-place set operations; improved tests; getquoted() will return ST_* for PostGIS 1.2.2+.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5786 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
ffcc38ebe8
commit
056f87ab28
@ -12,7 +12,7 @@ from types import StringType, IntType, FloatType
|
|||||||
# Python and GEOS-related dependencies.
|
# Python and GEOS-related dependencies.
|
||||||
import re
|
import re
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY, ISQLQuote
|
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY, ISQLQuote, GEOM_FUNC_PREFIX
|
||||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs
|
from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs
|
||||||
if HAS_NUMPY: from numpy import ndarray, array
|
if HAS_NUMPY: from numpy import ndarray, array
|
||||||
@ -87,7 +87,7 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Destroys this geometry -- only if the pointer is valid and whether or not it belongs to a parent."
|
"Destroys this geometry -- only if the pointer is valid and whether or not it belongs to a parent."
|
||||||
#print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
#print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
||||||
# Only calling destroy on valid pointers not spawned from a parent
|
# Only calling destroy on valid pointers not spawned from a parent
|
||||||
if self._ptr.valid and not self._parent: lgeos.GEOSGeom_destroy(self._ptr())
|
if self._ptr.valid and not self._parent: lgeos.GEOSGeom_destroy(self._ptr())
|
||||||
|
|
||||||
@ -101,11 +101,11 @@ class GEOSGeometry(object):
|
|||||||
# Comparison operators
|
# Comparison operators
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"Equivalence testing."
|
"Equivalence testing."
|
||||||
return self.equals(other)
|
return self.equals_exact(other)
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
"The not equals operator."
|
"The not equals operator."
|
||||||
return not self.equals(other)
|
return not self.equals_exact(other)
|
||||||
|
|
||||||
### Geometry set-like operations ###
|
### Geometry set-like operations ###
|
||||||
# Thanks to Sean Gillies for inspiration:
|
# Thanks to Sean Gillies for inspiration:
|
||||||
@ -115,21 +115,76 @@ class GEOSGeometry(object):
|
|||||||
"Returns the union of this Geometry and the other."
|
"Returns the union of this Geometry and the other."
|
||||||
return self.union(other)
|
return self.union(other)
|
||||||
|
|
||||||
|
# g1 |= g2
|
||||||
|
def __ior__(self, other):
|
||||||
|
"Reassigns this Geometry to the union of this Geometry and the other."
|
||||||
|
return self.union(other)
|
||||||
|
|
||||||
# g = g1 & g2
|
# g = g1 & g2
|
||||||
def __and__(self, other):
|
def __and__(self, other):
|
||||||
"Returns the intersection of this Geometry and the other."
|
"Returns the intersection of this Geometry and the other."
|
||||||
return self.intersection(other)
|
return self.intersection(other)
|
||||||
|
|
||||||
|
# g1 &= g2
|
||||||
|
def __iand__(self, other):
|
||||||
|
"Reassigns this Geometry to the intersection of this Geometry and the other."
|
||||||
|
return self.intersection(other)
|
||||||
|
|
||||||
# g = g1 - g2
|
# g = g1 - g2
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
"Return the difference this Geometry and the other."
|
"Return the difference this Geometry and the other."
|
||||||
return self.difference(other)
|
return self.difference(other)
|
||||||
|
|
||||||
|
# g1 -= g2
|
||||||
|
def __isub__(self, other):
|
||||||
|
"Reassigns this Geometry to the difference of this Geometry and the other."
|
||||||
|
return self.difference(other)
|
||||||
|
|
||||||
# g = g1 ^ g2
|
# g = g1 ^ g2
|
||||||
def __xor__(self, other):
|
def __xor__(self, other):
|
||||||
"Return the symmetric difference of this Geometry and the other."
|
"Return the symmetric difference of this Geometry and the other."
|
||||||
return self.sym_difference(other)
|
return self.sym_difference(other)
|
||||||
|
|
||||||
|
# g1 ^= g2
|
||||||
|
def __ixor__(self, other):
|
||||||
|
"Reassigns this Geometry to the symmetric difference of this Geometry and the other."
|
||||||
|
return self.sym_difference(other)
|
||||||
|
|
||||||
|
def _nullify(self):
|
||||||
|
"""During initialization of geometries from other geometries, this routine is
|
||||||
|
used to nullify any parent geometries (since they will now be missing memory
|
||||||
|
components) and to nullify the geometry itself to prevent future access.
|
||||||
|
Only the address (an integer) of the current geometry is returned for use in
|
||||||
|
initializing the new geometry."""
|
||||||
|
# First getting the memory address of the geometry.
|
||||||
|
address = self._ptr()
|
||||||
|
|
||||||
|
# If the geometry is a child geometry, then the parent geometry pointer is
|
||||||
|
# nullified.
|
||||||
|
if self._parent: self._parent.nullify()
|
||||||
|
|
||||||
|
# Nullifying the geometry pointer
|
||||||
|
self._ptr.nullify()
|
||||||
|
|
||||||
|
return address
|
||||||
|
|
||||||
|
def _reassign(self, new_geom):
|
||||||
|
"Internal routine for reassigning internal pointer to a new geometry."
|
||||||
|
# Only can re-assign when given a pointer or a geometry.
|
||||||
|
if not isinstance(new_geom, (GEOSPointer, GEOSGeometry)):
|
||||||
|
raise TypeError, 'cannot reassign geometry on given type: %s' % type(new_geom)
|
||||||
|
gtype = new_geom.geom_type
|
||||||
|
|
||||||
|
# Re-assigning the internal GEOSPointer to the new geometry, nullifying
|
||||||
|
# the new Geometry in the process.
|
||||||
|
if isinstance(new_geom, GEOSGeometry): self._ptr.set(new_geom._nullify())
|
||||||
|
else: self._ptr = new_geom
|
||||||
|
|
||||||
|
# The new geometry class may be different from the original, so setting
|
||||||
|
# the __class__ and populating the internal geometry or ring dictionary.
|
||||||
|
self.__class__ = GEOS_CLASSES[gtype]
|
||||||
|
if isinstance(self, (Polygon, GeometryCollection)): self._populate()
|
||||||
|
|
||||||
#### Psycopg2 database adaptor routines ####
|
#### Psycopg2 database adaptor routines ####
|
||||||
def __conform__(self, proto):
|
def __conform__(self, proto):
|
||||||
# Does the given protocol conform to what Psycopg2 expects?
|
# Does the given protocol conform to what Psycopg2 expects?
|
||||||
@ -140,7 +195,8 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
def getquoted(self):
|
def getquoted(self):
|
||||||
"Returns a properly quoted string for use in PostgresSQL/PostGIS."
|
"Returns a properly quoted string for use in PostgresSQL/PostGIS."
|
||||||
return "GeometryFromText('%s', %s)" % (self.wkt, self.srid or -1)
|
# GeometryFromText() is ST_GeometryFromText() in PostGIS >= 1.2.2
|
||||||
|
return "%sGeometryFromText('%s', %s)" % (GEOM_FUNC_PREFIX, self.wkt, self.srid or -1)
|
||||||
|
|
||||||
#### Coordinate Sequence Routines ####
|
#### Coordinate Sequence Routines ####
|
||||||
@property
|
@property
|
||||||
@ -299,10 +355,8 @@ class GEOSGeometry(object):
|
|||||||
def get_srid(self):
|
def get_srid(self):
|
||||||
"Gets the SRID for the geometry, returns None if no SRID is set."
|
"Gets the SRID for the geometry, returns None if no SRID is set."
|
||||||
s = lgeos.GEOSGetSRID(self._ptr())
|
s = lgeos.GEOSGetSRID(self._ptr())
|
||||||
if s == 0:
|
if s == 0: return None
|
||||||
return None
|
else: return s
|
||||||
else:
|
|
||||||
return s
|
|
||||||
|
|
||||||
def set_srid(self, srid):
|
def set_srid(self, srid):
|
||||||
"Sets the SRID for the geometry."
|
"Sets the SRID for the geometry."
|
||||||
@ -404,7 +458,7 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
"Clones this Geometry."
|
"Clones this Geometry."
|
||||||
return GEOSGeometry(lgeos.GEOSGeom_clone(self._ptr()))
|
return GEOSGeometry(lgeos.GEOSGeom_clone(self._ptr()), srid=self.srid)
|
||||||
|
|
||||||
# Class mapping dictionary
|
# Class mapping dictionary
|
||||||
from django.contrib.gis.geos.geometries import Point, Polygon, LineString, LinearRing
|
from django.contrib.gis.geos.geometries import Point, Polygon, LineString, LinearRing
|
||||||
|
@ -4,37 +4,25 @@
|
|||||||
"""
|
"""
|
||||||
from ctypes import c_int, c_uint, byref, cast
|
from ctypes import c_int, c_uint, byref, cast
|
||||||
from types import TupleType, ListType
|
from types import TupleType, ListType
|
||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, init_from_geom, get_pointer_arr, GEOM_PTR
|
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, GEOM_PTR
|
||||||
from django.contrib.gis.geos.base import GEOSGeometry
|
from django.contrib.gis.geos.base import GEOSGeometry
|
||||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon
|
from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon
|
||||||
|
|
||||||
def init_from_poly(poly):
|
|
||||||
"Internal routine used for initializing Geometry Collections from Polygons."
|
|
||||||
# Constructing a new Polygon to take control of the rings.
|
|
||||||
p = Polygon(*tuple(ring for ring in poly))
|
|
||||||
|
|
||||||
# If this Polygon came from a GeometryCollection, it is a child
|
|
||||||
# and the parent geometry pointer is nullified.
|
|
||||||
if poly._parent: poly._parent.nullify()
|
|
||||||
|
|
||||||
# Nullifying the polygon pointer
|
|
||||||
poly._ptr.nullify()
|
|
||||||
|
|
||||||
# Returning the address of the new Polygon.
|
|
||||||
return p._ptr()
|
|
||||||
|
|
||||||
class GeometryCollection(GEOSGeometry):
|
class GeometryCollection(GEOSGeometry):
|
||||||
_allowed = (Point, LineString, LinearRing, Polygon)
|
_allowed = (Point, LineString, LinearRing, Polygon)
|
||||||
_typeid = 7
|
_typeid = 7
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
"Initializes a Geometry Collection from a sequence of Geometry objects."
|
||||||
|
# Setting up the collection for creation
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
self._ptr = GEOSPointer(0) # Initially NULL
|
||||||
self._geoms = {}
|
self._geoms = {}
|
||||||
self._parent = False
|
self._parent = None
|
||||||
|
|
||||||
|
# Checking the arguments
|
||||||
if not args:
|
if not args:
|
||||||
raise TypeError, 'Must provide at least one LinearRing to initialize Polygon.'
|
raise TypeError, 'Must provide at least one Geometry to initialize %s.' % self.__class__.__name__
|
||||||
|
|
||||||
if len(args) == 1: # If only one geometry provided or a list of geometries is provided
|
if len(args) == 1: # If only one geometry provided or a list of geometries is provided
|
||||||
if isinstance(args[0], (TupleType, ListType)):
|
if isinstance(args[0], (TupleType, ListType)):
|
||||||
@ -54,23 +42,18 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
|
|
||||||
# Incrementing through each input geometry.
|
# Incrementing through each input geometry.
|
||||||
for i in xrange(ngeom):
|
for i in xrange(ngeom):
|
||||||
if isinstance(init_geoms[i], Polygon):
|
geoms[i] = cast(init_geoms[i]._nullify(), GEOM_PTR)
|
||||||
# Special care is taken when importing from Polygons
|
|
||||||
geoms[i] = cast(init_from_poly(init_geoms[i]), GEOM_PTR)
|
|
||||||
else:
|
|
||||||
geoms[i] = cast(init_from_geom(init_geoms[i]), GEOM_PTR)
|
|
||||||
|
|
||||||
# Calling the parent class, using the pointer returned from GEOS createCollection()
|
# Calling the parent class, using the pointer returned from GEOS createCollection()
|
||||||
super(GeometryCollection, self).__init__(lgeos.GEOSGeom_createCollection(c_int(self._typeid), byref(geoms), c_uint(ngeom)), **kwargs)
|
super(GeometryCollection, self).__init__(lgeos.GEOSGeom_createCollection(c_int(self._typeid), byref(geoms), c_uint(ngeom)), **kwargs)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Overloaded deletion method for Geometry Collections."
|
"Overloaded deletion method for Geometry Collections."
|
||||||
#print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
#print 'collection: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
||||||
# If this geometry is still valid, it hasn't been modified by others.
|
# If this geometry is still valid, it hasn't been modified by others.
|
||||||
if self._ptr.valid:
|
if self._ptr.valid:
|
||||||
# Nullifying pointers to internal geometries, preventing any attempted future access.
|
# Nullifying pointers to internal geometries, preventing any attempted future access.
|
||||||
for k in self._geoms: self._geoms[k].nullify()
|
for k in self._geoms: self._geoms[k].nullify()
|
||||||
super(GeometryCollection, self).__del__()
|
|
||||||
else:
|
else:
|
||||||
# Internal memory has become part of other Geometry objects, must delete the
|
# Internal memory has become part of other Geometry objects, must delete the
|
||||||
# internal objects which are still valid individually, since calling destructor
|
# internal objects which are still valid individually, since calling destructor
|
||||||
@ -80,20 +63,38 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
if self._geoms[k].valid:
|
if self._geoms[k].valid:
|
||||||
lgeos.GEOSGeom_destroy(self._geoms[k].address)
|
lgeos.GEOSGeom_destroy(self._geoms[k].address)
|
||||||
self._geoms[k].nullify()
|
self._geoms[k].nullify()
|
||||||
|
super(GeometryCollection, self).__del__()
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"For indexing on the multiple geometries."
|
"Returns the Geometry from this Collection at the given index (0-based)."
|
||||||
# Checking the index and returning the corresponding GEOS geometry.
|
# Checking the index and returning the corresponding GEOS geometry.
|
||||||
self._checkindex(index)
|
self._checkindex(index)
|
||||||
return GEOSGeometry(self._geoms[index], parent=self._ptr, srid=self.srid)
|
return GEOSGeometry(self._geoms[index], parent=self._ptr, srid=self.srid)
|
||||||
|
|
||||||
|
def __setitem__(self, index, geom):
|
||||||
|
"Sets the Geometry at the specified index."
|
||||||
|
self._checkindex(index)
|
||||||
|
if not isinstance(geom, self._allowed):
|
||||||
|
raise TypeError, 'Incompatible Geometry for collection.'
|
||||||
|
|
||||||
|
# Constructing the list of geometries that will go in the collection.
|
||||||
|
new_geoms = []
|
||||||
|
for i in xrange(len(self)):
|
||||||
|
if i == index: new_geoms.append(geom)
|
||||||
|
else: new_geoms.append(self[i])
|
||||||
|
|
||||||
|
# Creating a new geometry collection from the list, and
|
||||||
|
# re-assigning the pointers.
|
||||||
|
new_collection = self.__class__(*new_geoms)
|
||||||
|
self._reassign(new_collection)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"For iteration on the multiple geometries."
|
"Iterates over each Geometry in the Collection."
|
||||||
for i in xrange(len(self)):
|
for i in xrange(len(self)):
|
||||||
yield self.__getitem__(i)
|
yield self.__getitem__(i)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
"Returns the number of geometries in this collection."
|
"Returns the number of geometries in this Collection."
|
||||||
return self.num_geom
|
return self.num_geom
|
||||||
|
|
||||||
def _checkindex(self, index):
|
def _checkindex(self, index):
|
||||||
@ -101,13 +102,18 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
if index < 0 or index >= self.num_geom:
|
if index < 0 or index >= self.num_geom:
|
||||||
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
||||||
|
|
||||||
|
def _nullify(self):
|
||||||
|
"Overloaded from base method to nullify geometry references in this Collection."
|
||||||
|
# Nullifying the references to the internal Geometry objects from this Collection.
|
||||||
|
for k in self._geoms: self._geoms[k].nullify()
|
||||||
|
return super(GeometryCollection, self)._nullify()
|
||||||
|
|
||||||
def _populate(self):
|
def _populate(self):
|
||||||
"Populates the internal child geometry dictionary."
|
"Populates the internal child geometry dictionary."
|
||||||
self._geoms = {}
|
self._geoms = {}
|
||||||
for i in xrange(self.num_geom):
|
for i in xrange(self.num_geom):
|
||||||
self._geoms[i] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(i)))
|
self._geoms[i] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(i)))
|
||||||
|
|
||||||
|
|
||||||
# MultiPoint, MultiLineString, and MultiPolygon class definitions.
|
# MultiPoint, MultiLineString, and MultiPolygon class definitions.
|
||||||
class MultiPoint(GeometryCollection):
|
class MultiPoint(GeometryCollection):
|
||||||
_allowed = Point
|
_allowed = Point
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
from ctypes import c_double, c_int, c_uint, byref, cast
|
from ctypes import c_double, c_int, c_uint, byref, cast
|
||||||
from types import FloatType, IntType, ListType, TupleType
|
from types import FloatType, IntType, ListType, TupleType
|
||||||
from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs
|
from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs
|
||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, init_from_geom, GEOM_PTR, HAS_NUMPY
|
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, GEOM_PTR, HAS_NUMPY
|
||||||
from django.contrib.gis.geos.base import GEOSGeometry
|
from django.contrib.gis.geos.base import GEOSGeometry
|
||||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
if HAS_NUMPY: from numpy import ndarray, array
|
if HAS_NUMPY: from numpy import ndarray, array
|
||||||
@ -21,7 +21,9 @@ class Point(GEOSGeometry):
|
|||||||
>>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters
|
>>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Setting-up for Point Creation
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
self._ptr = GEOSPointer(0) # Initially NULL
|
||||||
|
self._parent = None
|
||||||
|
|
||||||
if isinstance(x, (TupleType, ListType)):
|
if isinstance(x, (TupleType, ListType)):
|
||||||
# Here a tuple or list was passed in under the ``x`` parameter.
|
# Here a tuple or list was passed in under the ``x`` parameter.
|
||||||
@ -132,7 +134,9 @@ class LineString(GEOSGeometry):
|
|||||||
ls = LineString(array([(1, 1), (2, 2)]))
|
ls = LineString(array([(1, 1), (2, 2)]))
|
||||||
ls = LineString(Point(1, 1), Point(2, 2))
|
ls = LineString(Point(1, 1), Point(2, 2))
|
||||||
"""
|
"""
|
||||||
|
# Setting up for LineString creation
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
self._ptr = GEOSPointer(0) # Initially NULL
|
||||||
|
self._parent = None
|
||||||
|
|
||||||
# If only one argument was provided, then set the coords array appropriately
|
# If only one argument was provided, then set the coords array appropriately
|
||||||
if len(args) == 1: coords = args[0]
|
if len(args) == 1: coords = args[0]
|
||||||
@ -254,6 +258,7 @@ class Polygon(GEOSGeometry):
|
|||||||
poly = Polygon(shell, (hole1, hole2))
|
poly = Polygon(shell, (hole1, hole2))
|
||||||
"""
|
"""
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
self._ptr = GEOSPointer(0) # Initially NULL
|
||||||
|
self._parent = None
|
||||||
self._rings = {}
|
self._rings = {}
|
||||||
if not args:
|
if not args:
|
||||||
raise TypeError, 'Must provide at list one LinearRing instance to initialize Polygon.'
|
raise TypeError, 'Must provide at list one LinearRing instance to initialize Polygon.'
|
||||||
@ -277,43 +282,58 @@ class Polygon(GEOSGeometry):
|
|||||||
holes = get_pointer_arr(nholes)
|
holes = get_pointer_arr(nholes)
|
||||||
for i in xrange(nholes):
|
for i in xrange(nholes):
|
||||||
# Casting to the Geometry Pointer type
|
# Casting to the Geometry Pointer type
|
||||||
holes[i] = cast(init_from_geom(init_holes[i]), GEOM_PTR)
|
holes[i] = cast(init_holes[i]._nullify(), GEOM_PTR)
|
||||||
|
|
||||||
# Getting the shell pointer address,
|
# Getting the shell pointer address,
|
||||||
shell = init_from_geom(ext_ring)
|
shell = ext_ring._nullify()
|
||||||
|
|
||||||
# Calling with the GEOS createPolygon factory.
|
# Calling with the GEOS createPolygon factory.
|
||||||
super(Polygon, self).__init__(lgeos.GEOSGeom_createPolygon(shell, byref(holes), c_uint(nholes)), **kwargs)
|
super(Polygon, self).__init__(lgeos.GEOSGeom_createPolygon(shell, byref(holes), c_uint(nholes)), **kwargs)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Overloaded deletion method for Polygons."
|
"Overloaded deletion method for Polygons."
|
||||||
#print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
#print 'polygon: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
||||||
# If this geometry is still valid, it hasn't been modified by others.
|
# If this geometry is still valid, it hasn't been modified by others.
|
||||||
if self._ptr.valid:
|
if self._ptr.valid:
|
||||||
# Nulling the pointers to internal rings, preventing any attempted future access
|
# Nulling the pointers to internal rings, preventing any attempted future access
|
||||||
for k in self._rings: self._rings[k].nullify()
|
for k in self._rings: self._rings[k].nullify()
|
||||||
super(Polygon, self).__del__()
|
elif not self._parent:
|
||||||
else:
|
|
||||||
# Internal memory has become part of other objects; must delete the
|
# Internal memory has become part of other objects; must delete the
|
||||||
# internal objects which are still valid individually, since calling
|
# internal objects which are still valid individually, since calling
|
||||||
# destructor on entire geometry will result in an attempted deletion
|
# destructor on entire geometry will result in an attempted deletion
|
||||||
# of NULL pointers for the missing components.
|
# of NULL pointers for the missing components. Not performed on
|
||||||
|
# children Polygons from MultiPolygon or GeometryCollection objects.
|
||||||
for k in self._rings:
|
for k in self._rings:
|
||||||
if self._rings[k].valid:
|
if self._rings[k].valid:
|
||||||
lgeos.GEOSGeom_destroy(self._rings[k].address)
|
lgeos.GEOSGeom_destroy(self._rings[k].address)
|
||||||
self._rings[k].nullify()
|
self._rings[k].nullify()
|
||||||
|
super(Polygon, self).__del__()
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"""Returns the ring at the specified index. The first index, 0, will always
|
"""Returns the ring at the specified index. The first index, 0, will always
|
||||||
return the exterior ring. Indices > 0 will return the interior ring."""
|
return the exterior ring. Indices > 0 will return the interior ring."""
|
||||||
if index < 0 or index > self.num_interior_rings:
|
if index == 0:
|
||||||
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
return self.exterior_ring
|
||||||
else:
|
else:
|
||||||
if index == 0:
|
# Getting the interior ring, have to subtract 1 from the index.
|
||||||
return self.exterior_ring
|
return self.get_interior_ring(index-1)
|
||||||
else:
|
|
||||||
# Getting the interior ring, have to subtract 1 from the index.
|
def __setitem__(self, index, ring):
|
||||||
return self.get_interior_ring(index-1)
|
"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'
|
||||||
|
|
||||||
|
# Constructing the ring parameters
|
||||||
|
new_rings = []
|
||||||
|
for i in xrange(len(self)):
|
||||||
|
if index == i: new_rings.append(ring)
|
||||||
|
else: new_rings.append(self[i])
|
||||||
|
|
||||||
|
# Constructing the new Polygon with the ring parameters, and reassigning the internals.
|
||||||
|
new_poly = Polygon(*new_rings)
|
||||||
|
self._reassign(new_poly)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
"Iterates over each ring in the polygon."
|
"Iterates over each ring in the polygon."
|
||||||
@ -324,6 +344,17 @@ class Polygon(GEOSGeometry):
|
|||||||
"Returns the number of rings in this Polygon."
|
"Returns the number of rings in this Polygon."
|
||||||
return self.num_interior_rings + 1
|
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 GEOSGeometryIndexError, 'invalid Polygon ring index: %s' % index
|
||||||
|
|
||||||
|
def _nullify(self):
|
||||||
|
"Overloaded from base method to nullify ring references as well."
|
||||||
|
# Nullifying the references to the internal rings of this Polygon.
|
||||||
|
for k in self._rings: self._rings[k].nullify()
|
||||||
|
return super(Polygon, self)._nullify()
|
||||||
|
|
||||||
def _populate(self):
|
def _populate(self):
|
||||||
"Populates the internal rings dictionary."
|
"Populates the internal rings dictionary."
|
||||||
# Getting the exterior ring first for the 0th index.
|
# Getting the exterior ring first for the 0th index.
|
||||||
@ -336,13 +367,9 @@ class Polygon(GEOSGeometry):
|
|||||||
def get_interior_ring(self, ring_i):
|
def get_interior_ring(self, ring_i):
|
||||||
"""Gets the interior ring at the specified index,
|
"""Gets the interior ring at the specified index,
|
||||||
0 is for the first interior ring, not the exterior ring."""
|
0 is for the first interior ring, not the exterior ring."""
|
||||||
|
# Returning the ring from the internal ring dictionary (have to add one
|
||||||
# Making sure the ring index is within range
|
# to index since all internal rings come after the exterior ring)
|
||||||
if ring_i < 0 or ring_i >= self.num_interior_rings:
|
self._checkindex(ring_i+1)
|
||||||
raise IndexError, 'ring index out of range'
|
|
||||||
|
|
||||||
# Returning the ring from the internal ring dictionary (have to
|
|
||||||
# add one to the index)
|
|
||||||
return GEOSGeometry(self._rings[ring_i+1], parent=self._ptr, srid=self.srid)
|
return GEOSGeometry(self._rings[ring_i+1], parent=self._ptr, srid=self.srid)
|
||||||
|
|
||||||
#### Polygon Properties ####
|
#### Polygon Properties ####
|
||||||
@ -361,14 +388,13 @@ class Polygon(GEOSGeometry):
|
|||||||
"Gets the exterior ring of the Polygon."
|
"Gets the exterior ring of the Polygon."
|
||||||
return GEOSGeometry(self._rings[0], parent=self._ptr, srid=self.srid)
|
return GEOSGeometry(self._rings[0], parent=self._ptr, srid=self.srid)
|
||||||
|
|
||||||
def set_ext_ring(self):
|
def set_ext_ring(self, ring):
|
||||||
"Sets the exterior ring of the Polygon."
|
"Sets the exterior ring of the Polygon."
|
||||||
# Sets the exterior ring
|
self[0] = ring
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
# properties for the exterior ring/shell
|
# properties for the exterior ring/shell
|
||||||
exterior_ring = property(get_ext_ring)
|
exterior_ring = property(get_ext_ring, set_ext_ring)
|
||||||
shell = property(get_ext_ring)
|
shell = property(get_ext_ring, set_ext_ring)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tuple(self):
|
def tuple(self):
|
||||||
|
@ -18,11 +18,13 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_NUMPY = False
|
HAS_NUMPY = False
|
||||||
|
|
||||||
# Psycopg2 supported?
|
# Are psycopg2 and GeoDjango models being used?
|
||||||
try:
|
try:
|
||||||
from psycopg2.extensions import ISQLQuote
|
from psycopg2.extensions import ISQLQuote
|
||||||
except ImportError:
|
from django.contrib.gis.db.backend.postgis import GEOM_FUNC_PREFIX
|
||||||
|
except (ImportError, EnvironmentError):
|
||||||
ISQLQuote = None
|
ISQLQuote = None
|
||||||
|
GEOM_FUNC_PREFIX = None
|
||||||
|
|
||||||
# Setting the appropriate name for the GEOS-C library, depending on which
|
# Setting the appropriate name for the GEOS-C library, depending on which
|
||||||
# OS and POSIX platform we're running.
|
# OS and POSIX platform we're running.
|
||||||
@ -71,21 +73,22 @@ error_h = ERRORFUNC(error_h)
|
|||||||
# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);"
|
# "extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);"
|
||||||
lgeos.initGEOS(notice_h, error_h)
|
lgeos.initGEOS(notice_h, error_h)
|
||||||
|
|
||||||
#### GEOS Geometry Pointer utilities. ####
|
#### GEOS Geometry Pointer object, related C data structures, and functions. ####
|
||||||
|
|
||||||
# Opaque GEOS geometry structure
|
# Opaque GEOS geometry structure
|
||||||
class GEOSGeom_t(Structure):
|
class GEOSGeom_t(Structure):
|
||||||
"Opaque structure used when arrays of geometries are needed as parameters."
|
"Opaque structure used when arrays of geometries are needed as parameters."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Pointer to opaque geometry structure
|
# Pointer to opaque geometry structure
|
||||||
GEOM_PTR = POINTER(GEOSGeom_t)
|
GEOM_PTR = POINTER(GEOSGeom_t)
|
||||||
|
|
||||||
# Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection GEOS routines
|
# Used specifically by the GEOSGeom_createPolygon and GEOSGeom_createCollection GEOS routines
|
||||||
def get_pointer_arr(n):
|
def get_pointer_arr(n):
|
||||||
"Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer."
|
"Gets a ctypes pointer array (of length `n`) for GEOSGeom_t opaque pointer."
|
||||||
GeomArr = GEOM_PTR * n
|
GeomArr = GEOM_PTR * n
|
||||||
return GeomArr()
|
return GeomArr()
|
||||||
|
|
||||||
#### GEOS Pointer object and routines ####
|
|
||||||
class GEOSPointer(object):
|
class GEOSPointer(object):
|
||||||
"""The GEOSPointer provides a layer of abstraction in accessing the values returned by
|
"""The GEOSPointer provides a layer of abstraction in accessing the values returned by
|
||||||
GEOS geometry creation routines. Memory addresses (integers) are kept in a C pointer,
|
GEOS geometry creation routines. Memory addresses (integers) are kept in a C pointer,
|
||||||
@ -108,13 +111,16 @@ class GEOSPointer(object):
|
|||||||
if self.valid: return self.address
|
if self.valid: return self.address
|
||||||
else: raise GEOSException, 'GEOS pointer no longer valid (was this geometry or the parent geometry deleted or modified?)'
|
else: raise GEOSException, 'GEOS pointer no longer valid (was this geometry or the parent geometry deleted or modified?)'
|
||||||
|
|
||||||
def __bool__(self):
|
def __nonzero__(self):
|
||||||
"Returns True when the GEOSPointer is valid."
|
"Returns True when the GEOSPointer is valid."
|
||||||
return self.valid
|
return self.valid
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.address)
|
return str(self.address)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'GEOSPointer(%s)' % self.address
|
||||||
|
|
||||||
### GEOSPointer Properties ###
|
### GEOSPointer Properties ###
|
||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
@ -161,21 +167,3 @@ class GEOSPointer(object):
|
|||||||
# Nullifying both the geometry and coordinate sequence pointer.
|
# Nullifying both the geometry and coordinate sequence pointer.
|
||||||
self.set(0)
|
self.set(0)
|
||||||
self.set(0, coordseq=True)
|
self.set(0, coordseq=True)
|
||||||
|
|
||||||
def init_from_geom(geom):
|
|
||||||
"""During initialization of geometries from other geometries, this routine is
|
|
||||||
used to nullify any parent geometries (since they will now be missing memory
|
|
||||||
components) and to nullify the geometry itself to prevent future access.
|
|
||||||
Only the address (an integer) of the current geometry is returned for use in
|
|
||||||
initializing the new geometry."""
|
|
||||||
# First getting the memory address of the geometry.
|
|
||||||
address = geom._ptr()
|
|
||||||
|
|
||||||
# If the geometry is a child geometry, then the parent geometry pointer is
|
|
||||||
# nullified.
|
|
||||||
if geom._parent: geom._parent.nullify()
|
|
||||||
|
|
||||||
# Nullifying the geometry pointer
|
|
||||||
geom._ptr.nullify()
|
|
||||||
|
|
||||||
return address
|
|
||||||
|
@ -56,9 +56,13 @@ def run_tests(module_list, verbosity=1):
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
# Getting initial values.
|
# Getting initial values.
|
||||||
|
old_debug = settings.DEBUG
|
||||||
old_name = copy(settings.DATABASE_NAME)
|
old_name = copy(settings.DATABASE_NAME)
|
||||||
old_installed = copy(settings.INSTALLED_APPS)
|
old_installed = copy(settings.INSTALLED_APPS)
|
||||||
|
|
||||||
|
# Want DEBUG to be set to False.
|
||||||
|
settings.DEBUG = False
|
||||||
|
|
||||||
# Creating the test suite, adding the test models to INSTALLED_APPS, and
|
# Creating the test suite, adding the test models to INSTALLED_APPS, and
|
||||||
# adding the model test suites to our suite package.
|
# adding the model test suites to our suite package.
|
||||||
test_suite = suite()
|
test_suite = suite()
|
||||||
@ -82,6 +86,7 @@ def run_tests(module_list, verbosity=1):
|
|||||||
|
|
||||||
# Cleaning up, destroying the test spatial database and resetting the INSTALLED_APPS.
|
# Cleaning up, destroying the test spatial database and resetting the INSTALLED_APPS.
|
||||||
destroy_test_db(old_name, verbosity)
|
destroy_test_db(old_name, verbosity)
|
||||||
|
settings.DEBUG = old_debug
|
||||||
settings.INSTALLED_APPS = old_installed
|
settings.INSTALLED_APPS = old_installed
|
||||||
|
|
||||||
# Returning the total failures and errors
|
# Returning the total failures and errors
|
||||||
|
@ -115,19 +115,19 @@ topology_geoms = ( (TestGeom('POLYGON ((-5.0 0.0, -5.0 10.0, 5.0 10.0, 5.0 0.0,
|
|||||||
)
|
)
|
||||||
|
|
||||||
intersect_geoms = ( TestGeom('POLYGON ((5 5,5 0,0 0,0 5,5 5))'),
|
intersect_geoms = ( TestGeom('POLYGON ((5 5,5 0,0 0,0 5,5 5))'),
|
||||||
TestGeom('POLYGON ((10 1, 11 3, 13 4, 15 6, 16 8, 16 10, 15 12, 13 13, 11 12, 10 10, 9 12, 7 13, 5 12, 4 10, 4 8, 5 6, 7 4, 9 3, 10 1))')
|
TestGeom('POLYGON ((10 1, 9 3, 7 4, 5 6, 4 8, 4 10, 5 12, 7 13, 9 12, 10 10, 11 12, 13 13, 15 12, 16 10, 16 8, 15 6, 13 4, 11 3, 10 1))'),
|
||||||
)
|
)
|
||||||
|
|
||||||
union_geoms = ( TestGeom('POLYGON ((-5 0,-5 10,5 10,5 5,10 5,10 -5,0 -5,0 0,-5 0))'),
|
union_geoms = ( TestGeom('POLYGON ((-5 0,-5 10,5 10,5 5,10 5,10 -5,0 -5,0 0,-5 0))'),
|
||||||
TestGeom('POLYGON ((2 0, 18 0, 18 15, 2 15, 2 0))'),
|
TestGeom('POLYGON ((2 0, 2 15, 18 15, 18 0, 2 0))'),
|
||||||
)
|
)
|
||||||
|
|
||||||
diff_geoms = ( TestGeom('POLYGON ((-5 0,-5 10,5 10,5 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))'),
|
||||||
TestGeom('POLYGON ((2 0, 18 0, 18 15, 2 15, 2 0), (10 1, 11 3, 13 4, 15 6, 16 8, 16 10, 15 12, 13 13, 11 12, 10 10, 9 12, 7 13, 5 12, 4 10, 4 8, 5 6, 7 4, 9 3, 10 1))'),
|
TestGeom('POLYGON ((2 0, 2 15, 18 15, 18 0, 2 0), (10 1, 11 3, 13 4, 15 6, 16 8, 16 10, 15 12, 13 13, 11 12, 10 10, 9 12, 7 13, 5 12, 4 10, 4 8, 5 6, 7 4, 9 3, 10 1))'),
|
||||||
)
|
)
|
||||||
|
|
||||||
sdiff_geoms = ( TestGeom('MULTIPOLYGON (((-5 0,-5 10,5 10,5 5,0 5,0 0,-5 0)),((0 0,5 0,5 5,10 5,10 -5,0 -5,0 0)))'),
|
sdiff_geoms = ( TestGeom('MULTIPOLYGON (((-5 0,-5 10,5 10,5 5,0 5,0 0,-5 0)),((0 0,5 0,5 5,10 5,10 -5,0 -5,0 0)))'),
|
||||||
TestGeom('POLYGON ((2 0, 18 0, 18 15, 2 15, 2 0), (10 1, 11 3, 13 4, 15 6, 16 8, 16 10, 15 12, 13 13, 11 12, 10 10, 9 12, 7 13, 5 12, 4 10, 4 8, 5 6, 7 4, 9 3, 10 1))'),
|
TestGeom('POLYGON ((2 0, 2 15, 18 15, 18 0, 2 0), (10 1, 11 3, 13 4, 15 6, 16 8, 16 10, 15 12, 13 13, 11 12, 10 10, 9 12, 7 13, 5 12, 4 10, 4 8, 5 6, 7 4, 9 3, 10 1))'),
|
||||||
)
|
)
|
||||||
|
|
||||||
relate_geoms = ( (TestGeom('MULTIPOINT(80 70, 20 20, 200 170, 140 120)'),
|
relate_geoms = ( (TestGeom('MULTIPOINT(80 70, 20 20, 200 170, 140 120)'),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import unittest
|
import random, unittest
|
||||||
from django.contrib.gis.geos import \
|
from django.contrib.gis.geos import \
|
||||||
GEOSException, GEOSGeometryIndexError, \
|
GEOSException, GEOSGeometryIndexError, \
|
||||||
GEOSGeometry, Point, LineString, LinearRing, Polygon, \
|
GEOSGeometry, Point, LineString, LinearRing, Polygon, \
|
||||||
@ -166,7 +166,7 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertEqual(lr, LinearRing(lr.tuple))
|
self.assertEqual(lr, LinearRing(lr.tuple))
|
||||||
self.assertEqual(lr, LinearRing(*lr.tuple))
|
self.assertEqual(lr, LinearRing(*lr.tuple))
|
||||||
self.assertEqual(lr, LinearRing([list(tup) for tup in lr.tuple]))
|
self.assertEqual(lr, LinearRing([list(tup) for tup in lr.tuple]))
|
||||||
if HAS_NUMPY: self.assertEqual(lr, LineString(array(lr.tuple)))
|
if HAS_NUMPY: self.assertEqual(lr, LinearRing(array(lr.tuple)))
|
||||||
|
|
||||||
def test05a_polygons(self):
|
def test05a_polygons(self):
|
||||||
"Testing Polygon objects."
|
"Testing Polygon objects."
|
||||||
@ -200,10 +200,15 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertEqual(p.ext_ring_cs, ring.tuple)
|
self.assertEqual(p.ext_ring_cs, ring.tuple)
|
||||||
self.assertEqual(p.ext_ring_cs, poly[0].tuple) # Testing __getitem__
|
self.assertEqual(p.ext_ring_cs, poly[0].tuple) # Testing __getitem__
|
||||||
|
|
||||||
# Testing __iter__
|
# Testing __getitem__ and __setitem__ on invalid indices
|
||||||
|
self.assertRaises(GEOSGeometryIndexError, poly.__getitem__, len(poly))
|
||||||
|
self.assertRaises(GEOSGeometryIndexError, poly.__setitem__, len(poly), False)
|
||||||
|
self.assertRaises(GEOSGeometryIndexError, poly.__getitem__, -1)
|
||||||
|
|
||||||
|
# Testing __iter__
|
||||||
for r in poly:
|
for r in poly:
|
||||||
self.assertEqual(ring.geom_type, 'LinearRing')
|
self.assertEqual(r.geom_type, 'LinearRing')
|
||||||
self.assertEqual(ring.geom_typeid, 2)
|
self.assertEqual(r.geom_typeid, 2)
|
||||||
|
|
||||||
# Testing polygon construction.
|
# Testing polygon construction.
|
||||||
self.assertRaises(TypeError, Polygon, 0, [1, 2, 3])
|
self.assertRaises(TypeError, Polygon, 0, [1, 2, 3])
|
||||||
@ -224,8 +229,8 @@ class GEOSTest(unittest.TestCase):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
poly[0][1] = newval # setting the second point in the polygon with the newvalue (based on the old)
|
poly[0][1] = newval # setting the second point in the polygon with the newvalue (based on the old)
|
||||||
self.assertEqual(newval, poly[0][1]) # The point in the polygon should be the
|
self.assertEqual(newval, poly[0][1]) # The point in the polygon should be the new value
|
||||||
self.assertEqual(False, poly == prev) # Even different from the clone we just made
|
self.assertEqual(False, poly == prev) # Should be different from the clone we just made
|
||||||
|
|
||||||
def test05b_multipolygons(self):
|
def test05b_multipolygons(self):
|
||||||
"Testing MultiPolygon objects."
|
"Testing MultiPolygon objects."
|
||||||
@ -426,11 +431,12 @@ class GEOSTest(unittest.TestCase):
|
|||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = GEOSGeometry(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = GEOSGeometry(g_tup[1].wkt)
|
||||||
i1 = GEOSGeometry(intersect_geoms[i].wkt)
|
i1 = GEOSGeometry(intersect_geoms[i].wkt)
|
||||||
|
|
||||||
self.assertEqual(True, a.intersects(b))
|
self.assertEqual(True, a.intersects(b))
|
||||||
i2 = a.intersection(b)
|
i2 = a.intersection(b)
|
||||||
self.assertEqual(i1, i2)
|
self.assertEqual(i1, i2)
|
||||||
self.assertEqual(i1, a & b)
|
self.assertEqual(i1, a & b) # __and__ is intersection operator
|
||||||
|
a &= b # testing __iand__
|
||||||
|
self.assertEqual(i1, a)
|
||||||
|
|
||||||
def test11_union(self):
|
def test11_union(self):
|
||||||
"Testing union()."
|
"Testing union()."
|
||||||
@ -441,7 +447,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
u1 = GEOSGeometry(union_geoms[i].wkt)
|
u1 = GEOSGeometry(union_geoms[i].wkt)
|
||||||
u2 = a.union(b)
|
u2 = a.union(b)
|
||||||
self.assertEqual(u1, u2)
|
self.assertEqual(u1, u2)
|
||||||
self.assertEqual(u1, a | b) # Union ('|') operator
|
self.assertEqual(u1, a | b) # __or__ is union operator
|
||||||
|
a |= b # testing __ior__
|
||||||
|
self.assertEqual(u1, a)
|
||||||
|
|
||||||
def test12_difference(self):
|
def test12_difference(self):
|
||||||
"Testing difference()."
|
"Testing difference()."
|
||||||
@ -452,7 +460,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
d1 = GEOSGeometry(diff_geoms[i].wkt)
|
d1 = GEOSGeometry(diff_geoms[i].wkt)
|
||||||
d2 = a.difference(b)
|
d2 = a.difference(b)
|
||||||
self.assertEqual(d1, d2)
|
self.assertEqual(d1, d2)
|
||||||
self.assertEqual(d1, a - b) # Difference ('-') operator
|
self.assertEqual(d1, a - b) # __sub__ is difference operator
|
||||||
|
a -= b # testing __isub__
|
||||||
|
self.assertEqual(d1, a)
|
||||||
|
|
||||||
def test13_symdifference(self):
|
def test13_symdifference(self):
|
||||||
"Testing sym_difference()."
|
"Testing sym_difference()."
|
||||||
@ -463,7 +473,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
d1 = GEOSGeometry(sdiff_geoms[i].wkt)
|
d1 = GEOSGeometry(sdiff_geoms[i].wkt)
|
||||||
d2 = a.sym_difference(b)
|
d2 = a.sym_difference(b)
|
||||||
self.assertEqual(d1, d2)
|
self.assertEqual(d1, d2)
|
||||||
self.assertEqual(d1, a ^ b) # Symmetric difference ('^') operator
|
self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator
|
||||||
|
a ^= b # testing __ixor__
|
||||||
|
self.assertEqual(d1, a)
|
||||||
|
|
||||||
def test14_buffer(self):
|
def test14_buffer(self):
|
||||||
"Testing buffer()."
|
"Testing buffer()."
|
||||||
@ -492,6 +504,85 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(exp_ring[k][0], buf_ring[k][0], 9)
|
self.assertAlmostEqual(exp_ring[k][0], buf_ring[k][0], 9)
|
||||||
self.assertAlmostEqual(exp_ring[k][1], buf_ring[k][1], 9)
|
self.assertAlmostEqual(exp_ring[k][1], buf_ring[k][1], 9)
|
||||||
|
|
||||||
|
def test15_srid(self):
|
||||||
|
"Testing the SRID property and keyword."
|
||||||
|
# Testing SRID keyword on Point
|
||||||
|
pnt = Point(5, 23, srid=4326)
|
||||||
|
self.assertEqual(4326, pnt.srid)
|
||||||
|
pnt.srid = 3084
|
||||||
|
self.assertEqual(3084, pnt.srid)
|
||||||
|
self.assertRaises(TypeError, pnt.set_srid, '4326')
|
||||||
|
|
||||||
|
# Testing SRID keyword on fromstr(), and on Polygon rings.
|
||||||
|
poly = fromstr(polygons[1].wkt, srid=4269)
|
||||||
|
self.assertEqual(4269, poly.srid)
|
||||||
|
for ring in poly: self.assertEqual(4269, ring.srid)
|
||||||
|
poly.srid = 4326
|
||||||
|
self.assertEqual(4326, poly.shell.srid)
|
||||||
|
|
||||||
|
# Testing SRID keyword on GeometryCollection
|
||||||
|
gc = GeometryCollection(Point(5, 23), LineString((0, 0), (1.5, 1.5), (3, 3)), srid=32021)
|
||||||
|
self.assertEqual(32021, gc.srid)
|
||||||
|
for i in range(len(gc)): self.assertEqual(32021, gc[i].srid)
|
||||||
|
|
||||||
|
def test16_mutable_geometries(self):
|
||||||
|
"Testing the mutability of Polygons and Geometry Collections."
|
||||||
|
### Testing the mutability of Polygons ###
|
||||||
|
for p in polygons:
|
||||||
|
poly = fromstr(p.wkt)
|
||||||
|
|
||||||
|
# Should only be able to use __setitem__ with LinearRing geometries.
|
||||||
|
self.assertRaises(TypeError, poly.__setitem__, 0, LineString((1, 1), (2, 2)))
|
||||||
|
|
||||||
|
# Constructing the new shell by adding 500 to every point in the old shell.
|
||||||
|
shell_tup = poly.shell.tuple
|
||||||
|
new_coords = []
|
||||||
|
for point in shell_tup: new_coords.append((point[0] + 500., point[1] + 500.))
|
||||||
|
shell1 = LinearRing(*tuple(new_coords))
|
||||||
|
shell2 = shell1.clone()
|
||||||
|
|
||||||
|
# Assigning polygon's exterior ring w/the new shell
|
||||||
|
poly.exterior_ring = shell1
|
||||||
|
self.assertRaises(GEOSException, str, shell1) # shell1 should no longer be accessible
|
||||||
|
self.assertEqual(poly.exterior_ring, shell2)
|
||||||
|
self.assertEqual(poly[0], shell2)
|
||||||
|
del poly, shell1, shell_tup # cleaning up
|
||||||
|
|
||||||
|
### Testing the mutability of Geometry Collections
|
||||||
|
for tg in multipoints:
|
||||||
|
mp = fromstr(tg.wkt)
|
||||||
|
for i in range(len(mp)):
|
||||||
|
# Creating a random point.
|
||||||
|
pnt = mp[i].clone()
|
||||||
|
new = Point(random.randint(1, 100), random.randint(1, 100))
|
||||||
|
tmp = new.clone()
|
||||||
|
# Testing the assignmen
|
||||||
|
mp[i] = tmp
|
||||||
|
self.assertRaises(GEOSException, len, tmp)
|
||||||
|
self.assertEqual(mp[i], new)
|
||||||
|
self.assertEqual(mp[i].wkt, new.wkt)
|
||||||
|
self.assertNotEqual(pnt, mp[i])
|
||||||
|
del mp
|
||||||
|
|
||||||
|
# Multipolygons involve much more memory management because each
|
||||||
|
# polygon w/in the collection has its own rings.
|
||||||
|
for tg in multipolygons:
|
||||||
|
mpoly = fromstr(tg.wkt)
|
||||||
|
for i in xrange(len(mpoly)):
|
||||||
|
poly = mpoly[i].clone()
|
||||||
|
# Offsetting the each ring in the polygon by 500.
|
||||||
|
tmp = poly.clone()
|
||||||
|
for r in tmp:
|
||||||
|
for j in xrange(len(r)): r[j] = (r[j][0] + 500., r[j][1] + 500.)
|
||||||
|
self.assertNotEqual(poly, tmp)
|
||||||
|
new = tmp.clone() # a 'reference' copy of the geometry used in assignment
|
||||||
|
# Testing the assignment
|
||||||
|
mpoly[i] = tmp
|
||||||
|
self.assertRaises(GEOSException, str, tmp)
|
||||||
|
self.assertEqual(mpoly[i], new)
|
||||||
|
self.assertNotEqual(poly, mpoly[i])
|
||||||
|
del mpoly
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
s = unittest.TestSuite()
|
s = unittest.TestSuite()
|
||||||
s.addTest(unittest.makeSuite(GEOSTest))
|
s.addTest(unittest.makeSuite(GEOSTest))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user