mirror of
https://github.com/django/django.git
synced 2025-07-05 02:09:13 +00:00
gis: geos: Memory-management refactor, changes include:
(1) Moved GEOSPointer to its own module, and now tracks children geometries. (2) Fixed 'deep-indexing' issues for nested rings in GeometryCollections/MultiPolygons (e.g., mpoly[0][1] = new_ring). (3) Added simplify() -- simplifies geometries using the Douglas-Peucker algorithm. (4) Conformed docstrings to Django coding style. (5) GEOSGeometry no longer uses `parent` and `input_type` keywords. git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6024 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
cb64f5375c
commit
f4203ef757
@ -33,6 +33,7 @@ from django.contrib.gis.geos.base import GEOSGeometry
|
|||||||
from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon, HAS_NUMPY
|
from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon, HAS_NUMPY
|
||||||
from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
|
from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
|
||||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
|
from django.contrib.gis.geos.libgeos import geos_version
|
||||||
|
|
||||||
def fromstr(wkt_or_hex, **kwargs):
|
def fromstr(wkt_or_hex, **kwargs):
|
||||||
"Given a string value (wkt or hex), returns a GEOSGeometry object."
|
"Given a string value (wkt or hex), returns a GEOSGeometry object."
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
This module contains the 'base' GEOSGeometry object -- all GEOS geometries
|
This module contains the 'base' GEOSGeometry object -- all GEOS geometries
|
||||||
inherit from this object.
|
inherit from this object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ctypes and types dependencies.
|
# ctypes and types dependencies.
|
||||||
from ctypes import \
|
from ctypes import \
|
||||||
byref, string_at, create_string_buffer, pointer, \
|
byref, string_at, create_string_buffer, pointer, \
|
||||||
@ -11,11 +10,10 @@ from types import StringType, UnicodeType, IntType, FloatType
|
|||||||
|
|
||||||
# Python and GEOS-related dependencies.
|
# Python and GEOS-related dependencies.
|
||||||
import re
|
import re
|
||||||
from warnings import warn
|
|
||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY, ISQLQuote
|
|
||||||
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
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
|
from django.contrib.gis.geos.libgeos import lgeos, HAS_NUMPY, ISQLQuote
|
||||||
|
from django.contrib.gis.geos.pointer import GEOSPointer, NULL_GEOM
|
||||||
|
|
||||||
# Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure
|
# Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure
|
||||||
# to prevent potentially malicious input from reaching the underlying C
|
# to prevent potentially malicious input from reaching the underlying C
|
||||||
@ -26,28 +24,23 @@ wkt_regex = re.compile(r'^(POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTIL
|
|||||||
class GEOSGeometry(object):
|
class GEOSGeometry(object):
|
||||||
"A class that, generally, encapsulates a GEOS geometry."
|
"A class that, generally, encapsulates a GEOS geometry."
|
||||||
|
|
||||||
|
# Initially, all geometries use a NULL pointer.
|
||||||
|
_ptr = NULL_GEOM
|
||||||
|
|
||||||
#### Python 'magic' routines ####
|
#### Python 'magic' routines ####
|
||||||
def __init__(self, geo_input, input_type=False, parent=None, srid=None):
|
def __init__(self, geo_input, srid=None):
|
||||||
"""The constructor for GEOS geometry objects. May take the following
|
|
||||||
strings as inputs, WKT ("wkt"), HEXEWKB ("hex", PostGIS-specific canonical form).
|
|
||||||
|
|
||||||
The `input_type` keyword has been deprecated -- geometry type is now auto-detected.
|
|
||||||
|
|
||||||
The `parent` keyword is for internal use only, and indicates to the garbage collector
|
|
||||||
not to delete this geometry because it was spawned from a parent (e.g., the exterior
|
|
||||||
ring from a polygon). Its value is the GEOSPointer of the parent geometry.
|
|
||||||
"""
|
"""
|
||||||
|
The base constructor for GEOS geometry objects, and may take the following
|
||||||
|
string inputs: WKT and HEXEWKB (a PostGIS-specific canonical form).
|
||||||
|
|
||||||
# Initially, setting the pointer to NULL
|
The `srid` keyword is used to specify the Source Reference Identifier
|
||||||
self._ptr = GEOSPointer(0)
|
(SRID) number for this Geometry. If not set, the SRID will be None.
|
||||||
|
"""
|
||||||
|
|
||||||
if isinstance(geo_input, UnicodeType):
|
if isinstance(geo_input, UnicodeType):
|
||||||
# Encoding to ASCII, WKT or HEXEWKB doesn't need any more.
|
# Encoding to ASCII, WKT or HEXEWKB doesn't need any more.
|
||||||
geo_input = geo_input.encode('ascii')
|
geo_input = geo_input.encode('ascii')
|
||||||
|
|
||||||
if isinstance(geo_input, StringType):
|
if isinstance(geo_input, StringType):
|
||||||
if input_type: warn('input_type keyword is deprecated')
|
|
||||||
|
|
||||||
if hex_regex.match(geo_input):
|
if hex_regex.match(geo_input):
|
||||||
# If the regex matches, the geometry is in HEX form.
|
# If the regex matches, the geometry is in HEX form.
|
||||||
sz = c_size_t(len(geo_input))
|
sz = c_size_t(len(geo_input))
|
||||||
@ -58,47 +51,41 @@ class GEOSGeometry(object):
|
|||||||
g = lgeos.GEOSGeomFromWKT(c_char_p(geo_input))
|
g = lgeos.GEOSGeomFromWKT(c_char_p(geo_input))
|
||||||
else:
|
else:
|
||||||
raise GEOSException, 'given string input "%s" unrecognized as WKT or HEXEWKB.' % geo_input
|
raise GEOSException, 'given string input "%s" unrecognized as WKT or HEXEWKB.' % geo_input
|
||||||
|
|
||||||
elif isinstance(geo_input, (IntType, GEOSPointer)):
|
elif isinstance(geo_input, (IntType, GEOSPointer)):
|
||||||
# When the input is either a raw pointer value (an integer), or a GEOSPointer object.
|
# When the input is either a memory address (an integer), or a
|
||||||
|
# GEOSPointer object.
|
||||||
g = geo_input
|
g = geo_input
|
||||||
else:
|
else:
|
||||||
# Invalid geometry type.
|
# Invalid geometry type.
|
||||||
raise TypeError, 'Improper geometry input type: %s' % str(type(geo_input))
|
raise TypeError, 'Improper geometry input type: %s' % str(type(geo_input))
|
||||||
|
|
||||||
if bool(g):
|
if bool(g):
|
||||||
# If we have a GEOSPointer object, just set the '_ptr' attribute with input
|
# Setting the pointer object with a valid pointer.
|
||||||
if isinstance(g, GEOSPointer): self._ptr = g
|
self._ptr = GEOSPointer(g)
|
||||||
else: self._ptr.set(g) # Otherwise, set with the address
|
|
||||||
else:
|
else:
|
||||||
raise GEOSException, 'Could not initialize GEOS Geometry with given input.'
|
raise GEOSException, 'Could not initialize GEOS Geometry with given input.'
|
||||||
|
|
||||||
# Setting the 'parent' flag -- when the object is labeled with this flag
|
|
||||||
# it will not be destroyed by __del__(). This is used for child geometries spawned from
|
|
||||||
# parent geometries (e.g., LinearRings from a Polygon, Points from a MultiPoint, etc.).
|
|
||||||
if isinstance(parent, GEOSPointer):
|
|
||||||
self._parent = parent
|
|
||||||
else:
|
|
||||||
self._parent = GEOSPointer(0)
|
|
||||||
|
|
||||||
# Setting the SRID, if given.
|
# Setting the SRID, if given.
|
||||||
if srid and isinstance(srid, int): self.srid = srid
|
if srid and isinstance(srid, int): self.srid = srid
|
||||||
|
|
||||||
# Setting the class type (e.g., 'Point', 'Polygon', etc.)
|
# Setting the class type (e.g., 'Point', 'Polygon', etc.)
|
||||||
self.__class__ = GEOS_CLASSES[self.geom_type]
|
self.__class__ = GEOS_CLASSES[self.geom_type]
|
||||||
|
|
||||||
# Getting the coordinate sequence for the geometry (will be None on geometries that
|
# Setting the coordinate sequence for the geometry (will be None on
|
||||||
# do not have coordinate sequences)
|
# geometries that do not have coordinate sequences)
|
||||||
self._get_cs()
|
self._set_cs()
|
||||||
|
|
||||||
# Extra setup needed for Geometries that may be parents.
|
# _populate() needs to be called for parent Geometries.
|
||||||
if isinstance(self, (Polygon, GeometryCollection)): self._populate()
|
if isinstance(self, (Polygon, GeometryCollection)): self._populate()
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Destroys this geometry -- only if the pointer is valid and whether or not it belongs to a parent."
|
"""
|
||||||
#print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid)
|
Destroys this Geometry; in other words, frees the memory used by the
|
||||||
# Only calling destroy on valid pointers not spawned from a parent
|
GEOS C++ object -- but only if the pointer is not a child Geometry
|
||||||
if self._ptr.valid and not self._parent: lgeos.GEOSGeom_destroy(self._ptr())
|
(e.g., don't delete the LinearRings spawned from a Polygon).
|
||||||
|
"""
|
||||||
|
#print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid)
|
||||||
|
if not self._ptr.child: self._ptr.destroy()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"WKT is used for the string representation."
|
"WKT is used for the string representation."
|
||||||
@ -156,29 +143,38 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
# g1 ^= g2
|
# g1 ^= g2
|
||||||
def __ixor__(self, other):
|
def __ixor__(self, other):
|
||||||
"Reassigns this Geometry to the symmetric difference of this Geometry and the other."
|
"""
|
||||||
|
Reassigns this Geometry to the symmetric difference of this Geometry
|
||||||
|
and the other.
|
||||||
|
"""
|
||||||
return self.sym_difference(other)
|
return self.sym_difference(other)
|
||||||
|
|
||||||
|
#### Internal GEOSPointer-related routines. ####
|
||||||
def _nullify(self):
|
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
|
Returns the address of this Geometry, and nullifies any related pointers.
|
||||||
components) and to nullify the geometry itself to prevent future access.
|
This function is called if this Geometry is used in the initialization
|
||||||
Only the address (an integer) of the current geometry is returned for use in
|
of another Geometry.
|
||||||
initializing the new geometry."""
|
"""
|
||||||
# First getting the memory address of the geometry.
|
# First getting the memory address of the geometry.
|
||||||
address = self._ptr()
|
address = self._ptr()
|
||||||
|
|
||||||
# If the geometry is a child geometry, then the parent geometry pointer is
|
# If the geometry is a child geometry, then the parent geometry pointer is
|
||||||
# nullified.
|
# nullified.
|
||||||
if self._parent: self._parent.nullify()
|
if self._ptr.child:
|
||||||
|
p = self._ptr.parent
|
||||||
|
# If we have a grandchild (a LinearRing from a MultiPolygon or
|
||||||
|
# GeometryCollection), then nullify the collection as well.
|
||||||
|
if p.child: p.parent.nullify()
|
||||||
|
p.nullify()
|
||||||
|
|
||||||
# Nullifying the geometry pointer
|
# Nullifying the geometry pointer
|
||||||
self._ptr.nullify()
|
self._ptr.nullify()
|
||||||
|
|
||||||
return address
|
return address
|
||||||
|
|
||||||
def _reassign(self, new_geom):
|
def _reassign(self, new_geom):
|
||||||
"Internal routine for reassigning internal pointer to a new geometry."
|
"Reassigns the internal pointer to that of the new Geometry."
|
||||||
# Only can re-assign when given a pointer or a geometry.
|
# Only can re-assign when given a pointer or a geometry.
|
||||||
if not isinstance(new_geom, (GEOSPointer, GEOSGeometry)):
|
if not isinstance(new_geom, (GEOSPointer, GEOSGeometry)):
|
||||||
raise TypeError, 'cannot reassign geometry on given type: %s' % type(new_geom)
|
raise TypeError, 'cannot reassign geometry on given type: %s' % type(new_geom)
|
||||||
@ -186,8 +182,8 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
# Re-assigning the internal GEOSPointer to the new geometry, nullifying
|
# Re-assigning the internal GEOSPointer to the new geometry, nullifying
|
||||||
# the new Geometry in the process.
|
# the new Geometry in the process.
|
||||||
if isinstance(new_geom, GEOSGeometry): self._ptr.set(new_geom._nullify())
|
if isinstance(new_geom, GEOSPointer): self._ptr = new_geom
|
||||||
else: self._ptr = new_geom
|
else: self._ptr = GEOSPointer(new_geom._nullify())
|
||||||
|
|
||||||
# The new geometry class may be different from the original, so setting
|
# The new geometry class may be different from the original, so setting
|
||||||
# the __class__ and populating the internal geometry or ring dictionary.
|
# the __class__ and populating the internal geometry or ring dictionary.
|
||||||
@ -200,10 +196,10 @@ class GEOSGeometry(object):
|
|||||||
if proto == ISQLQuote:
|
if proto == ISQLQuote:
|
||||||
return self
|
return self
|
||||||
else:
|
else:
|
||||||
raise GEOSException, 'Error implementing psycopg2 protocol. Is psycopg2 installed?'
|
raise GEOSException, 'Error implementing psycopg2 protocol. Is psycopg2 installed?'
|
||||||
|
|
||||||
def getquoted(self):
|
def getquoted(self):
|
||||||
"Returns a properly quoted string for use in PostgresSQL/PostGIS."
|
"Returns a properly quoted string for use in PostgreSQL/PostGIS."
|
||||||
# Using ST_GeomFromText(), corresponds to SQL/MM ISO standard.
|
# Using ST_GeomFromText(), corresponds to SQL/MM ISO standard.
|
||||||
return "ST_GeomFromText('%s', %s)" % (self.wkt, self.srid or -1)
|
return "ST_GeomFromText('%s', %s)" % (self.wkt, self.srid or -1)
|
||||||
|
|
||||||
@ -217,10 +213,11 @@ class GEOSGeometry(object):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _get_cs(self):
|
def _set_cs(self):
|
||||||
"Gets the coordinate sequence for this Geometry."
|
"Sets the coordinate sequence for this Geometry."
|
||||||
if self.has_cs:
|
if self.has_cs:
|
||||||
self._ptr.set(lgeos.GEOSGeom_getCoordSeq(self._ptr()), coordseq=True)
|
if not self._ptr.coordseq_valid:
|
||||||
|
self._ptr.set_coordseq(lgeos.GEOSGeom_getCoordSeq(self._ptr()))
|
||||||
self._cs = GEOSCoordSeq(self._ptr, self.hasz)
|
self._cs = GEOSCoordSeq(self._ptr, self.hasz)
|
||||||
else:
|
else:
|
||||||
self._cs = None
|
self._cs = None
|
||||||
@ -272,16 +269,20 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
## Internal for GEOS unary & binary predicate functions ##
|
## Internal for GEOS unary & binary predicate functions ##
|
||||||
def _unary_predicate(self, func):
|
def _unary_predicate(self, func):
|
||||||
"""Returns the result, or raises an exception for the given unary
|
"""
|
||||||
predicate function."""
|
Returns the result, or raises an exception for the given unary predicate
|
||||||
|
function.
|
||||||
|
"""
|
||||||
val = func(self._ptr())
|
val = func(self._ptr())
|
||||||
if val == 0: return False
|
if val == 0: return False
|
||||||
elif val == 1: return True
|
elif val == 1: return True
|
||||||
else: raise GEOSException, '%s: exception occurred.' % func.__name__
|
else: raise GEOSException, '%s: exception occurred.' % func.__name__
|
||||||
|
|
||||||
def _binary_predicate(self, func, other, *args):
|
def _binary_predicate(self, func, other, *args):
|
||||||
"""Returns the result, or raises an exception for the given binary
|
"""
|
||||||
predicate function."""
|
Returns the result, or raises an exception for the given binary
|
||||||
|
predicate function.
|
||||||
|
"""
|
||||||
if not isinstance(other, GEOSGeometry):
|
if not isinstance(other, GEOSGeometry):
|
||||||
raise TypeError, 'Binary predicate operation ("%s") requires another GEOSGeometry instance.' % func.__name__
|
raise TypeError, 'Binary predicate operation ("%s") requires another GEOSGeometry instance.' % func.__name__
|
||||||
val = func(self._ptr(), other._ptr(), *args)
|
val = func(self._ptr(), other._ptr(), *args)
|
||||||
@ -292,7 +293,10 @@ class GEOSGeometry(object):
|
|||||||
#### Unary predicates ####
|
#### Unary predicates ####
|
||||||
@property
|
@property
|
||||||
def empty(self):
|
def empty(self):
|
||||||
"Returns a boolean indicating whether the set of points in this Geometry are empty."
|
"""
|
||||||
|
Returns a boolean indicating whether the set of points in this Geometry
|
||||||
|
are empty.
|
||||||
|
"""
|
||||||
return self._unary_predicate(lgeos.GEOSisEmpty)
|
return self._unary_predicate(lgeos.GEOSisEmpty)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -317,20 +321,26 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
#### Binary predicates. ####
|
#### Binary predicates. ####
|
||||||
def relate_pattern(self, other, pattern):
|
def relate_pattern(self, other, pattern):
|
||||||
"""Returns true if the elements in the DE-9IM intersection matrix for
|
"""
|
||||||
the two Geometries match the elements in pattern."""
|
Returns true if the elements in the DE-9IM intersection matrix for the
|
||||||
|
two Geometries match the elements in pattern.
|
||||||
|
"""
|
||||||
if len(pattern) > 9:
|
if len(pattern) > 9:
|
||||||
raise GEOSException, 'invalid intersection matrix pattern'
|
raise GEOSException, 'invalid intersection matrix pattern'
|
||||||
return self._binary_predicate(lgeos.GEOSRelatePattern, other, c_char_p(pattern))
|
return self._binary_predicate(lgeos.GEOSRelatePattern, other, c_char_p(pattern))
|
||||||
|
|
||||||
def disjoint(self, other):
|
def disjoint(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is FF*FF****."""
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
|
is FF*FF****.
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSDisjoint, other)
|
return self._binary_predicate(lgeos.GEOSDisjoint, other)
|
||||||
|
|
||||||
def touches(self, other):
|
def touches(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is FT*******, F**T***** or F***T****."""
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
|
is FT*******, F**T***** or F***T****.
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSTouches, other)
|
return self._binary_predicate(lgeos.GEOSTouches, other)
|
||||||
|
|
||||||
def intersects(self, other):
|
def intersects(self, other):
|
||||||
@ -338,14 +348,18 @@ class GEOSGeometry(object):
|
|||||||
return self._binary_predicate(lgeos.GEOSIntersects, other)
|
return self._binary_predicate(lgeos.GEOSIntersects, other)
|
||||||
|
|
||||||
def crosses(self, other):
|
def crosses(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is T*T****** (for a point and a curve,a point and an area or a line and
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
an area) 0******** (for two curves)."""
|
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._binary_predicate(lgeos.GEOSCrosses, other)
|
return self._binary_predicate(lgeos.GEOSCrosses, other)
|
||||||
|
|
||||||
def within(self, other):
|
def within(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is T*F**F***."""
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
|
is T*F**F***.
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSWithin, other)
|
return self._binary_predicate(lgeos.GEOSWithin, other)
|
||||||
|
|
||||||
def contains(self, other):
|
def contains(self, other):
|
||||||
@ -353,18 +367,24 @@ class GEOSGeometry(object):
|
|||||||
return self._binary_predicate(lgeos.GEOSContains, other)
|
return self._binary_predicate(lgeos.GEOSContains, other)
|
||||||
|
|
||||||
def overlaps(self, other):
|
def overlaps(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves)."""
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
|
is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves).
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSOverlaps, other)
|
return self._binary_predicate(lgeos.GEOSOverlaps, other)
|
||||||
|
|
||||||
def equals(self, other):
|
def equals(self, other):
|
||||||
"""Returns true if the DE-9IM intersection matrix for the two Geometries
|
"""
|
||||||
is T*F**FFF*."""
|
Returns true if the DE-9IM intersection matrix for the two Geometries
|
||||||
|
is T*F**FFF*.
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSEquals, other)
|
return self._binary_predicate(lgeos.GEOSEquals, other)
|
||||||
|
|
||||||
def equals_exact(self, other, tolerance=0):
|
def equals_exact(self, other, tolerance=0):
|
||||||
"""Returns true if the two Geometries are exactly equal, up to a
|
"""
|
||||||
specified tolerance."""
|
Returns true if the two Geometries are exactly equal, up to a
|
||||||
|
specified tolerance.
|
||||||
|
"""
|
||||||
return self._binary_predicate(lgeos.GEOSEqualsExact, other,
|
return self._binary_predicate(lgeos.GEOSEqualsExact, other,
|
||||||
c_double(tolerance))
|
c_double(tolerance))
|
||||||
|
|
||||||
@ -383,12 +403,12 @@ class GEOSGeometry(object):
|
|||||||
#### Output Routines ####
|
#### Output Routines ####
|
||||||
@property
|
@property
|
||||||
def wkt(self):
|
def wkt(self):
|
||||||
"Returns the WKT of the Geometry."
|
"Returns the WKT (Well-Known Text) of the Geometry."
|
||||||
return string_at(lgeos.GEOSGeomToWKT(self._ptr()))
|
return string_at(lgeos.GEOSGeomToWKT(self._ptr()))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hex(self):
|
def hex(self):
|
||||||
"Returns the WKBHEX of the Geometry."
|
"Returns the HEXEWKB of the Geometry."
|
||||||
sz = c_size_t()
|
sz = c_size_t()
|
||||||
h = lgeos.GEOSGeomToHEX_buf(self._ptr(), byref(sz))
|
h = lgeos.GEOSGeomToHEX_buf(self._ptr(), byref(sz))
|
||||||
return string_at(h, sz.value)
|
return string_at(h, sz.value)
|
||||||
@ -401,21 +421,26 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
#### Topology Routines ####
|
#### Topology Routines ####
|
||||||
def _unary_topology(self, func, *args):
|
def _unary_topology(self, func, *args):
|
||||||
"""Returns a GEOSGeometry for the given unary (takes only one Geomtery
|
"""
|
||||||
as a paramter) topological operation."""
|
Returns a GEOSGeometry for the given unary (takes only one Geomtery
|
||||||
|
as a paramter) topological operation.
|
||||||
|
"""
|
||||||
return GEOSGeometry(func(self._ptr(), *args), srid=self.srid)
|
return GEOSGeometry(func(self._ptr(), *args), srid=self.srid)
|
||||||
|
|
||||||
def _binary_topology(self, func, other, *args):
|
def _binary_topology(self, func, other, *args):
|
||||||
"""Returns a GEOSGeometry for the given binary (takes two Geometries
|
"""
|
||||||
as parameters) topological operation."""
|
Returns a GEOSGeometry for the given binary (takes two Geometries
|
||||||
|
as parameters) topological operation.
|
||||||
|
"""
|
||||||
return GEOSGeometry(func(self._ptr(), other._ptr(), *args), srid=self.srid)
|
return GEOSGeometry(func(self._ptr(), other._ptr(), *args), srid=self.srid)
|
||||||
|
|
||||||
def buffer(self, width, quadsegs=8):
|
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
|
Returns a geometry that represents all points whose distance from this
|
||||||
Spatial Reference System of this Geometry. The optional third parameter sets
|
Geometry is less than or equal to distance. Calculations are in the
|
||||||
the number of segment used to approximate a quarter circle (defaults to 8).
|
Spatial Reference System of this Geometry. The optional third parameter sets
|
||||||
(Text from PostGIS documentation at ch. 6.1.3)
|
the number of segment used to approximate a quarter circle (defaults to 8).
|
||||||
|
(Text from PostGIS documentation at ch. 6.1.3)
|
||||||
"""
|
"""
|
||||||
if not isinstance(width, (FloatType, IntType)):
|
if not isinstance(width, (FloatType, IntType)):
|
||||||
raise TypeError, 'width parameter must be a float'
|
raise TypeError, 'width parameter must be a float'
|
||||||
@ -430,9 +455,11 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def centroid(self):
|
def centroid(self):
|
||||||
"""The centroid is equal to the centroid of the set of component Geometries
|
"""
|
||||||
of highest dimension (since the lower-dimension geometries contribute zero
|
The centroid is equal to the centroid of the set of component Geometries
|
||||||
"weight" to the centroid)."""
|
of highest dimension (since the lower-dimension geometries contribute zero
|
||||||
|
"weight" to the centroid).
|
||||||
|
"""
|
||||||
return self._unary_topology(lgeos.GEOSGetCentroid)
|
return self._unary_topology(lgeos.GEOSGetCentroid)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -442,8 +469,10 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def convex_hull(self):
|
def convex_hull(self):
|
||||||
"""Returns the smallest convex Polygon that contains all the points
|
"""
|
||||||
in the Geometry."""
|
Returns the smallest convex Polygon that contains all the points
|
||||||
|
in the Geometry.
|
||||||
|
"""
|
||||||
return self._unary_topology(lgeos.GEOSConvexHull)
|
return self._unary_topology(lgeos.GEOSConvexHull)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -451,8 +480,16 @@ class GEOSGeometry(object):
|
|||||||
"Computes an interior point of this Geometry."
|
"Computes an interior point of this Geometry."
|
||||||
return self._unary_topology(lgeos.GEOSPointOnSurface)
|
return self._unary_topology(lgeos.GEOSPointOnSurface)
|
||||||
|
|
||||||
|
def simplify(self, tolerance=0.0):
|
||||||
|
"""
|
||||||
|
Returns the Geometry, simplified using the Douglas-Peucker algorithm
|
||||||
|
to the specified tolerance (higher tolerance => less points). If no
|
||||||
|
tolerance provided, defaults to 0.
|
||||||
|
"""
|
||||||
|
return self._unary_topology(lgeos.GEOSSimplify, c_double(tolerance))
|
||||||
|
|
||||||
def relate(self, other):
|
def relate(self, other):
|
||||||
"Returns the DE-9IM intersection matrix for this geometry and the other."
|
"Returns the DE-9IM intersection matrix for this Geometry and the other."
|
||||||
return string_at(lgeos.GEOSRelate(self._ptr(), other._ptr()))
|
return string_at(lgeos.GEOSRelate(self._ptr(), other._ptr()))
|
||||||
|
|
||||||
def difference(self, other):
|
def difference(self, other):
|
||||||
@ -461,8 +498,10 @@ class GEOSGeometry(object):
|
|||||||
return self._binary_topology(lgeos.GEOSDifference, other)
|
return self._binary_topology(lgeos.GEOSDifference, other)
|
||||||
|
|
||||||
def sym_difference(self, other):
|
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."""
|
Returns a set combining the points in this Geometry not in other,
|
||||||
|
and the points in other not in this Geometry.
|
||||||
|
"""
|
||||||
return self._binary_topology(lgeos.GEOSSymDifference, other)
|
return self._binary_topology(lgeos.GEOSSymDifference, other)
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other):
|
||||||
@ -483,9 +522,11 @@ class GEOSGeometry(object):
|
|||||||
else: return a.value
|
else: return a.value
|
||||||
|
|
||||||
def distance(self, other):
|
def distance(self, other):
|
||||||
"""Returns the distance between the closest points on this Geometry
|
"""
|
||||||
and the other. Units will be in those of the coordinate system. of
|
Returns the distance between the closest points on this Geometry
|
||||||
the Geometry."""
|
and the other. Units will be in those of the coordinate system of
|
||||||
|
the Geometry.
|
||||||
|
"""
|
||||||
if not isinstance(other, GEOSGeometry):
|
if not isinstance(other, GEOSGeometry):
|
||||||
raise TypeError, 'distance() works only on other GEOS Geometries.'
|
raise TypeError, 'distance() works only on other GEOS Geometries.'
|
||||||
dist = c_double()
|
dist = c_double()
|
||||||
@ -495,8 +536,10 @@ class GEOSGeometry(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def length(self):
|
def length(self):
|
||||||
"""Returns the length of this Geometry (e.g., 0 for point, or the
|
"""
|
||||||
circumfrence of a Polygon)."""
|
Returns the length of this Geometry (e.g., 0 for point, or the
|
||||||
|
circumfrence of a Polygon).
|
||||||
|
"""
|
||||||
l = c_double()
|
l = c_double()
|
||||||
status = lgeos.GEOSLength(self._ptr(), byref(l))
|
status = lgeos.GEOSLength(self._ptr(), byref(l))
|
||||||
if status != 1: return None
|
if status != 1: return None
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
This module houses the Geometry Collection objects:
|
This module houses the Geometry Collection objects:
|
||||||
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
||||||
"""
|
"""
|
||||||
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, 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
|
||||||
|
from django.contrib.gis.geos.libgeos import lgeos, get_pointer_arr, GEOM_PTR
|
||||||
|
from django.contrib.gis.geos.pointer import GEOSPointer
|
||||||
|
|
||||||
class GeometryCollection(GEOSGeometry):
|
class GeometryCollection(GEOSGeometry):
|
||||||
_allowed = (Point, LineString, LinearRing, Polygon)
|
_allowed = (Point, LineString, LinearRing, Polygon)
|
||||||
@ -15,16 +16,14 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"Initializes a Geometry Collection from a sequence of Geometry objects."
|
"Initializes a Geometry Collection from a sequence of Geometry objects."
|
||||||
# Setting up the collection for creation
|
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
|
||||||
self._geoms = {}
|
|
||||||
self._parent = None
|
|
||||||
|
|
||||||
# Checking the arguments
|
# Checking the arguments
|
||||||
if not args:
|
if not args:
|
||||||
raise TypeError, 'Must provide at least one Geometry to initialize %s.' % self.__class__.__name__
|
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
|
||||||
|
# in the first argument.
|
||||||
if isinstance(args[0], (TupleType, ListType)):
|
if isinstance(args[0], (TupleType, ListType)):
|
||||||
init_geoms = args[0]
|
init_geoms = args[0]
|
||||||
else:
|
else:
|
||||||
@ -36,40 +35,47 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
if False in [isinstance(geom, self._allowed) for geom in init_geoms]:
|
if False in [isinstance(geom, self._allowed) for geom in init_geoms]:
|
||||||
raise TypeError, 'Invalid Geometry type encountered in the arguments.'
|
raise TypeError, 'Invalid Geometry type encountered in the arguments.'
|
||||||
|
|
||||||
# Creating the geometry pointer array
|
# Creating the geometry pointer array, and populating each element in
|
||||||
|
# the array with the address of the Geometry returned by _nullify().
|
||||||
ngeom = len(init_geoms)
|
ngeom = len(init_geoms)
|
||||||
geoms = get_pointer_arr(ngeom)
|
geoms = get_pointer_arr(ngeom)
|
||||||
|
|
||||||
# Incrementing through each input geometry.
|
|
||||||
for i in xrange(ngeom):
|
for i in xrange(ngeom):
|
||||||
geoms[i] = cast(init_geoms[i]._nullify(), GEOM_PTR)
|
geoms[i] = cast(init_geoms[i]._nullify(), GEOM_PTR)
|
||||||
|
|
||||||
# Calling the parent class, using the pointer returned from GEOS createCollection()
|
# Calling the parent class, using the pointer returned from the
|
||||||
super(GeometryCollection, self).__init__(lgeos.GEOSGeom_createCollection(c_int(self._typeid), byref(geoms), c_uint(ngeom)), **kwargs)
|
# GEOS createCollection() factory.
|
||||||
|
addr = lgeos.GEOSGeom_createCollection(c_int(self._typeid),
|
||||||
|
byref(geoms), c_uint(ngeom))
|
||||||
|
super(GeometryCollection, self).__init__(addr, **kwargs)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Overloaded deletion method for Geometry Collections."
|
"Overloaded deletion method for Geometry Collections."
|
||||||
#print 'collection: 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._ptr.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
|
||||||
for k in self._geoms: self._geoms[k].nullify()
|
# attempted future access.
|
||||||
|
for g in self._ptr: g.nullify()
|
||||||
else:
|
else:
|
||||||
# Internal memory has become part of other Geometry objects, must delete the
|
# Internal memory has become part of other Geometry objects; must
|
||||||
# internal objects which are still valid individually, since calling destructor
|
# delete the internal objects which are still valid individually,
|
||||||
# on entire geometry will result in an attempted deletion of NULL pointers for
|
# because calling the destructor on the entire geometry will result
|
||||||
# the missing components.
|
# in an attempted deletion of NULL pointers for the missing
|
||||||
for k in self._geoms:
|
# components (which may crash Python).
|
||||||
if self._geoms[k].valid:
|
for g in self._ptr:
|
||||||
lgeos.GEOSGeom_destroy(self._geoms[k].address)
|
if len(g) > 0:
|
||||||
self._geoms[k].nullify()
|
# The collection geometry is a Polygon, destroy any leftover
|
||||||
|
# LinearRings.
|
||||||
|
for r in g: r.destroy()
|
||||||
|
g.destroy()
|
||||||
|
|
||||||
super(GeometryCollection, self).__del__()
|
super(GeometryCollection, self).__del__()
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"Returns the Geometry from this Collection at the given index (0-based)."
|
"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._ptr[index], srid=self.srid)
|
||||||
|
|
||||||
def __setitem__(self, index, geom):
|
def __setitem__(self, index, geom):
|
||||||
"Sets the Geometry at the specified index."
|
"Sets the Geometry at the specified index."
|
||||||
@ -105,14 +111,23 @@ class GeometryCollection(GEOSGeometry):
|
|||||||
def _nullify(self):
|
def _nullify(self):
|
||||||
"Overloaded from base method to nullify geometry references in this Collection."
|
"Overloaded from base method to nullify geometry references in this Collection."
|
||||||
# Nullifying the references to the internal Geometry objects from this Collection.
|
# Nullifying the references to the internal Geometry objects from this Collection.
|
||||||
for k in self._geoms: self._geoms[k].nullify()
|
for g in self._ptr: g.nullify()
|
||||||
return super(GeometryCollection, self)._nullify()
|
return super(GeometryCollection, self)._nullify()
|
||||||
|
|
||||||
def _populate(self):
|
def _populate(self):
|
||||||
"Populates the internal child geometry dictionary."
|
"Internal routine that populates the internal children geometries list."
|
||||||
self._geoms = {}
|
ptr_list = []
|
||||||
for i in xrange(self.num_geom):
|
for i in xrange(len(self)):
|
||||||
self._geoms[i] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(i)))
|
# Getting the geometry pointer for the geometry at the index.
|
||||||
|
geom_ptr = lgeos.GEOSGetGeometryN(self._ptr(), c_int(i))
|
||||||
|
|
||||||
|
# Adding the coordinate sequence to the list, or using None if the
|
||||||
|
# collection Geometry doesn't support coordinate sequences.
|
||||||
|
if lgeos.GEOSGeomTypeId(geom_ptr) in (0, 1, 2):
|
||||||
|
ptr_list.append((geom_ptr, lgeos.GEOSGeom_getCoordSeq(geom_ptr)))
|
||||||
|
else:
|
||||||
|
ptr_list.append((geom_ptr, None))
|
||||||
|
self._ptr.set_children(ptr_list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kml(self):
|
def kml(self):
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY
|
"""
|
||||||
|
This module houses the GEOSCoordSeq object, and is used internally
|
||||||
|
by GEOSGeometry to house the actual coordinates of the Point,
|
||||||
|
LineString, and LinearRing geometries.
|
||||||
|
"""
|
||||||
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError
|
||||||
|
from django.contrib.gis.geos.libgeos import lgeos, HAS_NUMPY
|
||||||
|
from django.contrib.gis.geos.pointer import GEOSPointer
|
||||||
from ctypes import c_double, c_int, c_uint, byref
|
from ctypes import c_double, c_int, c_uint, byref
|
||||||
from types import ListType, TupleType
|
from types import ListType, TupleType
|
||||||
if HAS_NUMPY: from numpy import ndarray
|
if HAS_NUMPY: from numpy import ndarray
|
||||||
|
|
||||||
"""
|
|
||||||
This module houses the GEOSCoordSeq object, and is used internally
|
|
||||||
by GEOSGeometry to house the actual coordinates of the Point,
|
|
||||||
LineString, and LinearRing geometries.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class GEOSCoordSeq(object):
|
class GEOSCoordSeq(object):
|
||||||
"The internal representation of a list of coordinates inside a Geometry."
|
"The internal representation of a list of coordinates inside a Geometry."
|
||||||
|
|
||||||
@ -31,18 +31,18 @@ class GEOSCoordSeq(object):
|
|||||||
return int(self.size)
|
return int(self.size)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"The string representation of the coordinate sequence."
|
"Returns the string representation of the coordinate sequence."
|
||||||
return str(self.tuple)
|
return str(self.tuple)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"Can use the index [] operator to get coordinate sequence at an index."
|
"Returns the coordinate sequence value at the given index."
|
||||||
coords = [self.getX(index), self.getY(index)]
|
coords = [self.getX(index), self.getY(index)]
|
||||||
if self.dims == 3 and self._z:
|
if self.dims == 3 and self._z:
|
||||||
coords.append(self.getZ(index))
|
coords.append(self.getZ(index))
|
||||||
return tuple(coords)
|
return tuple(coords)
|
||||||
|
|
||||||
def __setitem__(self, index, value):
|
def __setitem__(self, index, value):
|
||||||
"Can use the index [] operator to set coordinate sequence at an index."
|
"Sets the coordinate sequence value at the given index."
|
||||||
# Checking the input value
|
# Checking the input value
|
||||||
if isinstance(value, (ListType, TupleType)):
|
if isinstance(value, (ListType, TupleType)):
|
||||||
pass
|
pass
|
||||||
@ -66,7 +66,7 @@ class GEOSCoordSeq(object):
|
|||||||
|
|
||||||
#### Internal Routines ####
|
#### Internal Routines ####
|
||||||
def _checkindex(self, index):
|
def _checkindex(self, index):
|
||||||
"Checks the index."
|
"Checks the given index."
|
||||||
sz = self.size
|
sz = self.size
|
||||||
if (sz < 1) or (index < 0) or (index >= sz):
|
if (sz < 1) or (index < 0) or (index >= sz):
|
||||||
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index)
|
||||||
@ -78,7 +78,7 @@ class GEOSCoordSeq(object):
|
|||||||
|
|
||||||
#### Ordinate getting and setting routines ####
|
#### Ordinate getting and setting routines ####
|
||||||
def getOrdinate(self, dimension, index):
|
def getOrdinate(self, dimension, index):
|
||||||
"Gets the value for the given dimension and index."
|
"Returns the value for the given dimension and index."
|
||||||
self._checkindex(index)
|
self._checkindex(index)
|
||||||
self._checkdim(dimension)
|
self._checkdim(dimension)
|
||||||
|
|
||||||
@ -152,7 +152,10 @@ class GEOSCoordSeq(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def hasz(self):
|
def hasz(self):
|
||||||
"Inherits this from the parent geometry."
|
"""
|
||||||
|
Returns whether this coordinate sequence is 3D. This property value is
|
||||||
|
inherited from the parent Geometry.
|
||||||
|
"""
|
||||||
return self._z
|
return self._z
|
||||||
|
|
||||||
### Other Methods ###
|
### Other Methods ###
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
|
"""
|
||||||
|
This module houses the GEOS exceptions, specifically, GEOSException and
|
||||||
|
GEOSGeometryIndexError.
|
||||||
|
"""
|
||||||
|
|
||||||
class GEOSException(Exception):
|
class GEOSException(Exception):
|
||||||
"The base GEOS exception, indicates a GEOS-related error."
|
"The base GEOS exception, indicates a GEOS-related error."
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class GEOSGeometryIndexError(GEOSException, KeyError):
|
class GEOSGeometryIndexError(GEOSException, KeyError):
|
||||||
"""This exception is raised when an invalid index is encountered, and has
|
"""
|
||||||
|
This exception is raised when an invalid index is encountered, and has
|
||||||
the 'silent_variable_feature' attribute set to true. This ensures that
|
the 'silent_variable_feature' attribute set to true. This ensures that
|
||||||
django's templates proceed to use the next lookup type gracefully when
|
django's templates proceed to use the next lookup type gracefully when
|
||||||
an Exception is raised. Fixes ticket #4740.
|
an Exception is raised. Fixes ticket #4740.
|
||||||
"""
|
"""
|
||||||
# "If, during the method lookup, a method raises an exception, the exception
|
# "If, during the method lookup, a method raises an exception, the exception
|
||||||
# will be propagated, unless the exception has an attribute silent_variable_failure
|
# will be propagated, unless the exception has an attribute
|
||||||
# whose value is True." -- django template docs.
|
# `silent_variable_failure` whose value is True." -- Django template docs.
|
||||||
silent_variable_failure = True
|
silent_variable_failure = True
|
||||||
|
@ -3,36 +3,33 @@
|
|||||||
geometry classes. All geometry classes in this module inherit from
|
geometry classes. All geometry classes in this module inherit from
|
||||||
GEOSGeometry.
|
GEOSGeometry.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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.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.coordseq import GEOSCoordSeq, create_cs
|
||||||
|
from django.contrib.gis.geos.libgeos import lgeos, get_pointer_arr, GEOM_PTR, HAS_NUMPY
|
||||||
|
from django.contrib.gis.geos.pointer import GEOSPointer
|
||||||
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
|
||||||
|
|
||||||
class Point(GEOSGeometry):
|
class Point(GEOSGeometry):
|
||||||
|
|
||||||
def __init__(self, x, y=None, z=None, srid=None):
|
def __init__(self, x, y=None, z=None, srid=None):
|
||||||
"""The Point object may be initialized with either a tuple, or individual
|
"""
|
||||||
parameters. For example:
|
The Point object may be initialized with either a tuple, or individual
|
||||||
|
parameters. For example:
|
||||||
>>> p = Point((5, 23)) # 2D point, passed in as a tuple
|
>>> p = Point((5, 23)) # 2D point, passed in as a tuple
|
||||||
>>> 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._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.
|
||||||
ndim = len(x)
|
ndim = len(x)
|
||||||
if ndim < 2 or ndim > 3:
|
if ndim < 2 or ndim > 3:
|
||||||
raise TypeError, 'Invalid sequence parameter: %s' % str(x)
|
raise TypeError, 'Invalid sequence parameter: %s' % str(x)
|
||||||
coords = x
|
coords = x
|
||||||
elif isinstance(x, (IntType, FloatType)) and isinstance(y, (IntType, FloatType)):
|
elif isinstance(x, (IntType, FloatType)) and isinstance(y, (IntType, FloatType)):
|
||||||
# Here X, Y, and (optionally) Z were passed in individually as parameters.
|
# Here X, Y, and (optionally) Z were passed in individually, as parameters.
|
||||||
if isinstance(z, (IntType, FloatType)):
|
if isinstance(z, (IntType, FloatType)):
|
||||||
ndim = 3
|
ndim = 3
|
||||||
coords = [x, y, z]
|
coords = [x, y, z]
|
||||||
@ -42,22 +39,17 @@ class Point(GEOSGeometry):
|
|||||||
else:
|
else:
|
||||||
raise TypeError, 'Invalid parameters given for Point initialization.'
|
raise TypeError, 'Invalid parameters given for Point initialization.'
|
||||||
|
|
||||||
# Creating the coordinate sequence
|
# Creating the coordinate sequence, and setting X, Y, [Z]
|
||||||
cs = create_cs(c_uint(1), c_uint(ndim))
|
cs = create_cs(c_uint(1), c_uint(ndim))
|
||||||
|
|
||||||
# Setting the X
|
|
||||||
status = lgeos.GEOSCoordSeq_setX(cs, c_uint(0), c_double(coords[0]))
|
status = lgeos.GEOSCoordSeq_setX(cs, c_uint(0), c_double(coords[0]))
|
||||||
if not status: raise GEOSException, 'Could not set X during Point initialization.'
|
if not status: raise GEOSException, 'Could not set X during Point initialization.'
|
||||||
|
|
||||||
# Setting the Y
|
|
||||||
status = lgeos.GEOSCoordSeq_setY(cs, c_uint(0), c_double(coords[1]))
|
status = lgeos.GEOSCoordSeq_setY(cs, c_uint(0), c_double(coords[1]))
|
||||||
if not status: raise GEOSException, 'Could not set Y during Point initialization.'
|
if not status: raise GEOSException, 'Could not set Y during Point initialization.'
|
||||||
|
|
||||||
# Setting the Z
|
|
||||||
if ndim == 3:
|
if ndim == 3:
|
||||||
status = lgeos.GEOSCoordSeq_setZ(cs, c_uint(0), c_double(coords[2]))
|
status = lgeos.GEOSCoordSeq_setZ(cs, c_uint(0), c_double(coords[2]))
|
||||||
|
|
||||||
# Initializing from the geometry, and getting a Python object
|
# Initializing using the address returned from the GEOS
|
||||||
|
# createPoint factory.
|
||||||
super(Point, self).__init__(lgeos.GEOSGeom_createPoint(cs), srid=srid)
|
super(Point, self).__init__(lgeos.GEOSGeom_createPoint(cs), srid=srid)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
@ -125,9 +117,10 @@ class LineString(GEOSGeometry):
|
|||||||
|
|
||||||
#### Python 'magic' routines ####
|
#### Python 'magic' routines ####
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initializes on the given sequence -- may take lists, tuples, NumPy arrays
|
"""
|
||||||
of X,Y pairs, or Point objects. If Point objects are used, ownership is
|
Initializes on the given sequence -- may take lists, tuples, NumPy arrays
|
||||||
_not_ transferred to the LineString object.
|
of X,Y pairs, or Point objects. If Point objects are used, ownership is
|
||||||
|
_not_ transferred to the LineString object.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
ls = LineString((1, 1), (2, 2))
|
ls = LineString((1, 1), (2, 2))
|
||||||
@ -135,11 +128,7 @@ 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
|
# If only one argument provided, set the coords array appropriately
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
|
||||||
self._parent = None
|
|
||||||
|
|
||||||
# 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]
|
||||||
else: coords = args
|
else: coords = args
|
||||||
|
|
||||||
@ -166,10 +155,9 @@ class LineString(GEOSGeometry):
|
|||||||
else:
|
else:
|
||||||
raise TypeError, 'Invalid initialization input for LineStrings.'
|
raise TypeError, 'Invalid initialization input for LineStrings.'
|
||||||
|
|
||||||
# Creating the coordinate sequence
|
# Creating a coordinate sequence object because it is easier to
|
||||||
|
# set the points using GEOSCoordSeq.__setitem__().
|
||||||
cs = GEOSCoordSeq(GEOSPointer(0, create_cs(c_uint(ncoords), c_uint(ndim))), z=bool(ndim==3))
|
cs = GEOSCoordSeq(GEOSPointer(0, create_cs(c_uint(ncoords), c_uint(ndim))), z=bool(ndim==3))
|
||||||
|
|
||||||
# Setting each point in the coordinate sequence
|
|
||||||
for i in xrange(ncoords):
|
for i in xrange(ncoords):
|
||||||
if numpy_coords: cs[i] = coords[i,:]
|
if numpy_coords: cs[i] = coords[i,:]
|
||||||
elif isinstance(coords[i], Point): cs[i] = coords[i].tuple
|
elif isinstance(coords[i], Point): cs[i] = coords[i].tuple
|
||||||
@ -184,7 +172,8 @@ class LineString(GEOSGeometry):
|
|||||||
# If SRID was passed in with the keyword arguments
|
# If SRID was passed in with the keyword arguments
|
||||||
srid = kwargs.get('srid', None)
|
srid = kwargs.get('srid', None)
|
||||||
|
|
||||||
# Calling the base geometry initialization with the returned pointer from the function.
|
# Calling the base geometry initialization with the returned pointer
|
||||||
|
# from the function.
|
||||||
super(LineString, self).__init__(func(cs._ptr.coordseq()), srid=srid)
|
super(LineString, self).__init__(func(cs._ptr.coordseq()), srid=srid)
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
@ -214,9 +203,11 @@ class LineString(GEOSGeometry):
|
|||||||
return self._cs.tuple
|
return self._cs.tuple
|
||||||
|
|
||||||
def _listarr(self, func):
|
def _listarr(self, func):
|
||||||
"""Internal routine that returns a sequence (list) corresponding with
|
"""
|
||||||
the given function. Will return a numpy array if possible."""
|
Internal routine that returns a sequence (list) corresponding with
|
||||||
lst = [func(i) for i in xrange(len(self))] # constructing the list, using the function
|
the given function. Will return a numpy array if possible.
|
||||||
|
"""
|
||||||
|
lst = [func(i) for i in xrange(len(self))]
|
||||||
if HAS_NUMPY: return array(lst) # ARRRR!
|
if HAS_NUMPY: return array(lst) # ARRRR!
|
||||||
else: return lst
|
else: return lst
|
||||||
|
|
||||||
@ -251,16 +242,16 @@ class LinearRing(LineString):
|
|||||||
class Polygon(GEOSGeometry):
|
class Polygon(GEOSGeometry):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initializes on an exterior ring and a sequence of holes (both instances of LinearRings.
|
|
||||||
All LinearRing instances used for creation will become owned by this Polygon.
|
|
||||||
|
|
||||||
Examples, where shell, hole1, and hole2 are valid LinearRing geometries:
|
|
||||||
poly = Polygon(shell, hole1, hole2)
|
|
||||||
poly = Polygon(shell, (hole1, hole2))
|
|
||||||
"""
|
"""
|
||||||
self._ptr = GEOSPointer(0) # Initially NULL
|
Initializes on an exterior ring and a sequence of holes (both
|
||||||
self._parent = None
|
instances of LinearRings. All LinearRing instances used for creation
|
||||||
self._rings = {}
|
will become owned by this Polygon.
|
||||||
|
|
||||||
|
Below are some examples of initialization, where shell, hole1, and
|
||||||
|
hole2 are valid LinearRing geometries:
|
||||||
|
>>> poly = Polygon(shell, hole1, hole2)
|
||||||
|
>>> poly = Polygon(shell, (hole1, hole2))
|
||||||
|
"""
|
||||||
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.'
|
||||||
|
|
||||||
@ -293,26 +284,28 @@ class Polygon(GEOSGeometry):
|
|||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
"Overloaded deletion method for Polygons."
|
"Overloaded deletion method for Polygons."
|
||||||
#print 'polygon: 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._ptr.parent, self._ptr.valid)
|
||||||
# If this geometry is still valid, it hasn't been modified by others.
|
# Not performed on children Polygons from MultiPolygon or GeometryCollection objects.
|
||||||
if self._ptr.valid:
|
if not self._ptr.child:
|
||||||
# Nulling the pointers to internal rings, preventing any attempted future access
|
# If this geometry is still valid, it hasn't been modified by others.
|
||||||
for k in self._rings: self._rings[k].nullify()
|
if self._ptr.valid:
|
||||||
elif not self._parent:
|
# Nulling the pointers to internal rings, preventing any
|
||||||
# Internal memory has become part of other objects; must delete the
|
# attempted future access.
|
||||||
# internal objects which are still valid individually, since calling
|
for r in self._ptr: r.nullify()
|
||||||
# destructor on entire geometry will result in an attempted deletion
|
else:
|
||||||
# of NULL pointers for the missing components. Not performed on
|
# Internal memory has become part of other Geometry objects; must
|
||||||
# children Polygons from MultiPolygon or GeometryCollection objects.
|
# delete the internal objects which are still valid individually,
|
||||||
for k in self._rings:
|
# because calling the destructor on entire geometry will result
|
||||||
if self._rings[k].valid:
|
# in an attempted deletion of NULL pointers for the missing
|
||||||
lgeos.GEOSGeom_destroy(self._rings[k].address)
|
# components (which may crash Python).
|
||||||
self._rings[k].nullify()
|
for r in self._ptr: r.destroy()
|
||||||
super(Polygon, self).__del__()
|
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
|
"""
|
||||||
return the exterior ring. Indices > 0 will return the interior ring."""
|
Returns the ring at the specified index. The first index, 0, will always
|
||||||
|
return the exterior ring. Indices > 0 will return the interior ring.
|
||||||
|
"""
|
||||||
if index == 0:
|
if index == 0:
|
||||||
return self.exterior_ring
|
return self.exterior_ring
|
||||||
else:
|
else:
|
||||||
@ -353,31 +346,36 @@ class Polygon(GEOSGeometry):
|
|||||||
def _nullify(self):
|
def _nullify(self):
|
||||||
"Overloaded from base method to nullify ring references as well."
|
"Overloaded from base method to nullify ring references as well."
|
||||||
# Nullifying the references to the internal rings of this Polygon.
|
# Nullifying the references to the internal rings of this Polygon.
|
||||||
for k in self._rings: self._rings[k].nullify()
|
for r in self._ptr: r.nullify()
|
||||||
return super(Polygon, self)._nullify()
|
return super(Polygon, self)._nullify()
|
||||||
|
|
||||||
def _populate(self):
|
def _populate(self):
|
||||||
"Populates the internal rings dictionary."
|
"Internal routine for populating the internal ring pointers."
|
||||||
# Getting the exterior ring first for the 0th index.
|
# Only populate if there aren't already children pointers.
|
||||||
self._rings = {0 : GEOSPointer(lgeos.GEOSGetExteriorRing(self._ptr()))}
|
if len(self._ptr) == 0:
|
||||||
|
# Getting the exterior ring pointer address.
|
||||||
# Getting the interior rings.
|
ring_list = [lgeos.GEOSGetExteriorRing(self._ptr())]
|
||||||
for i in xrange(self.num_interior_rings):
|
# Getting the interior ring pointer addresses.
|
||||||
self._rings[i+1] = GEOSPointer(lgeos.GEOSGetInteriorRingN(self._ptr(), c_int(i)))
|
ring_list += [lgeos.GEOSGetInteriorRingN(self._ptr(), c_int(i)) for i in xrange(self.num_interior_rings)]
|
||||||
|
# Getting the coordinate sequence pointer address for each of the rings.
|
||||||
|
ptr_list = [(ring_ptr, lgeos.GEOSGeom_getCoordSeq(ring_ptr)) for ring_ptr in ring_list]
|
||||||
|
# Setting the children pointers.
|
||||||
|
self._ptr.set_children(ptr_list)
|
||||||
|
|
||||||
def get_interior_ring(self, ring_i):
|
def get_interior_ring(self, ring_i):
|
||||||
"""Gets the interior ring at the specified index,
|
"""
|
||||||
0 is for the first interior ring, not the exterior ring."""
|
Gets the interior ring at the specified index, 0 is for the first
|
||||||
|
interior ring, not the exterior ring.
|
||||||
|
"""
|
||||||
# Returning the ring from the internal ring dictionary (have to add one
|
# Returning the ring from the internal ring dictionary (have to add one
|
||||||
# to index since all internal rings come after the exterior ring)
|
# to index since all internal rings come after the exterior ring)
|
||||||
self._checkindex(ring_i+1)
|
self._checkindex(ring_i+1)
|
||||||
return GEOSGeometry(self._rings[ring_i+1], parent=self._ptr, srid=self.srid)
|
return GEOSGeometry(self._ptr[ring_i+1], srid=self.srid)
|
||||||
|
|
||||||
#### Polygon Properties ####
|
#### Polygon Properties ####
|
||||||
@property
|
@property
|
||||||
def num_interior_rings(self):
|
def num_interior_rings(self):
|
||||||
"Returns the number of interior rings."
|
"Returns the number of interior rings."
|
||||||
|
|
||||||
# Getting the number of rings
|
# Getting the number of rings
|
||||||
n = lgeos.GEOSGetNumInteriorRings(self._ptr())
|
n = lgeos.GEOSGetNumInteriorRings(self._ptr())
|
||||||
|
|
||||||
@ -387,7 +385,7 @@ class Polygon(GEOSGeometry):
|
|||||||
|
|
||||||
def get_ext_ring(self):
|
def get_ext_ring(self):
|
||||||
"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._ptr[0], srid=self.srid)
|
||||||
|
|
||||||
def set_ext_ring(self, ring):
|
def set_ext_ring(self, ring):
|
||||||
"Sets the exterior ring of the Polygon."
|
"Sets the exterior ring of the Polygon."
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
This module houses the ctypes initialization procedures, as well
|
This module houses the ctypes initialization procedures, as well
|
||||||
as the notice and error handler function callbacks (get called
|
as the notice and error handler function callbacks (get called
|
||||||
when an error occurs in GEOS).
|
when an error occurs in GEOS).
|
||||||
|
|
||||||
This module also houses GEOS Pointer utilities, including the
|
This module also houses GEOS Pointer utilities, including
|
||||||
GEOSPointer class, get_pointer_arr(), GEOM_PTR, and init_from_geom().
|
get_pointer_arr(), and GEOM_PTR.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.gis.geos.error import GEOSException
|
from django.contrib.gis.geos.error import GEOSException
|
||||||
from ctypes import c_char_p, c_int, pointer, CDLL, CFUNCTYPE, POINTER, Structure
|
from ctypes import c_char_p, c_int, string_at, CDLL, CFUNCTYPE, POINTER, Structure
|
||||||
import os, sys
|
import atexit, os, sys
|
||||||
|
|
||||||
# NumPy supported?
|
# NumPy supported?
|
||||||
try:
|
try:
|
||||||
@ -18,7 +18,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_NUMPY = False
|
HAS_NUMPY = False
|
||||||
|
|
||||||
# Are psycopg2 and GeoDjango models being used?
|
# Is psycopg2 available?
|
||||||
try:
|
try:
|
||||||
from psycopg2.extensions import ISQLQuote
|
from psycopg2.extensions import ISQLQuote
|
||||||
except (ImportError, EnvironmentError):
|
except (ImportError, EnvironmentError):
|
||||||
@ -67,11 +67,11 @@ error_h = ERRORFUNC(error_h)
|
|||||||
|
|
||||||
# The initGEOS routine should be called first, however, that routine takes
|
# The initGEOS routine should be called first, however, that routine takes
|
||||||
# the notice and error functions as parameters. Here is the C code that
|
# the notice and error functions as parameters. Here is the C code that
|
||||||
# we need to wrap:
|
# is wrapped:
|
||||||
# "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 object, related C data structures, and functions. ####
|
#### GEOS Geometry C data structures, and utility functions. ####
|
||||||
|
|
||||||
# Opaque GEOS geometry structure
|
# Opaque GEOS geometry structure
|
||||||
class GEOSGeom_t(Structure):
|
class GEOSGeom_t(Structure):
|
||||||
@ -81,87 +81,16 @@ class GEOSGeom_t(Structure):
|
|||||||
# 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()
|
||||||
|
|
||||||
class GEOSPointer(object):
|
def geos_version():
|
||||||
"""The GEOSPointer provides a layer of abstraction in accessing the values returned by
|
"Returns the string version of GEOS."
|
||||||
GEOS geometry creation routines. Memory addresses (integers) are kept in a C pointer,
|
return string_at(lgeos.GEOSversion())
|
||||||
which allows parent geometries to be 'nullified' if their children's memory is used
|
|
||||||
in construction of another geometry. Related coordinate sequence pointers are kept
|
|
||||||
in this object for the same reason."""
|
|
||||||
|
|
||||||
### Python 'magic' routines ###
|
# Calling the finishGEOS() upon exit of the interpreter.
|
||||||
def __init__(self, address, coordseq=0):
|
atexit.register(lgeos.finishGEOS)
|
||||||
"Initializes on an address (an integer)."
|
|
||||||
if isinstance(address, int):
|
|
||||||
self._geom = pointer(c_int(address))
|
|
||||||
self._coordseq = pointer(c_int(coordseq))
|
|
||||||
else:
|
|
||||||
raise TypeError, 'GEOSPointer object must initialize with an integer.'
|
|
||||||
|
|
||||||
def __call__(self):
|
|
||||||
"""If the pointer is NULL, then an exception will be raised, otherwise the
|
|
||||||
address value (an integer) will be returned."""
|
|
||||||
if self.valid: return self.address
|
|
||||||
else: raise GEOSException, 'GEOS pointer no longer valid (was this geometry or the parent geometry deleted or modified?)'
|
|
||||||
|
|
||||||
def __nonzero__(self):
|
|
||||||
"Returns True when the GEOSPointer is valid."
|
|
||||||
return self.valid
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self.address)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return 'GEOSPointer(%s)' % self.address
|
|
||||||
|
|
||||||
### GEOSPointer Properties ###
|
|
||||||
@property
|
|
||||||
def address(self):
|
|
||||||
"Returns the address of the GEOSPointer (represented as an integer)."
|
|
||||||
return self._geom.contents.value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def valid(self):
|
|
||||||
"Tests whether this GEOSPointer is valid."
|
|
||||||
if bool(self.address): return True
|
|
||||||
else: return False
|
|
||||||
|
|
||||||
### Coordinate Sequence routines and properties ###
|
|
||||||
def coordseq(self):
|
|
||||||
"If the coordinate sequence pointer is NULL (0), an exception will be raised."
|
|
||||||
if self.coordseq_valid: return self.coordseq_address
|
|
||||||
else: raise GEOSException, 'GEOS coordinate sequence pointer invalid (was this geometry or the parent geometry deleted or modified?)'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coordseq_address(self):
|
|
||||||
"Returns the address of the related coordinate sequence."
|
|
||||||
return self._coordseq.contents.value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def coordseq_valid(self):
|
|
||||||
"Returns True if the coordinate sequence address is valid, False otherwise."
|
|
||||||
if bool(self.coordseq_address): return True
|
|
||||||
else: return False
|
|
||||||
|
|
||||||
### GEOSPointer Methods ###
|
|
||||||
def set(self, address, coordseq=False):
|
|
||||||
"Sets this pointer with the new address (represented as an integer)"
|
|
||||||
if not isinstance(address, int):
|
|
||||||
raise TypeError, 'GEOSPointer must be set with an address (an integer).'
|
|
||||||
if coordseq:
|
|
||||||
self._coordseq.contents = c_int(address)
|
|
||||||
else:
|
|
||||||
self._geom.contents = c_int(address)
|
|
||||||
|
|
||||||
def nullify(self):
|
|
||||||
"""Nullify this geometry pointer (set the address to 0). This does not delete
|
|
||||||
any memory, rather, it sets the GEOS pointer to a NULL address, to prevent
|
|
||||||
access to addressses of deleted objects."""
|
|
||||||
# Nullifying both the geometry and coordinate sequence pointer.
|
|
||||||
self.set(0)
|
|
||||||
self.set(0, coordseq=True)
|
|
||||||
|
265
django/contrib/gis/geos/pointer.py
Normal file
265
django/contrib/gis/geos/pointer.py
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
"""
|
||||||
|
This module houses the GEOSPointer class, used by GEOS Geometry objects
|
||||||
|
internally for memory management. Do not modify unless you _really_
|
||||||
|
know what you're doing.
|
||||||
|
"""
|
||||||
|
from ctypes import cast, c_int, c_void_p, pointer, POINTER, Structure
|
||||||
|
from django.contrib.gis.geos.error import GEOSException
|
||||||
|
from django.contrib.gis.geos.libgeos import lgeos
|
||||||
|
|
||||||
|
# This C data structure houses the memory addresses (integers) of the
|
||||||
|
# pointers returned from the GEOS C routines.
|
||||||
|
class GEOSStruct(Structure): pass
|
||||||
|
GEOSStruct._fields_ = [("geom", POINTER(c_int)), # for the geometry
|
||||||
|
("cs", POINTER(c_int)), # for geometries w/coordinate sequences
|
||||||
|
("parent", POINTER(GEOSStruct)), # points to the GEOSStruct of the parent
|
||||||
|
("child", c_void_p), # points to an array of GEOSStructs
|
||||||
|
("nchild", c_int), # the number of children
|
||||||
|
]
|
||||||
|
|
||||||
|
class GEOSPointer(object):
|
||||||
|
"""
|
||||||
|
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, which allows parent geometries to be 'nullified'
|
||||||
|
when a child's memory is used in construction of another geometry.
|
||||||
|
|
||||||
|
This object is also used to store pointers for any associated coordinate
|
||||||
|
sequence and may store the pointers for any children geometries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#### Python 'magic' routines ####
|
||||||
|
def __init__(self, address, coordseq=0):
|
||||||
|
"""
|
||||||
|
Initializes on an address (an integer), another GEOSPointer, or a
|
||||||
|
GEOSStruct object.
|
||||||
|
"""
|
||||||
|
if isinstance(address, int):
|
||||||
|
self._struct = GEOSStruct()
|
||||||
|
# Integer addresses passed in, use the 'set' methods.
|
||||||
|
self.set(address)
|
||||||
|
if coordseq: self.set_coordseq(coordseq)
|
||||||
|
elif isinstance(address, GEOSPointer):
|
||||||
|
# Initializing from another GEOSPointer
|
||||||
|
self._struct = address._struct
|
||||||
|
elif isinstance(address, GEOSStruct):
|
||||||
|
# GEOSStruct passed directly in as a parameter
|
||||||
|
self._struct = address
|
||||||
|
else:
|
||||||
|
raise TypeError, 'GEOSPointer object must initialize with an integer.'
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
"""
|
||||||
|
Returns the address value (an integer) of the GEOS Geometry pointer.
|
||||||
|
If the pointer is NULL, a GEOSException will be raised, thus preventing
|
||||||
|
an invalid memory address from being passed to a C routine.
|
||||||
|
"""
|
||||||
|
if self.valid: return self.address
|
||||||
|
else: raise GEOSException, 'GEOS pointer no longer valid (was this geometry or the parent geometry deleted or modified?)'
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
"Returns a GEOSpointer object at the given child index."
|
||||||
|
n_child = len(self)
|
||||||
|
if n_child:
|
||||||
|
if index < 0 or index >= n_child:
|
||||||
|
raise IndexError, 'invalid child index'
|
||||||
|
else:
|
||||||
|
CHILD_PTR = POINTER(GEOSStruct * len(self))
|
||||||
|
return GEOSPointer(cast(self._struct.child, CHILD_PTR).contents[index])
|
||||||
|
else:
|
||||||
|
raise GEOSException, 'This GEOSPointer is not a parent'
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""
|
||||||
|
Iterates over the children Geometry pointers, return as GEOSPointer
|
||||||
|
objects to the caller.
|
||||||
|
"""
|
||||||
|
for i in xrange(len(self)):
|
||||||
|
yield self[i]
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"Returns the number of children Geometry pointers (or 0 if no children)."
|
||||||
|
return self._struct.nchild
|
||||||
|
|
||||||
|
def __nonzero__(self):
|
||||||
|
"Returns True when the GEOSPointer is valid."
|
||||||
|
return self.valid
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"Returns the string representation of this GEOSPointer."
|
||||||
|
# First getting the address for the Geometry pointer.
|
||||||
|
if self.valid: geom_addr = self.address
|
||||||
|
else: geom_addr = 0
|
||||||
|
# If there's a coordinate sequence, construct accoringly.
|
||||||
|
if self.coordseq_valid:
|
||||||
|
return 'GEOSPointer(%s, %s)' % (geom_addr, self.coordseq_address)
|
||||||
|
else:
|
||||||
|
return 'GEOSPointer(%s)' % geom_addr
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
#### GEOSPointer Properties ####
|
||||||
|
@property
|
||||||
|
def address(self):
|
||||||
|
"Returns the address of the GEOSPointer (represented as an integer)."
|
||||||
|
return self._struct.geom.contents.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def valid(self):
|
||||||
|
"Tests whether this GEOSPointer is valid."
|
||||||
|
return bool(self._struct.geom)
|
||||||
|
|
||||||
|
#### Parent & Child Properties ####
|
||||||
|
@property
|
||||||
|
def parent(self):
|
||||||
|
"Returns the GEOSPointer for the parent or None."
|
||||||
|
if self.child:
|
||||||
|
return GEOSPointer(self._struct.parent.contents)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child(self):
|
||||||
|
"Returns True if the GEOSPointer has a parent."
|
||||||
|
return bool(self._struct.parent)
|
||||||
|
|
||||||
|
#### Coordinate Sequence routines and properties ####
|
||||||
|
def coordseq(self):
|
||||||
|
"""
|
||||||
|
If the coordinate sequence pointer is NULL or 0, an exception will
|
||||||
|
be raised.
|
||||||
|
"""
|
||||||
|
if self.coordseq_valid: return self.coordseq_address
|
||||||
|
else: raise GEOSException, 'GEOS coordinate sequence pointer invalid (was this geometry or the parent geometry deleted or modified?)'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordseq_address(self):
|
||||||
|
"Returns the address of the related coordinate sequence."
|
||||||
|
return self._struct.cs.contents.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordseq_valid(self):
|
||||||
|
"Returns True if the coordinate sequence address is valid, False otherwise."
|
||||||
|
return bool(self._struct.cs)
|
||||||
|
|
||||||
|
#### GEOSPointer Methods ####
|
||||||
|
def destroy(self):
|
||||||
|
"""
|
||||||
|
Calls GEOSGeom_destroy on the address of this pointer, and nullifies
|
||||||
|
this pointer. Use VERY carefully, as trying to destroy an address that
|
||||||
|
no longer holds a valid GEOS Geometry may crash Python.
|
||||||
|
"""
|
||||||
|
if self.valid:
|
||||||
|
# ONLY on valid geometries.
|
||||||
|
lgeos.GEOSGeom_destroy(self.address)
|
||||||
|
self.nullify()
|
||||||
|
|
||||||
|
def set(self, address):
|
||||||
|
"""
|
||||||
|
Sets the address of this pointer with the given address (represented
|
||||||
|
as an integer). Using 0 or None will set the pointer to NULL.
|
||||||
|
"""
|
||||||
|
if address in (0, None):
|
||||||
|
self._struct.geom = None
|
||||||
|
else:
|
||||||
|
self._struct.geom.contents = c_int(address)
|
||||||
|
|
||||||
|
def set_coordseq(self, address):
|
||||||
|
"""
|
||||||
|
Sets the address of the coordinate sequence associated with
|
||||||
|
this pointer.
|
||||||
|
"""
|
||||||
|
if address in (0, None):
|
||||||
|
self._struct.cs = None
|
||||||
|
else:
|
||||||
|
self._struct.cs.contents = c_int(address)
|
||||||
|
|
||||||
|
def set_children(self, ptr_list):
|
||||||
|
"""
|
||||||
|
Sets children pointers with the given pointer list (of integers).
|
||||||
|
Alternatively, a list of tuples for the geometry and coordinate
|
||||||
|
sequence pointers of the children may be used.
|
||||||
|
"""
|
||||||
|
# The number of children geometries is the number of pointers (or
|
||||||
|
# tuples) passed in via the `ptr_list`.
|
||||||
|
n_child = len(ptr_list)
|
||||||
|
|
||||||
|
# Determining whether coordinate sequences pointers were passed in.
|
||||||
|
if isinstance(ptr_list[0], (tuple, list)):
|
||||||
|
self._child_cs = True
|
||||||
|
else:
|
||||||
|
self._child_cs = False
|
||||||
|
|
||||||
|
# Dynamically creating the C types for the children array (CHILD_ARR),
|
||||||
|
# initializing with the created type, and creating a parent pointer
|
||||||
|
# for the children.
|
||||||
|
CHILD_ARR = GEOSStruct * n_child
|
||||||
|
children = CHILD_ARR()
|
||||||
|
parent = pointer(self._struct)
|
||||||
|
|
||||||
|
# Incrementing through each of the children, and setting the
|
||||||
|
# pointers in the array of GEOSStructs.
|
||||||
|
for i in xrange(n_child):
|
||||||
|
if self._child_cs:
|
||||||
|
geom_ptr, cs_ptr = ptr_list[i]
|
||||||
|
if cs_ptr is not None:
|
||||||
|
children[i].cs.contents = c_int(cs_ptr)
|
||||||
|
else:
|
||||||
|
geom_ptr = ptr_list[i]
|
||||||
|
children[i].geom.contents = c_int(geom_ptr)
|
||||||
|
children[i].parent = parent
|
||||||
|
|
||||||
|
# Casting the CHILD_ARR to the contents of the void pointer, and
|
||||||
|
# setting the number of children within the struct (used by __len__).
|
||||||
|
self._struct.child = cast(pointer(children), c_void_p)
|
||||||
|
self._struct.nchild = c_int(n_child)
|
||||||
|
|
||||||
|
def nullify(self):
|
||||||
|
"""
|
||||||
|
Nullify the geometry and coordinate sequence pointers (sets the
|
||||||
|
pointers to NULL). This does not delete any memory (destroy()
|
||||||
|
handles that), rather, it sets the GEOS pointers to a NULL address,
|
||||||
|
preventing access to deleted objects.
|
||||||
|
"""
|
||||||
|
# Nullifying both the geometry and coordinate sequence pointer.
|
||||||
|
self.set(None)
|
||||||
|
self.set_coordseq(None)
|
||||||
|
|
||||||
|
def summary(self):
|
||||||
|
"""
|
||||||
|
Returns a summary string containing information about the pointer,
|
||||||
|
including the geometry address, any associated coordinate sequence
|
||||||
|
address, and child geometry addresses.
|
||||||
|
"""
|
||||||
|
sum = '%s\n' % str(self)
|
||||||
|
for p1 in self:
|
||||||
|
sum += ' * %s\n' % p1
|
||||||
|
for p2 in p1: sum += ' - %s\n' % p2
|
||||||
|
if bool(self._struct.parent):
|
||||||
|
sum += 'Parent: %s\n' % self.parent
|
||||||
|
return sum
|
||||||
|
|
||||||
|
class NullGEOSPointer(GEOSPointer):
|
||||||
|
"The NullGEOSPointer is always NULL, and cannot be set to anything else."
|
||||||
|
def __init__(self):
|
||||||
|
self._struct = GEOSStruct()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def valid(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordseq_valid(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_coordseq(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_children(self, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
NULL_GEOM = NullGEOSPointer()
|
@ -13,13 +13,13 @@ class GEOSTest(unittest.TestCase):
|
|||||||
def test01a_wkt(self):
|
def test01a_wkt(self):
|
||||||
"Testing WKT output."
|
"Testing WKT output."
|
||||||
for g in wkt_out:
|
for g in wkt_out:
|
||||||
geom = GEOSGeometry(g.wkt)
|
geom = fromstr(g.wkt)
|
||||||
self.assertEqual(g.ewkt, geom.wkt)
|
self.assertEqual(g.ewkt, geom.wkt)
|
||||||
|
|
||||||
def test01b_hex(self):
|
def test01b_hex(self):
|
||||||
"Testing HEX output."
|
"Testing HEX output."
|
||||||
for g in hex_wkt:
|
for g in hex_wkt:
|
||||||
geom = GEOSGeometry(g.wkt)
|
geom = fromstr(g.wkt)
|
||||||
self.assertEqual(g.hex, geom.hex)
|
self.assertEqual(g.hex, geom.hex)
|
||||||
|
|
||||||
def test01c_kml(self):
|
def test01c_kml(self):
|
||||||
@ -34,22 +34,22 @@ class GEOSTest(unittest.TestCase):
|
|||||||
print "\nBEGIN - expecting GEOS_ERROR; safe to ignore.\n"
|
print "\nBEGIN - expecting GEOS_ERROR; safe to ignore.\n"
|
||||||
for err in errors:
|
for err in errors:
|
||||||
if err.hex:
|
if err.hex:
|
||||||
self.assertRaises(GEOSException, GEOSGeometry, err.wkt)
|
self.assertRaises(GEOSException, fromstr, err.wkt)
|
||||||
else:
|
else:
|
||||||
self.assertRaises(GEOSException, GEOSGeometry, err.wkt)
|
self.assertRaises(GEOSException, fromstr, err.wkt)
|
||||||
print "\nEND - expecting GEOS_ERROR; safe to ignore.\n"
|
print "\nEND - expecting GEOS_ERROR; safe to ignore.\n"
|
||||||
|
|
||||||
def test02a_points(self):
|
def test02a_points(self):
|
||||||
"Testing Point objects."
|
"Testing Point objects."
|
||||||
prev = GEOSGeometry('POINT(0 0)')
|
prev = fromstr('POINT(0 0)')
|
||||||
for p in points:
|
for p in points:
|
||||||
# Creating the point from the WKT
|
# Creating the point from the WKT
|
||||||
pnt = GEOSGeometry(p.wkt)
|
pnt = fromstr(p.wkt)
|
||||||
self.assertEqual(pnt.geom_type, 'Point')
|
self.assertEqual(pnt.geom_type, 'Point')
|
||||||
self.assertEqual(pnt.geom_typeid, 0)
|
self.assertEqual(pnt.geom_typeid, 0)
|
||||||
self.assertEqual(p.x, pnt.x)
|
self.assertEqual(p.x, pnt.x)
|
||||||
self.assertEqual(p.y, pnt.y)
|
self.assertEqual(p.y, pnt.y)
|
||||||
self.assertEqual(True, pnt == GEOSGeometry(p.wkt))
|
self.assertEqual(True, pnt == fromstr(p.wkt))
|
||||||
self.assertEqual(False, pnt == prev)
|
self.assertEqual(False, pnt == prev)
|
||||||
|
|
||||||
# Making sure that the point's X, Y components are what we expect
|
# Making sure that the point's X, Y components are what we expect
|
||||||
@ -97,7 +97,7 @@ class GEOSTest(unittest.TestCase):
|
|||||||
def test02b_multipoints(self):
|
def test02b_multipoints(self):
|
||||||
"Testing MultiPoint objects."
|
"Testing MultiPoint objects."
|
||||||
for mp in multipoints:
|
for mp in multipoints:
|
||||||
mpnt = GEOSGeometry(mp.wkt)
|
mpnt = fromstr(mp.wkt)
|
||||||
self.assertEqual(mpnt.geom_type, 'MultiPoint')
|
self.assertEqual(mpnt.geom_type, 'MultiPoint')
|
||||||
self.assertEqual(mpnt.geom_typeid, 4)
|
self.assertEqual(mpnt.geom_typeid, 4)
|
||||||
|
|
||||||
@ -115,9 +115,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test03a_linestring(self):
|
def test03a_linestring(self):
|
||||||
"Testing LineString objects."
|
"Testing LineString objects."
|
||||||
prev = GEOSGeometry('POINT(0 0)')
|
prev = fromstr('POINT(0 0)')
|
||||||
for l in linestrings:
|
for l in linestrings:
|
||||||
ls = GEOSGeometry(l.wkt)
|
ls = fromstr(l.wkt)
|
||||||
self.assertEqual(ls.geom_type, 'LineString')
|
self.assertEqual(ls.geom_type, 'LineString')
|
||||||
self.assertEqual(ls.geom_typeid, 1)
|
self.assertEqual(ls.geom_typeid, 1)
|
||||||
self.assertEqual(ls.empty, False)
|
self.assertEqual(ls.empty, False)
|
||||||
@ -127,7 +127,7 @@ class GEOSTest(unittest.TestCase):
|
|||||||
if hasattr(l, 'tup'):
|
if hasattr(l, 'tup'):
|
||||||
self.assertEqual(l.tup, ls.tuple)
|
self.assertEqual(l.tup, ls.tuple)
|
||||||
|
|
||||||
self.assertEqual(True, ls == GEOSGeometry(l.wkt))
|
self.assertEqual(True, ls == fromstr(l.wkt))
|
||||||
self.assertEqual(False, ls == prev)
|
self.assertEqual(False, ls == prev)
|
||||||
self.assertRaises(GEOSGeometryIndexError, ls.__getitem__, len(ls))
|
self.assertRaises(GEOSGeometryIndexError, ls.__getitem__, len(ls))
|
||||||
prev = ls
|
prev = ls
|
||||||
@ -141,16 +141,16 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test03b_multilinestring(self):
|
def test03b_multilinestring(self):
|
||||||
"Testing MultiLineString objects."
|
"Testing MultiLineString objects."
|
||||||
prev = GEOSGeometry('POINT(0 0)')
|
prev = fromstr('POINT(0 0)')
|
||||||
for l in multilinestrings:
|
for l in multilinestrings:
|
||||||
ml = GEOSGeometry(l.wkt)
|
ml = fromstr(l.wkt)
|
||||||
self.assertEqual(ml.geom_type, 'MultiLineString')
|
self.assertEqual(ml.geom_type, 'MultiLineString')
|
||||||
self.assertEqual(ml.geom_typeid, 5)
|
self.assertEqual(ml.geom_typeid, 5)
|
||||||
|
|
||||||
self.assertAlmostEqual(l.centroid[0], ml.centroid.x, 9)
|
self.assertAlmostEqual(l.centroid[0], ml.centroid.x, 9)
|
||||||
self.assertAlmostEqual(l.centroid[1], ml.centroid.y, 9)
|
self.assertAlmostEqual(l.centroid[1], ml.centroid.y, 9)
|
||||||
|
|
||||||
self.assertEqual(True, ml == GEOSGeometry(l.wkt))
|
self.assertEqual(True, ml == fromstr(l.wkt))
|
||||||
self.assertEqual(False, ml == prev)
|
self.assertEqual(False, ml == prev)
|
||||||
prev = ml
|
prev = ml
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ class GEOSTest(unittest.TestCase):
|
|||||||
def test04_linearring(self):
|
def test04_linearring(self):
|
||||||
"Testing LinearRing objects."
|
"Testing LinearRing objects."
|
||||||
for rr in linearrings:
|
for rr in linearrings:
|
||||||
lr = GEOSGeometry(rr.wkt)
|
lr = fromstr(rr.wkt)
|
||||||
self.assertEqual(lr.geom_type, 'LinearRing')
|
self.assertEqual(lr.geom_type, 'LinearRing')
|
||||||
self.assertEqual(lr.geom_typeid, 2)
|
self.assertEqual(lr.geom_typeid, 2)
|
||||||
self.assertEqual(rr.n_p, len(lr))
|
self.assertEqual(rr.n_p, len(lr))
|
||||||
@ -181,10 +181,10 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test05a_polygons(self):
|
def test05a_polygons(self):
|
||||||
"Testing Polygon objects."
|
"Testing Polygon objects."
|
||||||
prev = GEOSGeometry('POINT(0 0)')
|
prev = fromstr('POINT(0 0)')
|
||||||
for p in polygons:
|
for p in polygons:
|
||||||
# Creating the Polygon, testing its properties.
|
# Creating the Polygon, testing its properties.
|
||||||
poly = GEOSGeometry(p.wkt)
|
poly = fromstr(p.wkt)
|
||||||
self.assertEqual(poly.geom_type, 'Polygon')
|
self.assertEqual(poly.geom_type, 'Polygon')
|
||||||
self.assertEqual(poly.geom_typeid, 3)
|
self.assertEqual(poly.geom_typeid, 3)
|
||||||
self.assertEqual(poly.empty, False)
|
self.assertEqual(poly.empty, False)
|
||||||
@ -199,7 +199,7 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertAlmostEqual(p.centroid[1], poly.centroid.tuple[1], 9)
|
self.assertAlmostEqual(p.centroid[1], poly.centroid.tuple[1], 9)
|
||||||
|
|
||||||
# Testing the geometry equivalence
|
# Testing the geometry equivalence
|
||||||
self.assertEqual(True, poly == GEOSGeometry(p.wkt))
|
self.assertEqual(True, poly == fromstr(p.wkt))
|
||||||
self.assertEqual(False, poly == prev) # Should not be equal to previous geometry
|
self.assertEqual(False, poly == prev) # Should not be equal to previous geometry
|
||||||
self.assertEqual(True, poly != prev)
|
self.assertEqual(True, poly != prev)
|
||||||
|
|
||||||
@ -222,9 +222,8 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertEqual(r.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.__init__, 0, [1, 2, 3])
|
||||||
self.assertRaises(TypeError, Polygon, 'foo')
|
self.assertRaises(TypeError, Polygon.__init__, 'foo')
|
||||||
|
|
||||||
rings = tuple(r.clone() for r in poly)
|
rings = tuple(r.clone() for r in poly)
|
||||||
self.assertEqual(poly, Polygon(rings[0], rings[1:]))
|
self.assertEqual(poly, Polygon(rings[0], rings[1:]))
|
||||||
self.assertEqual(poly.wkt, Polygon(*tuple(r.clone() for r in poly)).wkt)
|
self.assertEqual(poly.wkt, Polygon(*tuple(r.clone() for r in poly)).wkt)
|
||||||
@ -246,9 +245,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
def test05b_multipolygons(self):
|
def test05b_multipolygons(self):
|
||||||
"Testing MultiPolygon objects."
|
"Testing MultiPolygon objects."
|
||||||
print "\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n"
|
print "\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n"
|
||||||
prev = GEOSGeometry('POINT (0 0)')
|
prev = fromstr('POINT (0 0)')
|
||||||
for mp in multipolygons:
|
for mp in multipolygons:
|
||||||
mpoly = GEOSGeometry(mp.wkt)
|
mpoly = fromstr(mp.wkt)
|
||||||
self.assertEqual(mpoly.geom_type, 'MultiPolygon')
|
self.assertEqual(mpoly.geom_type, 'MultiPolygon')
|
||||||
self.assertEqual(mpoly.geom_typeid, 6)
|
self.assertEqual(mpoly.geom_typeid, 6)
|
||||||
self.assertEqual(mp.valid, mpoly.valid)
|
self.assertEqual(mp.valid, mpoly.valid)
|
||||||
@ -266,14 +265,14 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
|
print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
|
||||||
|
|
||||||
def test06_memory_hijinks(self):
|
def test06a_memory_hijinks(self):
|
||||||
"Testing Geometry __del__() in different scenarios"
|
"Testing Geometry __del__() on rings and polygons."
|
||||||
#### Memory issues with rings and polygons
|
#### Memory issues with rings and polygons
|
||||||
|
|
||||||
# These tests are needed to ensure sanity with writable geometries.
|
# These tests are needed to ensure sanity with writable geometries.
|
||||||
|
|
||||||
# Getting a polygon with interior rings, and pulling out the interior rings
|
# Getting a polygon with interior rings, and pulling out the interior rings
|
||||||
poly = GEOSGeometry(polygons[1].wkt)
|
poly = fromstr(polygons[1].wkt)
|
||||||
ring1 = poly[0]
|
ring1 = poly[0]
|
||||||
ring2 = poly[1]
|
ring2 = poly[1]
|
||||||
|
|
||||||
@ -292,6 +291,8 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertRaises(GEOSException, str, ring1)
|
self.assertRaises(GEOSException, str, ring1)
|
||||||
self.assertRaises(GEOSException, str, ring2)
|
self.assertRaises(GEOSException, str, ring2)
|
||||||
|
|
||||||
|
def test06b_memory_hijinks(self):
|
||||||
|
"Testing Geometry __del__() on collections."
|
||||||
#### Memory issues with geometries from Geometry Collections
|
#### Memory issues with geometries from Geometry Collections
|
||||||
mp = fromstr('MULTIPOINT(85 715, 235 1400, 4620 1711)')
|
mp = fromstr('MULTIPOINT(85 715, 235 1400, 4620 1711)')
|
||||||
|
|
||||||
@ -315,16 +316,17 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Should raise GEOSException when trying to get geometries from the multipoint
|
# Should raise GEOSException when trying to get geometries from the multipoint
|
||||||
# after it has been deleted.
|
# after it has been deleted.
|
||||||
|
parr1 = [ptr for ptr in mp._ptr]
|
||||||
|
parr2 = [p._ptr for p in mp]
|
||||||
del mp
|
del mp
|
||||||
|
|
||||||
for p in pts:
|
for p in pts:
|
||||||
self.assertRaises(GEOSException, str, p) # tests p's geometry pointer
|
self.assertRaises(GEOSException, str, p) # tests p's geometry pointer
|
||||||
self.assertRaises(GEOSException, p.get_coords) # tests p's coordseq pointer
|
self.assertRaises(GEOSException, p.get_coords) # tests p's coordseq pointer
|
||||||
|
|
||||||
# Now doing this with a GeometryCollection
|
# Now doing this with a GeometryCollection
|
||||||
polywkt = polygons[3].wkt # a 'real life' polygon.
|
poly = fromstr(polygons[3].wkt) # a 'real life' polygon.
|
||||||
lrwkt = linearrings[0].wkt # a 'real life' linear ring
|
linring = fromstr(linearrings[0].wkt) # a 'real life' linear ring
|
||||||
poly = fromstr(polywkt)
|
|
||||||
linring = fromstr(lrwkt)
|
|
||||||
|
|
||||||
# Pulling out the shell and cloning our initial geometries for later comparison.
|
# Pulling out the shell and cloning our initial geometries for later comparison.
|
||||||
shell = poly.shell
|
shell = poly.shell
|
||||||
@ -338,33 +340,42 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertRaises(GEOSException, str, shell)
|
self.assertRaises(GEOSException, str, shell)
|
||||||
self.assertRaises(GEOSException, str, linring)
|
self.assertRaises(GEOSException, str, linring)
|
||||||
|
|
||||||
r1 = gc[1] # pulling out the ring
|
# Deep-indexing delete should be 'harmless.'
|
||||||
|
tmpr1 = gc[0][0]
|
||||||
|
del tmpr1
|
||||||
|
|
||||||
|
r1 = gc[0][0] # pulling out shell from polygon -- deep indexing
|
||||||
|
r2 = gc[1] # pulling out the ring
|
||||||
pnt = gc[2] # pulling the point from the geometry collection
|
pnt = gc[2] # pulling the point from the geometry collection
|
||||||
|
|
||||||
# Now lets create a MultiPolygon from the geometry collection components
|
# Now lets create a MultiPolygon from the geometry collection components
|
||||||
mpoly = MultiPolygon(gc[0], Polygon(gc[1]))
|
mpoly = MultiPolygon(gc[0], Polygon(gc[1]))
|
||||||
self.assertEqual(polyc.wkt, mpoly[0].wkt)
|
self.assertEqual(polyc.wkt, mpoly[0].wkt)
|
||||||
|
self.assertEqual(linringc.wkt, mpoly[1][0].wkt) # deep-indexed ring
|
||||||
|
|
||||||
# Should no longer be able to access the geometry collection directly
|
# Should no longer be able to access the geometry collection directly
|
||||||
self.assertRaises(GEOSException, len, gc)
|
self.assertRaises(GEOSException, len, gc)
|
||||||
|
|
||||||
# BUT, should still be able to access the Point we obtained earlier, but
|
# BUT, should still be able to access the Point we obtained earlier,
|
||||||
# not the linear ring (since it is now part of the MultiPolygon.
|
# however, not the linear ring (since it is now part of the
|
||||||
|
# MultiPolygon).
|
||||||
self.assertEqual(5, pnt.x)
|
self.assertEqual(5, pnt.x)
|
||||||
self.assertEqual(23, pnt.y)
|
self.assertEqual(23, pnt.y)
|
||||||
|
for tmpr in [r1, r2]:
|
||||||
# __len__ is called on the coordinate sequence pointer -- make sure its nullified as well
|
# __len__ is called on the coordinate sequence pointer --
|
||||||
self.assertRaises(GEOSException, len, r1)
|
# making sure its nullified as well
|
||||||
self.assertRaises(GEOSException, str, r1)
|
self.assertRaises(GEOSException, len, tmpr)
|
||||||
|
self.assertRaises(GEOSException, str, tmpr)
|
||||||
|
|
||||||
# Can't access point after deletion of parent geometry.
|
# Can't access point after deletion of parent geometry.
|
||||||
del gc
|
del gc
|
||||||
self.assertRaises(GEOSException, str, pnt)
|
self.assertRaises(GEOSException, str, pnt)
|
||||||
|
|
||||||
# Cleaning up.
|
# Cleaning up.
|
||||||
del polyc
|
del polyc, mpoly
|
||||||
del mpoly
|
|
||||||
|
|
||||||
|
def test06c_memory_hijinks(self):
|
||||||
|
"Testing __init__ using other Geometries as parameters."
|
||||||
#### Memory issues with creating geometries from coordinate sequences within other geometries
|
#### Memory issues with creating geometries from coordinate sequences within other geometries
|
||||||
|
|
||||||
# Creating the initial polygon from the following tuples, and then pulling out
|
# Creating the initial polygon from the following tuples, and then pulling out
|
||||||
@ -388,18 +399,42 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
# BUT, the second hole is still accessible.
|
# BUT, the second hole is still accessible.
|
||||||
self.assertEqual(itup2, hole2.tuple)
|
self.assertEqual(itup2, hole2.tuple)
|
||||||
|
|
||||||
# Deleting the first polygon, and ensuring that
|
# Deleting the first polygon, and ensuring that
|
||||||
# the second hole is now gone for good.
|
# the second hole is now gone for good.
|
||||||
del poly1
|
del poly1, poly2
|
||||||
self.assertRaises(GEOSException, str, hole2)
|
self.assertRaises(GEOSException, str, hole2)
|
||||||
|
|
||||||
|
#### Testing creating Geometries w/"deep-indexed" rings ####
|
||||||
|
mpoly = MultiPolygon(Polygon(LinearRing(ext_tup), LinearRing(itup1), LinearRing(itup2)),
|
||||||
|
Polygon(LinearRing(itup1)))
|
||||||
|
|
||||||
|
r0 = mpoly[0][1] # itup1, left alone
|
||||||
|
r1 = mpoly[0][0] # ext_tup
|
||||||
|
r2 = mpoly[1][0] # itup1
|
||||||
|
r3 = mpoly[0][2] # itup2
|
||||||
|
|
||||||
|
# Using the rings of the multipolygon to create a new Polygon, should
|
||||||
|
# no longer be able to access the MultiPolygon or the ring objects
|
||||||
|
# used in initialization.
|
||||||
|
p1 = Polygon(r1, r2, r3)
|
||||||
|
for g in [mpoly, r1, r2, r3]:
|
||||||
|
self.assertRaises(GEOSException, len, g)
|
||||||
|
self.assertRaises(GEOSException, str, g)
|
||||||
|
|
||||||
|
# However, the middle ring of the first Polygon was not used.
|
||||||
|
self.assertEqual(r0.tuple, itup1)
|
||||||
|
self.assertEqual(r0, p1[1])
|
||||||
|
|
||||||
|
# This deletes the leftover ring (or whenever mpoly is garbage collected)
|
||||||
|
del mpoly
|
||||||
|
|
||||||
def test08_coord_seq(self):
|
def test08_coord_seq(self):
|
||||||
"Testing Coordinate Sequence objects."
|
"Testing Coordinate Sequence objects."
|
||||||
for p in polygons:
|
for p in polygons:
|
||||||
if p.ext_ring_cs:
|
if p.ext_ring_cs:
|
||||||
# Constructing the polygon and getting the coordinate sequence
|
# Constructing the polygon and getting the coordinate sequence
|
||||||
poly = GEOSGeometry(p.wkt)
|
poly = fromstr(p.wkt)
|
||||||
cs = poly.exterior_ring.coord_seq
|
cs = poly.exterior_ring.coord_seq
|
||||||
|
|
||||||
self.assertEqual(p.ext_ring_cs, cs.tuple) # done in the Polygon test too.
|
self.assertEqual(p.ext_ring_cs, cs.tuple) # done in the Polygon test too.
|
||||||
@ -423,13 +458,13 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test09_relate_pattern(self):
|
def test09_relate_pattern(self):
|
||||||
"Testing relate() and relate_pattern()."
|
"Testing relate() and relate_pattern()."
|
||||||
g = GEOSGeometry('POINT (0 0)')
|
g = fromstr('POINT (0 0)')
|
||||||
self.assertRaises(GEOSException, g.relate_pattern, 0, 'invalid pattern, yo')
|
self.assertRaises(GEOSException, g.relate_pattern, 0, 'invalid pattern, yo')
|
||||||
|
|
||||||
for i in xrange(len(relate_geoms)):
|
for i in xrange(len(relate_geoms)):
|
||||||
g_tup = relate_geoms[i]
|
g_tup = relate_geoms[i]
|
||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = fromstr(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = fromstr(g_tup[1].wkt)
|
||||||
pat = g_tup[2]
|
pat = g_tup[2]
|
||||||
result = g_tup[3]
|
result = g_tup[3]
|
||||||
self.assertEqual(result, a.relate_pattern(b, pat))
|
self.assertEqual(result, a.relate_pattern(b, pat))
|
||||||
@ -439,9 +474,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
"Testing intersects() and intersection()."
|
"Testing intersects() and intersection()."
|
||||||
for i in xrange(len(topology_geoms)):
|
for i in xrange(len(topology_geoms)):
|
||||||
g_tup = topology_geoms[i]
|
g_tup = topology_geoms[i]
|
||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = fromstr(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = fromstr(g_tup[1].wkt)
|
||||||
i1 = GEOSGeometry(intersect_geoms[i].wkt)
|
i1 = fromstr(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)
|
||||||
@ -453,9 +488,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
"Testing union()."
|
"Testing union()."
|
||||||
for i in xrange(len(topology_geoms)):
|
for i in xrange(len(topology_geoms)):
|
||||||
g_tup = topology_geoms[i]
|
g_tup = topology_geoms[i]
|
||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = fromstr(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = fromstr(g_tup[1].wkt)
|
||||||
u1 = GEOSGeometry(union_geoms[i].wkt)
|
u1 = fromstr(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) # __or__ is union operator
|
self.assertEqual(u1, a | b) # __or__ is union operator
|
||||||
@ -466,9 +501,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
"Testing difference()."
|
"Testing difference()."
|
||||||
for i in xrange(len(topology_geoms)):
|
for i in xrange(len(topology_geoms)):
|
||||||
g_tup = topology_geoms[i]
|
g_tup = topology_geoms[i]
|
||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = fromstr(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = fromstr(g_tup[1].wkt)
|
||||||
d1 = GEOSGeometry(diff_geoms[i].wkt)
|
d1 = fromstr(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) # __sub__ is difference operator
|
self.assertEqual(d1, a - b) # __sub__ is difference operator
|
||||||
@ -479,9 +514,9 @@ class GEOSTest(unittest.TestCase):
|
|||||||
"Testing sym_difference()."
|
"Testing sym_difference()."
|
||||||
for i in xrange(len(topology_geoms)):
|
for i in xrange(len(topology_geoms)):
|
||||||
g_tup = topology_geoms[i]
|
g_tup = topology_geoms[i]
|
||||||
a = GEOSGeometry(g_tup[0].wkt)
|
a = fromstr(g_tup[0].wkt)
|
||||||
b = GEOSGeometry(g_tup[1].wkt)
|
b = fromstr(g_tup[1].wkt)
|
||||||
d1 = GEOSGeometry(sdiff_geoms[i].wkt)
|
d1 = fromstr(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) # __xor__ is symmetric difference operator
|
self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator
|
||||||
@ -492,10 +527,10 @@ class GEOSTest(unittest.TestCase):
|
|||||||
"Testing buffer()."
|
"Testing buffer()."
|
||||||
for i in xrange(len(buffer_geoms)):
|
for i in xrange(len(buffer_geoms)):
|
||||||
g_tup = buffer_geoms[i]
|
g_tup = buffer_geoms[i]
|
||||||
g = GEOSGeometry(g_tup[0].wkt)
|
g = fromstr(g_tup[0].wkt)
|
||||||
|
|
||||||
# The buffer we expect
|
# The buffer we expect
|
||||||
exp_buf = GEOSGeometry(g_tup[1].wkt)
|
exp_buf = fromstr(g_tup[1].wkt)
|
||||||
|
|
||||||
# Can't use a floating-point for the number of quadsegs.
|
# Can't use a floating-point for the number of quadsegs.
|
||||||
self.assertRaises(TypeError, g.buffer, g_tup[2], float(g_tup[3]))
|
self.assertRaises(TypeError, g.buffer, g_tup[2], float(g_tup[3]))
|
||||||
@ -575,8 +610,8 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertNotEqual(pnt, mp[i])
|
self.assertNotEqual(pnt, mp[i])
|
||||||
del mp
|
del mp
|
||||||
|
|
||||||
# Multipolygons involve much more memory management because each
|
# MultiPolygons involve much more memory management because each
|
||||||
# polygon w/in the collection has its own rings.
|
# Polygon w/in the collection has its own rings.
|
||||||
for tg in multipolygons:
|
for tg in multipolygons:
|
||||||
mpoly = fromstr(tg.wkt)
|
mpoly = fromstr(tg.wkt)
|
||||||
for i in xrange(len(mpoly)):
|
for i in xrange(len(mpoly)):
|
||||||
@ -592,12 +627,17 @@ class GEOSTest(unittest.TestCase):
|
|||||||
self.assertRaises(GEOSException, str, tmp)
|
self.assertRaises(GEOSException, str, tmp)
|
||||||
self.assertEqual(mpoly[i], new)
|
self.assertEqual(mpoly[i], new)
|
||||||
self.assertNotEqual(poly, mpoly[i])
|
self.assertNotEqual(poly, mpoly[i])
|
||||||
del mpoly
|
|
||||||
|
# Extreme (!!) __setitem__
|
||||||
|
mpoly[0][0][0] = (3.14, 2.71)
|
||||||
|
self.assertEqual((3.14, 2.71), mpoly[0][0][0])
|
||||||
|
# Doing it more slowly..
|
||||||
|
self.assertEqual((3.14, 2.71), mpoly[0].shell[0])
|
||||||
|
|
||||||
|
del mpoly
|
||||||
|
|
||||||
def test17_threed(self):
|
def test17_threed(self):
|
||||||
"Testing three-dimensional geometries."
|
"Testing three-dimensional geometries."
|
||||||
|
|
||||||
# Testing a 3D Point
|
# Testing a 3D Point
|
||||||
pnt = Point(2, 3, 8)
|
pnt = Point(2, 3, 8)
|
||||||
self.assertEqual((2.,3.,8.), pnt.coords)
|
self.assertEqual((2.,3.,8.), pnt.coords)
|
||||||
@ -632,7 +672,6 @@ class GEOSTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test19_length(self):
|
def test19_length(self):
|
||||||
"Testing the length property."
|
"Testing the length property."
|
||||||
|
|
||||||
# Points have 0 length.
|
# Points have 0 length.
|
||||||
pnt = Point(0, 0)
|
pnt = Point(0, 0)
|
||||||
self.assertEqual(0.0, pnt.length)
|
self.assertEqual(0.0, pnt.length)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user