Changeset 5742
- Timestamp:
- 07/21/07 12:39:09 (1 year ago)
- Files:
-
- django/branches/gis/django/contrib/gis/geos/base.py (modified) (18 diffs)
- django/branches/gis/django/contrib/gis/geos/collections.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/geos/coordseq.py (modified) (9 diffs)
- django/branches/gis/django/contrib/gis/geos/geometries.py (modified) (19 diffs)
- django/branches/gis/django/contrib/gis/geos/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/geos/libgeos.py (modified) (5 diffs)
- django/branches/gis/django/contrib/gis/tests/geometries.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/tests/test_geos.py (modified) (16 diffs)
- django/branches/gis/django/contrib/gis/utils/LayerMapping.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/geos/base.py
r5655 r5742 1 # Trying not to pollute the namespace. 1 """ 2 This module contains the 'base' GEOSGeometry object -- all GEOS geometries 3 inherit from this object. 4 """ 5 6 # ctypes and types dependencies. 2 7 from ctypes import \ 3 8 byref, string_at, create_string_buffer, pointer, \ 4 9 c_char_p, c_double, c_int, c_size_t 5 from types import StringType, IntType, FloatType , TupleType, ListType6 7 # GettingGEOS-related dependencies.10 from types import StringType, IntType, FloatType 11 12 # Python and GEOS-related dependencies. 8 13 import re 9 14 from warnings import warn … … 11 16 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 12 17 from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 13 14 if HAS_NUMPY: 15 from numpy import ndarray, array 16 17 # For recognizing HEXEWKB. 18 hex_regex = re.compile(r'^[0-9A-Fa-f]+') 18 if HAS_NUMPY: from numpy import ndarray, array 19 20 # Regular expression for recognizing HEXEWKB. 21 hex_regex = re.compile(r'^[0-9A-Fa-f]+$') 19 22 20 23 class GEOSGeometry(object): … … 22 25 23 26 #### Python 'magic' routines #### 24 def __init__(self, geo_input, input_type=False, child=False):27 def __init__(self, geo_input, input_type=False, parent=False): 25 28 """The constructor for GEOS geometry objects. May take the following 26 29 strings as inputs, WKT ("wkt"), HEXEWKB ("hex", PostGIS-specific canonical form). … … 28 31 The `input_type` keyword has been deprecated -- geometry type is now auto-detected. 29 32 30 The ` child` keyword is for internal use only, and indicates to the garbage collector31 not to delete this geometry ifit was spawned from a parent (e.g., the exterior32 ring from a polygon). 33 The `parent` keyword is for internal use only, and indicates to the garbage collector 34 not to delete this geometry because it was spawned from a parent (e.g., the exterior 35 ring from a polygon). Its value is the GEOSPointer of the parent geometry. 33 36 """ 34 37 … … 56 59 57 60 if bool(g): 58 # If we have a GEOSPointer object, just set the '_ptr' attribute with g61 # If we have a GEOSPointer object, just set the '_ptr' attribute with input 59 62 if isinstance(g, GEOSPointer): self._ptr = g 60 else: self._ptr.set(g) # Otherwise, set the address63 else: self._ptr.set(g) # Otherwise, set with the address 61 64 else: 62 65 raise GEOSException, 'Could not initialize GEOS Geometry with given input.' 63 66 64 # Setting the ' child' flag -- when the object is labeled with this flag65 # it will not be destroyed by __del__(). This is used for child geometries from67 # Setting the 'parent' flag -- when the object is labeled with this flag 68 # it will not be destroyed by __del__(). This is used for child geometries spawned from 66 69 # parent geometries (e.g., LinearRings from a Polygon, Points from a MultiPoint, etc.). 67 self._child = child 68 70 if isinstance(parent, GEOSPointer): 71 self._parent = parent 72 else: 73 self._parent = GEOSPointer(0) 74 69 75 # Setting the class type (e.g., 'Point', 'Polygon', etc.) 70 76 self.__class__ = GEOS_CLASSES[self.geom_type] 71 77 78 # Getting the coordinate sequence for the geometry (will be None on geometries that 79 # do not have coordinate sequences) 80 self._get_cs() 81 72 82 # Extra setup needed for Geometries that may be parents. 73 if isinstance(self, GeometryCollection): self._geoms = {} 74 if isinstance(self, Polygon): self._rings = {} 83 if isinstance(self, (Polygon, GeometryCollection)): self._populate() 75 84 76 85 def __del__(self): 77 "Destroys this geometry -- only if the pointer is valid and this is not a child geometry." 78 #print 'Deleting %s (child=%s, valid=%s)' % (self.geom_type, self._child, self._ptr.valid) 79 if self._ptr.valid and not self._child: lgeos.GEOSGeom_destroy(self._ptr()) 86 "Destroys this geometry -- only if the pointer is valid and whether or not it belongs to a parent." 87 #print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 88 # Only calling destroy on valid pointers not spawned from a parent 89 if self._ptr.valid and not self._parent: lgeos.GEOSGeom_destroy(self._ptr()) 80 90 81 91 def __str__(self): … … 83 93 return self.wkt 84 94 95 def __repr__(self): 96 return '<%s object>' % self.geom_type 97 98 # Comparison operators 85 99 def __eq__(self, other): 86 100 "Equivalence testing." 87 101 return self.equals(other) 88 102 103 def __ne__(self, other): 104 "The not equals operator." 105 return not self.equals(other) 106 107 ### Geometry set-like operations ### 108 # Thanks to Sean Gillies for inspiration: 109 # http://lists.gispython.org/pipermail/community/2007-July/001034.html 110 # g = g1 | g2 111 def __or__(self, other): 112 "Returns the union of this Geometry and the other." 113 return self.union(other) 114 115 # g = g1 & g2 116 def __and__(self, other): 117 "Returns the intersection of this Geometry and the other." 118 return self.intersection(other) 119 120 # g = g1 - g2 121 def __sub__(self, other): 122 "Return the difference this Geometry and the other." 123 return self.difference(other) 124 125 # g = g1 ^ g2 126 def __xor__(self, other): 127 "Return the symmetric difference of this Geometry and the other." 128 return self.sym_difference(other) 129 89 130 #### Coordinate Sequence Routines #### 90 def _cache_cs(self): 91 "Caches the coordinate sequence for this Geometry." 92 if not hasattr(self, '_cs'): 93 # Only these geometries are allowed to have coordinate sequences. 94 if self.geom_type in ('LineString', 'LinearRing', 'Point'): 95 self._cs = GEOSCoordSeq(GEOSPointer(lgeos.GEOSGeom_getCoordSeq(self._ptr())), self.hasz) 96 else: 97 self._cs = None 131 @property 132 def has_cs(self): 133 "Returns True if this Geometry has a coordinate sequence, False if not." 134 # Only these geometries are allowed to have coordinate sequences. 135 if isinstance(self, (Point, LineString, LinearRing)): 136 return True 137 else: 138 return False 139 140 def _get_cs(self): 141 "Gets the coordinate sequence for this Geometry." 142 if self.has_cs: 143 self._ptr.set(lgeos.GEOSGeom_getCoordSeq(self._ptr()), coordseq=True) 144 self._cs = GEOSCoordSeq(self._ptr, self.hasz) 145 else: 146 self._cs = None 98 147 99 148 @property 100 149 def coord_seq(self): 101 "Returns the coordinate sequence for the geometry." 102 # Getting the coordinate sequence for the geometry 103 self._cache_cs() 104 105 # Returning a GEOSCoordSeq wrapped around the pointer. 150 "Returns the coordinate sequence for this Geometry." 106 151 return self._cs 107 152 … … 109 154 @property 110 155 def geom_type(self): 111 "Returns a string representing the geometry type, e.g. 'Polygon'"156 "Returns a string representing the Geometry type, e.g. 'Polygon'" 112 157 return string_at(lgeos.GEOSGeomType(self._ptr())) 113 158 114 159 @property 115 160 def geom_typeid(self): 116 "Returns an integer representing the geometry type."161 "Returns an integer representing the Geometry type." 117 162 return lgeos.GEOSGeomTypeId(self._ptr()) 118 163 119 164 @property 120 165 def num_geom(self): 121 "Returns the number of geometries in the geometry."166 "Returns the number of geometries in the Geometry." 122 167 n = lgeos.GEOSGetNumGeometries(self._ptr()) 123 168 if n == -1: raise GEOSException, 'Error getting number of geometries.' … … 126 171 @property 127 172 def num_coords(self): 128 "Returns the number of coordinates in the geometry."173 "Returns the number of coordinates in the Geometry." 129 174 n = lgeos.GEOSGetNumCoordinates(self._ptr()) 130 175 if n == -1: raise GEOSException, 'Error getting number of coordinates.' … … 133 178 @property 134 179 def num_points(self): 135 "Returns the number points, or coordinates, in the geometry."180 "Returns the number points, or coordinates, in the Geometry." 136 181 return self.num_coords 137 182 … … 146 191 if status == -1: raise GEOSException, 'failed to normalize geometry' 147 192 193 ## Internal for GEOS unary & binary predicate functions ## 148 194 def _unary_predicate(self, func): 149 195 "Returns the result, or raises an exception for the given unary predicate function." … … 165 211 @property 166 212 def empty(self): 167 "Returns a boolean indicating whether the set of points in this geometry are empty."213 "Returns a boolean indicating whether the set of points in this Geometry are empty." 168 214 return self._unary_predicate(lgeos.GEOSisEmpty) 169 215 170 216 @property 171 217 def valid(self): 172 "This property tests the validity of this geometry."218 "This property tests the validity of this Geometry." 173 219 return self._unary_predicate(lgeos.GEOSisValid) 174 220 … … 191 237 def relate_pattern(self, other, pattern): 192 238 """Returns true if the elements in the DE-9IM intersection matrix for 193 the two Geometr ys match the elements in pattern."""239 the two Geometries match the elements in pattern.""" 194 240 if len(pattern) > 9: 195 241 raise GEOSException, 'invalid intersection matrix pattern' … … 197 243 198 244 def disjoint(self, other): 199 "Returns true if the DE-9IM intersection matrix for the two Geometr ys is FF*FF****."245 "Returns true if the DE-9IM intersection matrix for the two Geometries is FF*FF****." 200 246 return self._binary_predicate(lgeos.GEOSDisjoint, other) 201 247 202 248 def touches(self, other): 203 "Returns true if the DE-9IM intersection matrix for the two Geometr ys is FT*******, F**T***** or F***T****."249 "Returns true if the DE-9IM intersection matrix for the two Geometries is FT*******, F**T***** or F***T****." 204 250 return self._binary_predicate(lgeos.GEOSTouches, other) 205 251 … … 209 255 210 256 def crosses(self, other): 211 """Returns true if the DE-9IM intersection matrix for the two Geometr ys is T*T****** (for a point and a curve,257 """Returns true if the DE-9IM intersection matrix for the two Geometries is T*T****** (for a point and a curve, 212 258 a point and an area or a line and an area) 0******** (for two curves).""" 213 259 return self._binary_predicate(lgeos.GEOSCrosses, other) 214 260 215 261 def within(self, other): 216 "Returns true if the DE-9IM intersection matrix for the two Geometr ys is T*F**F***."262 "Returns true if the DE-9IM intersection matrix for the two Geometries is T*F**F***." 217 263 return self._binary_predicate(lgeos.GEOSWithin, other) 218 264 … … 222 268 223 269 def overlaps(self, other): 224 """Returns true if the DE-9IM intersection matrix for the two Geometr ys is T*T***T** (for two points270 """Returns true if the DE-9IM intersection matrix for the two Geometries is T*T***T** (for two points 225 271 or two surfaces) 1*T***T** (for two curves).""" 226 272 return self._binary_predicate(lgeos.GEOSOverlaps, other) 227 273 228 274 def equals(self, other): 229 "Returns true if the DE-9IM intersection matrix for the two Geometr ys is T*F**FFF*."275 "Returns true if the DE-9IM intersection matrix for the two Geometries is T*F**FFF*." 230 276 return self._binary_predicate(lgeos.GEOSEquals, other) 231 277 232 278 def equals_exact(self, other, tolerance=0): 233 "Returns true if the two Geometr ys are exactly equal, up to a specified tolerance."279 "Returns true if the two Geometries are exactly equal, up to a specified tolerance." 234 280 tol = c_double(tolerance) 235 281 return self._binary_predicate(lgeos.GEOSEqualsExact, other, tol) … … 291 337 @property 292 338 def centroid(self): 293 """The centroid is equal to the centroid of the set of component Geometr ys339 """The centroid is equal to the centroid of the set of component Geometries 294 340 of highest dimension (since the lower-dimension geometries contribute zero 295 341 "weight" to the centroid).""" … … 345 391 "Clones this Geometry." 346 392 return GEOSGeometry(lgeos.GEOSGeom_clone(self._ptr())) 347 393 348 394 # Class mapping dictionary 349 395 from django.contrib.gis.geos.geometries import Point, Polygon, LineString, LinearRing … … 353 399 'LineString' : LineString, 354 400 'LinearRing' : LinearRing, 355 'GeometryCollection' : GeometryCollection,356 401 'MultiPoint' : MultiPoint, 357 402 'MultiLineString' : MultiLineString, 358 403 'MultiPolygon' : MultiPolygon, 404 'GeometryCollection' : GeometryCollection, 359 405 } django/branches/gis/django/contrib/gis/geos/collections.py
r5656 r5742 3 3 GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon 4 4 """ 5 from ctypes import c_int 6 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer 5 from ctypes import c_int, c_uint, byref, cast 6 from types import TupleType, ListType 7 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, init_from_geom, get_pointer_arr, GEOM_PTR 7 8 from django.contrib.gis.geos.base import GEOSGeometry 8 from django.contrib.gis.geos.error import GEOSException 9 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 10 from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon 11 12 def init_from_poly(poly): 13 "Internal routine used for initializing Geometry Collections from Polygons." 14 # Constructing a new Polygon to take control of the rings. 15 p = Polygon(*tuple(ring for ring in poly)) 16 17 # If this Polygon came from a GeometryCollection, it is a child 18 # and the parent geometry pointer is nullified. 19 if poly._parent: poly._parent.nullify() 20 21 # Nullifying the polygon pointer 22 poly._ptr.nullify() 23 24 # Returning the address of the new Polygon. 25 return p._ptr() 9 26 10 27 class GeometryCollection(GEOSGeometry): 28 _allowed = (Point, LineString, LinearRing, Polygon) 29 _typeid = 7 30 31 def __init__(self, *args): 32 self._ptr = GEOSPointer(0) # Initially NULL 33 self._geoms = {} 34 self._parent = False 35 36 if not args: 37 raise TypeError, 'Must provide at least one LinearRing to initialize Polygon.' 38 39 if len(args) == 1: # If only one geometry provided or a list of geometries is provided 40 if isinstance(args[0], (TupleType, ListType)): 41 init_geoms = args[0] 42 else: 43 init_geoms = args 44 else: 45 init_geoms = args 46 47 # Ensuring that only the permitted geometries are allowed in this collection 48 if False in [isinstance(geom, self._allowed) for geom in init_geoms]: 49 raise TypeError, 'Invalid Geometry type encountered in the arguments.' 50 51 # Creating the geometry pointer array 52 ngeom = len(init_geoms) 53 geoms = get_pointer_arr(ngeom) 54 55 # Incrementing through each input geometry. 56 for i in xrange(ngeom): 57 if isinstance(init_geoms[i], Polygon): 58 # Special care is taken when importing from Polygons 59 geoms[i] = cast(init_from_poly(init_geoms[i]), GEOM_PTR) 60 else: 61 geoms[i] = cast(init_from_geom(init_geoms[i]), GEOM_PTR) 62 63 # Calling the parent class, using the pointer returned from GEOS createCollection() 64 super(GeometryCollection, self).__init__(lgeos.GEOSGeom_createCollection(c_int(self._typeid), byref(geoms), c_uint(ngeom))) 11 65 12 66 def __del__(self): 13 "Override the GEOSGeometry delete routine to safely take care of any spawned geometries." 14 # Nullifying the pointers to internal geometries, preventing any attempted future access 15 for k in self._geoms: self._geoms[k].nullify() 16 super(GeometryCollection, self).__del__() # Calling the parent __del__() method. 17 67 "Overloaded deletion method for Geometry Collections." 68 #print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 69 # If this geometry is still valid, it hasn't been modified by others. 70 if self._ptr.valid: 71 # Nullifying pointers to internal geometries, preventing any attempted future access. 72 for k in self._geoms: self._geoms[k].nullify() 73 super(GeometryCollection, self).__del__() 74 else: 75 # Internal memory has become part of other Geometry objects, must delete the 76 # internal objects which are still valid individually, since calling destructor 77 # on entire geometry will result in an attempted deletion of NULL pointers for 78 # the missing components. 79 for k in self._geoms: 80 if self._geoms[k].valid: 81 lgeos.GEOSGeom_destroy(self._geoms[k].address) 82 self._geoms[k].nullify() 83 18 84 def __getitem__(self, index): 19 85 "For indexing on the multiple geometries." 86 # Checking the index and returning the corresponding GEOS geometry. 20 87 self._checkindex(index) 21 22 # Setting an entry in the _geoms dictionary for the requested geometry. 23 if not index in self._geoms: 24 self._geoms[index] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(index))) 25 26 # Cloning the GEOS Geometry first, before returning it. 27 return GEOSGeometry(self._geoms[index], child=True) 88 return GEOSGeometry(self._geoms[index], parent=self._ptr) 28 89 29 90 def __iter__(self): 30 91 "For iteration on the multiple geometries." 31 for i in xrange( self.__len__()):92 for i in xrange(len(self)): 32 93 yield self.__getitem__(i) 33 94 … … 41 102 raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index) 42 103 104 def _populate(self): 105 "Populates the internal child geometry dictionary." 106 self._geoms = {} 107 for i in xrange(self.num_geom): 108 self._geoms[i] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(i))) 109 110 43 111 # MultiPoint, MultiLineString, and MultiPolygon class definitions. 44 class MultiPoint(GeometryCollection): pass 45 class MultiLineString(GeometryCollection): pass 46 class MultiPolygon(GeometryCollection): pass 112 class MultiPoint(GeometryCollection): 113 _allowed = Point 114 _typeid = 4 115 class MultiLineString(GeometryCollection): 116 _allowed = (LineString, LinearRing) 117 _typeid = 5 118 class MultiPolygon(GeometryCollection): 119 _allowed = Polygon 120 _typeid = 6 django/branches/gis/django/contrib/gis/geos/coordseq.py
r5655 r5742 1 from django.contrib.gis.geos.libgeos import lgeos 1 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY 2 2 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 3 3 from ctypes import c_double, c_int, c_uint, byref 4 from types import ListType, TupleType 5 if HAS_NUMPY: from numpy import ndarray 4 6 5 7 """ … … 15 17 def __init__(self, ptr, z=False): 16 18 "Initializes from a GEOS pointer." 19 if not isinstance(ptr, GEOSPointer): 20 raise TypeError, 'Coordinate sequence should initialize with a GEOSPointer.' 17 21 self._ptr = ptr 18 22 self._z = z … … 40 44 def __setitem__(self, index, value): 41 45 "Can use the index [] operator to set coordinate sequence at an index." 46 # Checking the input value 47 if isinstance(value, (ListType, TupleType)): 48 pass 49 elif HAS_NUMPY and isinstance(value, ndarray): 50 pass 51 else: 52 raise TypeError, 'Must set coordinate with a sequence (list, tuple, or numpy array).' 53 # Checking the dims of the input 42 54 if self.dims == 3 and self._z: 43 55 n_args = 3 … … 48 60 if len(value) != n_args: 49 61 raise TypeError, 'Dimension of value does not match.' 62 # Setting the X, Y, Z 50 63 self.setX(index, value[0]) 51 64 self.setY(index, value[1]) … … 74 87 idx = c_uint(index) 75 88 76 # 'd' is the value of the point, passed in by reference89 # 'd' is the value of the ordinate, passed in by reference 77 90 d = c_double() 78 status = lgeos.GEOSCoordSeq_getOrdinate(self._ptr (), idx, dim, byref(d))91 status = lgeos.GEOSCoordSeq_getOrdinate(self._ptr.coordseq(), idx, dim, byref(d)) 79 92 if status == 0: 80 raise GEOSException, 'could not retrieve % th ordinate at index: %s' % (str(dimension), str(index))93 raise GEOSException, 'could not retrieve %sth ordinate at index: %s' % (dimension, index) 81 94 return d.value 82 95 … … 91 104 92 105 # Setting the ordinate 93 status = lgeos.GEOSCoordSeq_setOrdinate(self._ptr (), idx, dim, c_double(value))106 status = lgeos.GEOSCoordSeq_setOrdinate(self._ptr.coordseq(), idx, dim, c_double(value)) 94 107 if status == 0: 95 108 raise GEOSException, 'Could not set the ordinate for (dim, index): (%d, %d)' % (dimension, index) … … 124 137 "Returns the size of this coordinate sequence." 125 138 n = c_uint(0) 126 status = lgeos.GEOSCoordSeq_getSize(self._ptr (), byref(n))139 status = lgeos.GEOSCoordSeq_getSize(self._ptr.coordseq(), byref(n)) 127 140 if status == 0: 128 141 raise GEOSException, 'Could not get CoordSeq size.' … … 133 146 "Returns the dimensions of this coordinate sequence." 134 147 n = c_uint(0) 135 status = lgeos.GEOSCoordSeq_getDimensions(self._ptr (), byref(n))148 status = lgeos.GEOSCoordSeq_getDimensions(self._ptr.coordseq(), byref(n)) 136 149 if status == 0: 137 150 raise GEOSException, 'Could not get CoordSeq dimensions.' … … 147 160 def clone(self): 148 161 "Clones this coordinate sequence." 149 pass162 return GEOSCoordSeq(GEOSPointer(0, lgeos.GEOSCoordSeq_clone(self._ptr.coordseq())), self.hasz) 150 163 151 164 @property django/branches/gis/django/contrib/gis/geos/geometries.py
r5656 r5742 1 from ctypes import c_double, c_int, c_uint 1 """ 2 This module houses the Point, LineString, LinearRing, and Polygon OGC 3 geometry classes. All geometry classes in this module inherit from 4 GEOSGeometry. 5 """ 6 7 from ctypes import c_double, c_int, c_uint, byref, cast 2 8 from types import FloatType, IntType, ListType, TupleType 3 9 from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 4 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, HAS_NUMPY10 from django.contrib.gis.geos.libgeos import lgeos, GEOSPointer, get_pointer_arr, init_from_geom, GEOM_PTR, HAS_NUMPY 5 11 from django.contrib.gis.geos.base import GEOSGeometry 6 from django.contrib.gis.geos.error import GEOSException 7 8 if HAS_NUMPY: 9 from numpy import ndarray, array 12 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 13 if HAS_NUMPY: from numpy import ndarray, array 10 14 11 15 class Point(GEOSGeometry): … … 17 21 >>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters 18 22 """ 23 24 self._ptr = GEOSPointer(0) # Initially NULL 19 25 20 26 if isinstance(x, (TupleType, ListType)): … … 53 59 super(Point, self).__init__(lgeos.GEOSGeom_createPoint(cs)) 54 60 61 def __len__(self): 62 "Returns the number of dimensions for this Point (either 2 or 3)." 63 if self.hasz: return 3 64 else: return 2 65 55 66 def _getOrdinate(self, dim, idx): 56 67 "The coordinate sequence getOrdinate() wrapper." 57 self._cache_cs()58 68 return self._cs.getOrdinate(dim, idx) 59 69 60 70 def _setOrdinate(self, dim, idx, value): 61 71 "The coordinate sequence setOrdinate() wrapper." 62 self._cache_cs()63 72 self._cs.setOrdinate(dim, idx, value) 64 73 … … 101 110 def get_tuple(self): 102 111 "Returns a tuple of the point." 103 self._cache_cs()104 112 return self._cs.tuple 105 113 106 def set_tuple(self ):107 "Sets the tuple for this point object."108 pass109 114 def set_tuple(self, tup): 115 "Sets the coordinates of the point with the given tuple." 116 self._cs[0] = tup 117 110 118 # The tuple property 111 119 tuple = property(get_tuple, set_tuple) … … 114 122 115 123 #### Python 'magic' routines #### 116 def __init__(self, coords, ring=False): 117 """Initializes on the given sequence, may take lists, tuples, or NumPy arrays 118 of X,Y pairs.""" 124 def __init__(self, *args, **kwargs): 125 """Initializes on the given sequence -- may take lists, tuples, NumPy arrays 126 of X,Y pairs, or Point objects. If Point objects are used, ownership is 127 _not_ transferred to the LineString object. 128 129 Examples: 130 ls = LineString((1, 1), (2, 2)) 131 ls = LineString([(1, 1), (2, 2)]) 132 ls = LineString(array([(1, 1), (2, 2)])) 133 ls = LineString(Point(1, 1), Point(2, 2)) 134 """ 135 self._ptr = GEOSPointer(0) # Initially NULL 136 137 # If only one argument was provided, then set the coords array appropriately 138 if len(args) == 1: coords = args[0] 139 else: coords = args 119 140 120 141 if isinstance(coords, (TupleType, ListType)): 142 # Getting the number of coords and the number of dimensions -- which 143 # must stay the same, e.g., no LineString((1, 2), (1, 2, 3)). 121 144 ncoords = len(coords) 122 first = True 123 for coord in coords: 124 if not isinstance(coord, (TupleType, ListType)): 145 if coords: ndim = len(coords[0]) 146 else: raise TypeError, 'Cannot initialize on empty sequence.' 147 self._checkdim(ndim) 148 # Incrementing through each of the coordinates and verifying 149 for i in xrange(1, ncoords): 150 if not isinstance(coords[i], (TupleType, ListType, Point)): 125 151 raise TypeError, 'each coordinate should be a sequence (list or tuple)' 126 if first: 127 ndim = len(coord) 128 self._checkdim(ndim) 129 first = False 130 else: 131 if len(coord) != ndim: raise TypeError, 'Dimension mismatch.' 152 if len(coords[i]) != ndim: raise TypeError, 'Dimension mismatch.' 132 153 numpy_coords = False 133 154 elif HAS_NUMPY and isinstance(coords, ndarray): 134 shape = coords.shape 155 shape = coords.shape # Using numpy's shape. 135 156 if len(shape) != 2: raise TypeError, 'Too many dimensions.' 136 157 self._checkdim(shape[1]) … … 142 163 143 164 # Creating the coordinate sequence 144 cs = GEOSCoordSeq(GEOSPointer( create_cs(c_uint(ncoords), c_uint(ndim))))165 cs = GEOSCoordSeq(GEOSPointer(0, create_cs(c_uint(ncoords), c_uint(ndim)))) 145 166 146 167 # Setting each point in the coordinate sequence 147 168 for i in xrange(ncoords): 148 169 if numpy_coords: cs[i] = coords[i,:] 170 elif isinstance(coords[i], Point): cs[i] = coords[i].tuple 149 171 else: cs[i] = coords[i] 150 172 151 173 # Getting the initialization function 152 if ring:174 if kwargs.get('ring', False): 153 175 func = lgeos.GEOSGeom_createLinearRing 154 176 else: … … 156 178 157 179 # Calling the base geometry initialization with the returned pointer from the function. 158 super(LineString, self).__init__(func(cs._ptr ()))180 super(LineString, self).__init__(func(cs._ptr.coordseq())) 159 181 160 182 def __getitem__(self, index): 161 183 "Gets the point at the specified index." 162 self._cache_cs()163 184 return self._cs[index] 164 185 165 186 def __setitem__(self, index, value): 166 187 "Sets the point at the specified index, e.g., line_str[0] = (1, 2)." 167 self._cache_cs()168 188 self._cs[index] = value 169 189 … … 171 191 "Allows iteration over this LineString." 172 192 for i in xrange(self.__len__()): 173 yield self.__getitem__(i ndex)193 yield self.__getitem__(i) 174 194 175 195 def __len__(self): 176 196 "Returns the number of points in this LineString." 177 self._cache_cs()178 197 return len(self._cs) 179 198 … … 185 204 def tuple(self): 186 205 "Returns a tuple version of the geometry from the coordinate sequence." 187 self._cache_cs()188 206 return self._cs.tuple 189 207 … … 191 209 """Internal routine that returns a sequence (list) corresponding with 192 210 the given function. Will return a numpy array if possible.""" 193 lst = [func(i) for i in xrange( self.__len__())] # constructing the list, using the function211 lst = [func(i) for i in xrange(len(self))] # constructing the list, using the function 194 212 if HAS_NUMPY: return array(lst) # ARRRR! 195 213 else: return lst … … 198 216 def array(self): 199 217 "Returns a numpy array for the LineString." 200 self._cache_cs()201 218 return self._listarr(self._cs.__getitem__) 202 219 … … 204 221 def x(self): 205 222 "Returns a list or numpy array of the X variable." 206 self._cache_cs()207 223 return self._listarr(self._cs.getX) 208 224 … … 210 226 def y(self): 211 227 "Returns a list or numpy array of the Y variable." 212 self._cache_cs()213 228 return self._listarr(self._cs.getY) 214 229 … … 216 231 def z(self): 217 232 "Returns a list or numpy array of the Z variable." 218 self._cache_cs()219 233 if not self.hasz: return None 220 234 else: return self._listarr(self._cs.getZ) … … 222 236 # LinearRings are LineStrings used within Polygons. 223 237 class LinearRing(LineString): 224 def __init__(self, coords):238 def __init__(self, *args): 225 239 "Overriding the initialization function to set the ring keyword." 226 super(LinearRing, self).__init__(coords, ring=True) 240 kwargs = {'ring' : True} 241 super(LinearRing, self).__init__(*args, **kwargs) 227 242 228 243 class Polygon(GEOSGeometry): 229 244 245 def __init__(self, *args): 246 """Initializes on an exterior ring and a sequence of holes (both instances of LinearRings. 247 All LinearRing instances used for creation will become owned by this Polygon. 248 249 Examples, where shell, hole1, and hole2 are valid LinearRing geometries: 250 poly = Polygon(shell, hole1, hole2) 251 poly = Polygon(shell, (hole1, hole2)) 252 """ 253 self._ptr = GEOSPointer(0) # Initially NULL 254 self._rings = {} 255 if not args: 256 raise TypeError, 'Must provide at list one LinearRing instance to initialize Polygon.' 257 258 # Getting the ext_ring and init_holes parameters from the argument list 259 ext_ring = args[0] 260 init_holes = args[1:] 261 if len(init_holes) == 1 and isinstance(init_holes[0], (TupleType, ListType)): 262 init_holes = init_holes[0] 263 264 # Ensuring the exterior ring parameter is a LinearRing object 265 if not isinstance(ext_ring, LinearRing): 266 raise TypeError, 'First argument for Polygon initialization must be a LinearRing.' 267 268 # Making sure all of the holes are LinearRing objects 269 if False in [isinstance(hole, LinearRing) for hole in init_holes]: 270 raise TypeError, 'Holes parameter must be a sequence of LinearRings.' 271 272 # Getting the holes 273 nholes = len(init_holes) 274 holes = get_pointer_arr(nholes) 275 for i in xrange(nholes): 276 # Casting to the Geometry Pointer type 277 holes[i] = cast(init_from_geom(init_holes[i]), GEOM_PTR) 278 279 # Getting the shell pointer address, 280 shell = init_from_geom(ext_ring) 281 282 # Calling with the GEOS createPolygon factory. 283 super(Polygon, self).__init__(lgeos.GEOSGeom_createPolygon(shell, byref(holes), c_uint(nholes))) 284 230 285 def __del__(self): 231 "Override the GEOSGeometry delete routine to safely take care of any spawned rings." 232 # Nullifying the pointers to internal rings, preventing any attempted future access 233 for k in self._rings: self._rings[k].nullify() 234 super(Polygon, self).__del__() # Calling the parent __del__() method. 235 286 "Overloaded deletion method for Polygons." 287 #print 'Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._parent, self._ptr.valid) 288 # If this geometry is still valid, it hasn't been modified by others. 289 if self._ptr.valid: 290 # Nulling the pointers to internal rings, preventing any attempted future access 291 for k in self._rings: self._rings[k].nullify() 292 super(Polygon, self).__del__() 293 else: 294 # Internal memory has become part of other objects; must delete the 295 # internal objects which are still valid individually, since calling 296 # destructor on entire geometry will result in an attempted deletion 297 # of NULL pointers for the missing components. 298 for k in self._rings: 299 if self._rings[k].valid: 300 lgeos.GEOSGeom_destroy(self._rings[k].address) 301 self._rings[k].nullify() 302 236 303 def __getitem__(self, index): 237 304 """Returns the ring at the specified index. The first index, 0, will always … … 248 315 def __iter__(self): 249 316 "Iterates over each ring in the polygon." 250 for i in xrange( self.__len__()):317 for i in xrange(len(self)): 251 318 yield self.__getitem__(i) 252 319 … … 255 322 return self.num_interior_rings + 1 256 323 324 def _populate(self): 325 "Populates the internal rings dictionary." 326 # Getting the exterior ring first for the 0th index. 327 self._rings = {0 : GEOSPointer(lgeos.GEOSGetExteriorRing(self._ptr()))} 328 329 # Getting the interior rings. 330 for i in xrange(self.num_interior_rings): 331 self._rings[i+1] = GEOSPointer(lgeos.GEOSGetInteriorRingN(self._ptr(), c_int(i))) 332 257 333 def get_interior_ring(self, ring_i): 258 334 """Gets the interior ring at the specified index, … … 263 339 raise IndexError, 'ring index out of range' 264 340 265 # Placing the ring in internal rings dictionary. 266 idx = ring_i+1 # the index for the polygon is +1 because of the exterior ring 267 if not idx in self._rings: 268 self._rings[idx] = GEOSPointer(lgeos.GEOSGetInteriorRingN(self._ptr(), c_int(ring_i))) 269 270 # Returning the ring at the given index. 271 return GEOSGeometry(self._rings[idx], child=True) 341 # Returning the ring from the internal ring dictionary (have to 342 # add one to the index) 343 return GEOSGeometry(self._rings[ring_i+1], parent=self._ptr) 272 344 273 345 #### Polygon Properties #### … … 283 355 else: return n 284 356 285 @property 286 def exterior_ring(self): 357 def get_ext_ring(self): 287 358 "Gets the exterior ring of the Polygon." 288 # Returns exterior ring 289 self._rings[0] = GEOSPointer(lgeos.GEOSGetExteriorRing((self._ptr()))) 290 return GEOSGeometry(self._rings[0], child=True) 291 292 @property 293 def shell(self): 294 "Gets the shell (exterior ring) of the Polygon." 295 return self.exterior_ring 359 return GEOSGeometry(self._rings[0], parent=self._ptr) 360 361 def set_ext_ring(self): 362 "Sets the exterior ring of the Polygon." 363 # Sets the exterior ring 364 raise NotImplementedError 365 366 # properties for the exterior ring/shell 367 exterior_ring = property(get_ext_ring) 368 shell = property(get_ext_ring) 296 369 297 370 @property django/branches/gis/django/contrib/gis/geos/__init__.py
r5655 r5742 31 31 32 32 from base import GEOSGeometry 33 from geometries import Point, LineString, LinearRing, HAS_NUMPY 34 from error import GEOSException 33 from geometries import Point, LineString, LinearRing, Polygon, HAS_NUMPY 34 from collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon 35 from error import GEOSException, GEOSGeometryIndexError
