Changeset 6653
- Timestamp:
- 11/05/07 18:39:36 (8 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/geos/base.py (modified) (19 diffs)
- django/branches/gis/django/contrib/gis/geos/collections.py (modified) (4 diffs)
- django/branches/gis/django/contrib/gis/geos/coordseq.py (modified) (11 diffs)
- django/branches/gis/django/contrib/gis/geos/error.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/geos/geometries.py (modified) (20 diffs)
- django/branches/gis/django/contrib/gis/geos/__init__.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/geos/libgeos.py (modified) (3 diffs)
- django/branches/gis/django/contrib/gis/geos/pointer.py (deleted)
- django/branches/gis/django/contrib/gis/geos/prototypes (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/coordseq.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/errcheck.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/geom.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/__init__.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/misc.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/predicates.py (added)
- django/branches/gis/django/contrib/gis/geos/prototypes/topology.py (added)
- django/branches/gis/django/contrib/gis/tests/test_geos.py (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/geos/base.py
r6596 r6653 1 1 """ 2 This module contains the 'base' GEOSGeometry object -- all GEOS geometries3 inherit from this object.2 This module contains the 'base' GEOSGeometry object -- all GEOS Geometries 3 inherit from this object. 4 4 """ 5 # ctypes and types dependencies. 6 from ctypes import \ 7 byref, string_at, create_string_buffer, pointer, \ 8 c_char_p, c_double, c_int, c_size_t 5 # Python, ctypes and types dependencies. 6 import re 7 from ctypes import addressof, byref, c_double, c_size_t 9 8 from types import StringType, UnicodeType, IntType, FloatType, BufferType 10 9 11 # Python and GEOS-related dependencies. 12 import re 13 from django.contrib.gis.geos.coordseq import GEOSCoordSeq, create_cs 10 # GEOS-related dependencies. 11 from django.contrib.gis.geos.coordseq import GEOSCoordSeq 14 12 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 15 from django.contrib.gis.geos.libgeos import lgeos, HAS_NUMPY 16 from django.contrib.gis.geos.pointer import GEOSPointer, NULL_GEOM 13 from django.contrib.gis.geos.libgeos import GEOM_PTR 14 15 # All other functions in this module come from the ctypes 16 # prototypes module -- which handles all interaction with 17 # the underlying GEOS library. 18 from django.contrib.gis.geos.prototypes import * 17 19 18 20 # Trying to import GDAL libraries, if available. Have to place in … … 25 27 26 28 # Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure 27 # to prevent potentially malicious input from reaching the underlying C28 # library. Not a substitute for good web security programming practices.29 # to prevent potentially malicious input from reaching the underlying C 30 # library. Not a substitute for good web security programming practices. 29 31 hex_regex = re.compile(r'^[0-9A-F]+$', re.I) 30 32 wkt_regex = re.compile(r'^(POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+$', re.I) … … 32 34 class GEOSGeometry(object): 33 35 "A class that, generally, encapsulates a GEOS geometry." 34 35 # Initially, all geometries use a NULL pointer.36 _ptr = N ULL_GEOM37 36 37 # Initially, the geometry pointer is NULL 38 _ptr = None 39 38 40 #### Python 'magic' routines #### 39 41 def __init__(self, geo_input, srid=None): 40 42 """ 41 43 The base constructor for GEOS geometry objects, and may take the 42 following inputs:44 following inputs: 43 45 44 46 * string: WKT … … 47 49 48 50 The `srid` keyword is used to specify the Source Reference Identifier 49 (SRID) number for this Geometry. If not set, the SRID will be None.51 (SRID) number for this Geometry. If not set, the SRID will be None. 50 52 """ 51 53 if isinstance(geo_input, UnicodeType): … … 55 57 if hex_regex.match(geo_input): 56 58 # If the regex matches, the geometry is in HEX form. 57 sz = c_size_t(len(geo_input)) 58 buf = create_string_buffer(geo_input) 59 g = lgeos.GEOSGeomFromHEX_buf(buf, sz) 59 g = from_hex(geo_input, len(geo_input)) 60 60 elif wkt_regex.match(geo_input): 61 61 # Otherwise, the geometry is in WKT form. 62 g = lgeos.GEOSGeomFromWKT(c_char_p(geo_input))62 g = from_wkt(geo_input) 63 63 else: 64 raise GEOSException('String or unicode input unrecognized as WKT or HEXEWKB.') 65 elif isinstance(geo_input, (IntType, GEOSPointer)): 66 # When the input is either a memory address (an integer), or a 67 # GEOSPointer object. 64 raise ValueError('String or unicode input unrecognized as WKT or HEXEWKB.') 65 elif isinstance(geo_input, GEOM_PTR): 66 # When the input is a pointer to a geomtry (GEOM_PTR). 68 67 g = geo_input 69 68 elif isinstance(geo_input, BufferType): 70 69 # When the input is a buffer (WKB). 71 70 wkb_input = str(geo_input) 72 sz = c_size_t(len(wkb_input)) 73 g = lgeos.GEOSGeomFromWKB_buf(c_char_p(wkb_input), sz) 71 g = from_wkb(wkb_input, len(wkb_input)) 74 72 else: 75 73 # Invalid geometry type. 76 raise TypeError , 'Improper geometry input type: %s' % str(type(geo_input))74 raise TypeError('Improper geometry input type: %s' % str(type(geo_input))) 77 75 78 76 if bool(g): 79 77 # Setting the pointer object with a valid pointer. 80 self._ptr = GEOSPointer(g)81 else: 82 raise GEOSException , 'Could not initialize GEOS Geometry with given input.'78 self._ptr = g 79 else: 80 raise GEOSException('Could not initialize GEOS Geometry with given input.') 83 81 84 82 # Setting the SRID, if given. 85 83 if srid and isinstance(srid, int): self.srid = srid 86 84 87 # Setting the class type (e.g., 'Point', 'Polygon', etc.)88 self.__class__ = GEOS_CLASSES[self.geom_type ]85 # Setting the class type (e.g., Point, Polygon, etc.) 86 self.__class__ = GEOS_CLASSES[self.geom_typeid] 89 87 90 88 # Setting the coordinate sequence for the geometry (will be None on … … 92 90 self._set_cs() 93 91 94 # _populate() needs to be called for parent Geometries.95 if isinstance(self, (Polygon, GeometryCollection)): self._populate()96 97 92 def __del__(self): 98 93 """ 99 94 Destroys this Geometry; in other words, frees the memory used by the 100 GEOS C++ object -- but only if the pointer is not a child Geometry 101 (e.g., don't delete the LinearRings spawned from a Polygon). 102 """ 103 #print 'base: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid) 104 if not self._ptr.child: self._ptr.destroy() 95 GEOS C++ object. 96 """ 97 if self._ptr: destroy_geom(self._ptr) 105 98 106 99 def __str__(self): … … 109 102 110 103 def __repr__(self): 111 return '<%s object>' % self.geom_type 104 "Short-hand representation because WKT may be very large." 105 return '<%s object at %s>' % (self.geom_type, hex(addressof(self._ptr))) 112 106 113 107 # Comparison operators … … 166 160 return self.sym_difference(other) 167 161 168 #### Internal GEOSPointer-related routines. ####169 def _nullify(self):170 """171 Returns the address of this Geometry, and nullifies any related pointers.172 This function is called if this Geometry is used in the initialization173 of another Geometry.174 """175 # First getting the memory address of the geometry.176 address = self._ptr()177 178 # If the geometry is a child geometry, then the parent geometry pointer is179 # nullified.180 if self._ptr.child:181 p = self._ptr.parent182 # If we have a grandchild (a LinearRing from a MultiPolygon or183 # GeometryCollection), then nullify the collection as well.184 if p.child: p.parent.nullify()185 p.nullify()186 187 # Nullifying the geometry pointer188 self._ptr.nullify()189 190 return address191 192 def _reassign(self, new_geom):193 "Reassigns the internal pointer to that of the new Geometry."194 # Only can re-assign when given a pointer or a geometry.195 if not isinstance(new_geom, (GEOSPointer, GEOSGeometry)):196 raise TypeError, 'cannot reassign geometry on given type: %s' % type(new_geom)197 gtype = new_geom.geom_type198 199 # Re-assigning the internal GEOSPointer to the new geometry, nullifying200 # the new Geometry in the process.201 if isinstance(new_geom, GEOSPointer): self._ptr = new_geom202 else: self._ptr = GEOSPointer(new_geom._nullify())203 204 # The new geometry class may be different from the original, so setting205 # the __class__ and populating the internal geometry or ring dictionary.206 self.__class__ = GEOS_CLASSES[gtype]207 if isinstance(self, (Polygon, GeometryCollection)): self._populate()208 209 162 #### Coordinate Sequence Routines #### 210 163 @property … … 220 173 "Sets the coordinate sequence for this Geometry." 221 174 if self.has_cs: 222 if not self._ptr.coordseq_valid: 223 self._ptr.set_coordseq(lgeos.GEOSGeom_getCoordSeq(self._ptr())) 224 self._cs = GEOSCoordSeq(self._ptr, self.hasz) 175 self._cs = GEOSCoordSeq(get_cs(self._ptr), self.hasz) 225 176 else: 226 177 self._cs = None … … 228 179 @property 229 180 def coord_seq(self): 230 "Returns the coordinate sequence for this Geometry."231 return self._cs 181 "Returns a clone of the coordinate sequence for this Geometry." 182 return self._cs.clone() 232 183 233 184 #### Geometry Info #### … … 235 186 def geom_type(self): 236 187 "Returns a string representing the Geometry type, e.g. 'Polygon'" 237 return string_at(lgeos.GEOSGeomType(self._ptr()))188 return geos_type(self._ptr) 238 189 239 190 @property 240 191 def geom_typeid(self): 241 192 "Returns an integer representing the Geometry type." 242 return lgeos.GEOSGeomTypeId(self._ptr())193 return geos_typeid(self._ptr) 243 194 244 195 @property 245 196 def num_geom(self): 246 197 "Returns the number of geometries in the Geometry." 247 n = lgeos.GEOSGetNumGeometries(self._ptr()) 248 if n == -1: raise GEOSException, 'Error getting number of geometries.' 249 else: return n 198 return get_num_geoms(self._ptr) 250 199 251 200 @property 252 201 def num_coords(self): 253 202 "Returns the number of coordinates in the Geometry." 254 n = lgeos.GEOSGetNumCoordinates(self._ptr()) 255 if n == -1: raise GEOSException, 'Error getting number of coordinates.' 256 else: return n 203 return get_num_coords(self._ptr) 257 204 258 205 @property … … 264 211 def dims(self): 265 212 "Returns the dimension of this Geometry (0=point, 1=line, 2=surface)." 266 return lgeos.GEOSGeom_getDimensions(self._ptr())213 return get_dims(self._ptr) 267 214 268 215 def normalize(self): 269 216 "Converts this Geometry to normal form (or canonical form)." 270 status = lgeos.GEOSNormalize(self._ptr()) 271 if status == -1: raise GEOSException, 'failed to normalize geometry' 272 273 ## Internal for GEOS unary & binary predicate functions ## 274 def _unary_predicate(self, func): 275 """ 276 Returns the result, or raises an exception for the given unary predicate 277 function. 278 """ 279 val = func(self._ptr()) 280 if val == 0: return False 281 elif val == 1: return True 282 else: raise GEOSException, '%s: exception occurred.' % func.__name__ 283 284 def _binary_predicate(self, func, other, *args): 285 """ 286 Returns the result, or raises an exception for the given binary 287 predicate function. 288 """ 289 if not isinstance(other, GEOSGeometry): 290 raise TypeError, 'Binary predicate operation ("%s") requires another GEOSGeometry instance.' % func.__name__ 291 val = func(self._ptr(), other._ptr(), *args) 292 if val == 0: return False 293 elif val == 1: return True 294 else: raise GEOSException, '%s: exception occurred.' % func.__name__ 217 return geos_normalize(self._ptr) 295 218 296 219 #### Unary predicates #### … … 299 222 """ 300 223 Returns a boolean indicating whether the set of points in this Geometry 301 are empty. 302 """ 303 return self._unary_predicate(lgeos.GEOSisEmpty) 224 are empty. 225 """ 226 return geos_isempty(self._ptr) 227 228 @property 229 def hasz(self): 230 "Returns whether the geometry has a 3D dimension." 231 return geos_hasz(self._ptr) 232 233 @property 234 def ring(self): 235 "Returns whether or not the geometry is a ring." 236 return geos_isring(self._ptr) 237 238 @property 239 def simple(self): 240 "Returns false if the Geometry not simple." 241 return geos_issimple(self._ptr) 304 242 305 243 @property 306 244 def valid(self): 307 245 "This property tests the validity of this Geometry." 308 return self._unary_predicate(lgeos.GEOSisValid) 309 310 @property 311 def simple(self): 312 "Returns false if the Geometry not simple." 313 return self._unary_predicate(lgeos.GEOSisSimple) 314 315 @property 316 def ring(self): 317 "Returns whether or not the geometry is a ring." 318 return self._unary_predicate(lgeos.GEOSisRing) 319 320 @property 321 def hasz(self): 322 "Returns whether the geometry has a 3D dimension." 323 return self._unary_predicate(lgeos.GEOSHasZ) 246 return geos_isvalid(self._ptr) 324 247 325 248 #### Binary predicates. #### 326 def relate_pattern(self, other, pattern): 327 """ 328 Returns true if the elements in the DE-9IM intersection matrix for the 329 two Geometries match the elements in pattern. 330 """ 331 if len(pattern) > 9: 332 raise GEOSException, 'invalid intersection matrix pattern' 333 return self._binary_predicate(lgeos.GEOSRelatePattern, other, c_char_p(pattern)) 249 def contains(self, other): 250 "Returns true if other.within(this) returns true." 251 return geos_contains(self._ptr, other._ptr) 252 253 def crosses(self, other): 254 """ 255 Returns true if the DE-9IM intersection matrix for the two Geometries 256 is T*T****** (for a point and a curve,a point and an area or a line and 257 an area) 0******** (for two curves). 258 """ 259 return geos_crosses(self._ptr, other._ptr) 334 260 335 261 def disjoint(self, other): 336 262 """ 263 Returns true if the DE-9IM intersection matrix for the two Geometries 264 is FF*FF****. 265 """ 266 return geos_disjoint(self._ptr, other._ptr) 267 268 def equals(self, other): 269 """ 337 270 Returns true if the DE-9IM intersection matrix for the two Geometries 338 is FF*FF****.339 """ 340 return self._binary_predicate(lgeos.GEOSDisjoint, other)341 342 def touches(self, other):343 """ 344 Returns true if the DE-9IM intersection matrix for the two Geometries345 is FT*******, F**T***** or F***T****.346 """ 347 return self._binary_predicate(lgeos.GEOSTouches, other)271 is T*F**FFF*. 272 """ 273 return geos_equals(self._ptr, other._ptr) 274 275 def equals_exact(self, other, tolerance=0): 276 """ 277 Returns true if the two Geometries are exactly equal, up to a 278 specified tolerance. 279 """ 280 return geos_equalsexact(self._ptr, other._ptr, float(tolerance)) 348 281 349 282 def intersects(self, other): 350 283 "Returns true if disjoint returns false." 351 return self._binary_predicate(lgeos.GEOSIntersects, other)352 353 def crosses(self, other):284 return geos_intersects(self._ptr, other._ptr) 285 286 def overlaps(self, other): 354 287 """ 355 288 Returns true if the DE-9IM intersection matrix for the two Geometries 356 is T*T****** (for a point and a curve,a point and an area or a line and 357 an area) 0******** (for two curves). 358 """ 359 return self._binary_predicate(lgeos.GEOSCrosses, other) 289 is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves). 290 """ 291 return geos_overlaps(self._ptr, other._ptr) 292 293 def relate_pattern(self, other, pattern): 294 """ 295 Returns true if the elements in the DE-9IM intersection matrix for the 296 two Geometries match the elements in pattern. 297 """ 298 if not isinstance(pattern, StringType) or len(pattern) > 9: 299 raise GEOSException('invalid intersection matrix pattern') 300 return geos_relatepattern(self._ptr, other._ptr, pattern) 301 302 def touches(self, other): 303 """ 304 Returns true if the DE-9IM intersection matrix for the two Geometries 305 is FT*******, F**T***** or F***T****. 306 """ 307 return geos_touches(self._ptr, other._ptr) 360 308 361 309 def within(self, other): 362 310 """ 363 Returns true if the DE-9IM intersection matrix for the two Geometries 364 is T*F**F***. 365 """ 366 return self._binary_predicate(lgeos.GEOSWithin, other) 367 368 def contains(self, other): 369 "Returns true if other.within(this) returns true." 370 return self._binary_predicate(lgeos.GEOSContains, other) 371 372 def overlaps(self, other): 373 """ 374 Returns true if the DE-9IM intersection matrix for the two Geometries 375 is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves). 376 """ 377 return self._binary_predicate(lgeos.GEOSOverlaps, other) 378 379 def equals(self, other): 380 """ 381 Returns true if the DE-9IM intersection matrix for the two Geometries 382 is T*F**FFF*. 383 """ 384 return self._binary_predicate(lgeos.GEOSEquals, other) 385 386 def equals_exact(self, other, tolerance=0): 387 """ 388 Returns true if the two Geometries are exactly equal, up to a 389 specified tolerance. 390 """ 391 return self._binary_predicate(lgeos.GEOSEqualsExact, other, 392 c_double(tolerance)) 311 Returns true if the DE-9IM intersection matrix for the two Geometries 312 is T*F**F***. 313 """ 314 return geos_within(self._ptr, other._ptr) 393 315 394 316 #### SRID Routines #### 395 317 def get_srid(self): 396 318 "Gets the SRID for the geometry, returns None if no SRID is set." 397 s = lgeos.GEOSGetSRID(self._ptr())319 s = geos_get_srid(self._ptr) 398 320 if s == 0: return None 399 321 else: return s … … 401 323 def set_srid(self, srid): 402 324 "Sets the SRID for the geometry." 403 lgeos.GEOSSetSRID(self._ptr(), c_int(srid))325 geos_set_srid(self._ptr, srid) 404 326 srid = property(get_srid, set_srid) 405 327 … … 408 330 def wkt(self): 409 331 "Returns the WKT (Well-Known Text) of the Geometry." 410 return string_at(lgeos.GEOSGeomToWKT(self._ptr()))332 return to_wkt(self._ptr) 411 333 412 334 @property … … 419 341 # A possible faster, all-python, implementation: 420 342 # str(self.wkb).encode('hex') 421 sz = c_size_t() 422 h = lgeos.GEOSGeomToHEX_buf(self._ptr(), byref(sz)) 423 return string_at(h, sz.value) 343 return to_hex(self._ptr, byref(c_size_t())) 424 344 425 345 @property 426 346 def wkb(self): 427 347 "Returns the WKB of the Geometry as a buffer." 428 sz = c_size_t() 429 h = lgeos.GEOSGeomToWKB_buf(self._ptr(), byref(sz)) 430 return buffer(string_at(h, sz.value)) 348 bin = to_wkb(self._ptr, byref(c_size_t())) 349 return buffer(bin) 431 350 432 351 @property … … 456 375 return None 457 376 377 @property 378 def crs(self): 379 "Alias for `srs` property." 380 return self.srs 381 458 382 #### Topology Routines #### 459 def _unary_topology(self, func, *args): 460 """ 461 Returns a GEOSGeometry for the given unary (takes only one Geomtery 462 as a paramter) topological operation. 463 """ 464 return GEOSGeometry(func(self._ptr(), *args), srid=self.srid) 465 466 def _binary_topology(self, func, other, *args): 467 """ 468 Returns a GEOSGeometry for the given binary (takes two Geometries 469 as parameters) topological operation. 470 """ 471 return GEOSGeometry(func(self._ptr(), other._ptr(), *args), srid=self.srid) 383 def _topology(self, gptr): 384 "Helper routine to return Geometry from the given pointer." 385 return GEOSGeometry(gptr, srid=self.srid) 386 387 @property 388 def boundary(self): 389 "Returns the boundary as a newly allocated Geometry object." 390 return self._topology(geos_boundary(self._ptr)) 472 391 473 392 def buffer(self, width, quadsegs=8): 474 393 """ 475 394 Returns a geometry that represents all points whose distance from this 476 Geometry is less than or equal to distance. Calculations are in the 477 Spatial Reference System of this Geometry. The optional third parameter sets 478 the number of segment used to approximate a quarter circle (defaults to 8). 479 (Text from PostGIS documentation at ch. 6.1.3) 480 """ 481 if not isinstance(width, (FloatType, IntType)): 482 raise TypeError, 'width parameter must be a float' 483 if not isinstance(quadsegs, IntType): 484 raise TypeError, 'quadsegs parameter must be an integer' 485 return self._unary_topology(lgeos.GEOSBuffer, c_double(width), c_int(quadsegs)) 395 Geometry is less than or equal to distance. Calculations are in the 396 Spatial Reference System of this Geometry. The optional third parameter sets 397 the number of segment used to approximate a quarter circle (defaults to 8). 398 (Text from PostGIS documentation at ch. 6.1.3) 399 """ 400 return self._topology(geos_buffer(self._ptr, width, quadsegs)) 401 402 @property 403 def centroid(self): 404 """ 405 The centroid is equal to the centroid of the set of component Geometries 406 of highest dimension (since the lower-dimension geometries contribute zero 407 "weight" to the centroid). 408 """ 409 return self._topology(geos_centroid(self._ptr)) 410 411 @property 412 def convex_hull(self): 413 """ 414 Returns the smallest convex Polygon that contains all the points 415 in the Geometry. 416 """ 417 return self._topology(geos_convexhull(self._ptr)) 418 419 def difference(self, other): 420 """ 421 Returns a Geometry representing the points making up this Geometry 422 that do not make up other. 423 """ 424 return self._topology(geos_difference(self._ptr, other._ptr)) 486 425 487 426 @property 488 427 def envelope(self): 489 428 "Return the envelope for this geometry (a polygon)." 490 return self._unary_topology(lgeos.GEOSEnvelope) 491 492 @property 493 def centroid(self): 494 """ 495 The centroid is equal to the centroid of the set of component Geometries 496 of highest dimension (since the lower-dimension geometries contribute zero 497 "weight" to the centroid). 498 """ 499 return self._unary_topology(lgeos.GEOSGetCentroid) 500 501 @property 502 def boundary(self): 503 "Returns the boundary as a newly allocated Geometry object." 504 return self._unary_topology(lgeos.GEOSBoundary) 505 506 @property 507 def convex_hull(self): 508 """ 509 Returns the smallest convex Polygon that contains all the points 510 in the Geometry. 511 """ 512 return self._unary_topology(lgeos.GEOSConvexHull) 429 return self._topology(geos_envelope(self._ptr)) 430 431 def intersection(self, other): 432 "Returns a Geometry representing the points shared by this Geometry and other." 433 return self._topology(geos_intersection(self._ptr, other._ptr)) 513 434 514 435 @property 515 436 def point_on_surface(self): 516 437 "Computes an interior point of this Geometry." 517 return self._unary_topology(lgeos.GEOSPointOnSurface) 518 519 def simplify(self, tolerance=0.0, preserve_topology=False): 520 """ 521 Returns the Geometry, simplified using the Douglas-Peucker algorithm 522 to the specified tolerance (higher tolerance => less points). If no 523 tolerance provided, defaults to 0. 524 525 By default, this function does not preserve topology - e.g. polygons can 526 be split, collapse to lines or disappear holes can be created or 527 disappear, and lines can cross. By specifying preserve_topology=True, 528 the result will have the same dimension and number of components as the 529 input. This is significantly slower. 530 """ 531 if preserve_topology: 532 return self._unary_topology(lgeos.GEOSTopologyPreserveSimplify, c_double(tolerance)) 533 else: 534 return self._unary_topology(lgeos.GEOSSimplify, c_double(tolerance)) 438 return self._topology(geos_pointonsurface(self._ptr)) 535 439 536 440 def relate(self, other): 537 441 "Returns the DE-9IM intersection matrix for this Geometry and the other." 538 return string_at(lgeos.GEOSRelate(self._ptr(), other._ptr())) 539 540 def difference(self, other): 541 """Returns a Geometry representing the points making up this Geometry 542 that do not make up other.""" 543 return self._binary_topology(lgeos.GEOSDifference, other) 442 return geos_relate(self._ptr, other._ptr) 443 444 def simplify(self, tolerance=0.0, preserve_topology=False): 445 """ 446 Returns the Geometry, simplified using the Douglas-Peucker algorithm 447 to the specified tolerance (higher tolerance => less points). If no 448 tolerance provided, defaults to 0. 449 450 By default, this function does not preserve topology - e.g. polygons can 451 be split, collapse to lines or disappear holes can be created or 452 disappear, and lines can cross. By specifying preserve_topology=True, 453 the result will have the same dimension and number of components as the 454 input. This is significantly slower. 455 """ 456 if preserve_topology: 457 return self._topology(geos_preservesimplify(self._ptr, tolerance)) 458 else: 459 return self._topology(geos_simplify(self._ptr, tolerance)) 544 460 545 461 def sym_difference(self, other): 546 462 """ 547 463 Returns a set combining the points in this Geometry not in other, 548 and the points in other not in this Geometry. 549 """ 550 return self._binary_topology(lgeos.GEOSSymDifference, other) 551 552 def intersection(self, other): 553 "Returns a Geometry representing the points shared by this Geometry and other." 554 return self._binary_topology(lgeos.GEOSIntersection, other) 464 and the points in other not in this Geometry. 465 """ 466 return self._topology(geos_symdifference(self._ptr, other._ptr)) 555 467 556 468 def union(self, other): 557 469 "Returns a Geometry representing all the points in this Geometry and other." 558 return self._ binary_topology(lgeos.GEOSUnion, other)470 return self._topology(geos_union(self._ptr, other._ptr)) 559 471 560 472 #### Other Routines #### … … 562 474 def area(self): 563 475 "Returns the area of the Geometry." 564 a = c_double() 565 status = lgeos.GEOSArea(self._ptr(), byref(a)) 566 if status != 1: return None 567 else: return a.value 476 return geos_area(self._ptr, byref(c_double())) 568 477 569 478 def distance(self, other): 570 479 """ 571 480 Returns the distance between the closest points on this Geometry 572 and the other. Units will be in those of the coordinate system of573 the Geometry.481 and the other. Units will be in those of the coordinate system of 482 the Geometry. 574 483 """ 575 484 if not isinstance(other, GEOSGeometry): 576 raise TypeError, 'distance() works only on other GEOS Geometries.' 577 dist = c_double() 578 status = lgeos.GEOSDistance(self._ptr(), other._ptr(), byref(dist)) 579 if status != 1: return None 580 else: return dist.value 485 raise TypeError('distance() works only on other GEOS Geometries.') 486 return geos_distance(self._ptr, other._ptr, byref(c_double())) 581 487 582 488 @property … … 584 490 """ 585 491 Returns the length of this Geometry (e.g., 0 for point, or the 586 circumfrence of a Polygon). 587 """ 588 l = c_double() 589 status = lgeos.GEOSLength(self._ptr(), byref(l)) 590 if status != 1: return None 591 else: return l.value 492 circumfrence of a Polygon). 493 """ 494 return geos_length(self._ptr, byref(c_double())) 592 495 593 496 def clone(self): 594 497 "Clones this Geometry." 595 return GEOSGeometry( lgeos.GEOSGeom_clone(self._ptr()), srid=self.srid)498 return GEOSGeometry(geom_clone(self._ptr), srid=self.srid) 596 499 597 500 # Class mapping dictionary 598 501 from django.contrib.gis.geos.geometries import Point, Polygon, LineString, LinearRing 599 502 from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon 600 GEOS_CLASSES = { 'Point': Point,601 'Polygon' : Polygon,602 'LineString' : LineString,603 'LinearRing' : LinearRing,604 'MultiPoint': MultiPoint,605 'MultiLineString': MultiLineString,606 'MultiPolygon': MultiPolygon,607 'GeometryCollection' : GeometryCollection,503 GEOS_CLASSES = {0 : Point, 504 1 : LineString, 505 2 : LinearRing, 506 3 : Polygon, 507 4 : MultiPoint, 508 5 : MultiLineString, 509 6 : MultiPolygon, 510 7 : GeometryCollection, 608 511 } django/branches/gis/django/contrib/gis/geos/collections.py
r6024 r6653 1 1 """ 2 This module houses the Geometry Collection objects:3 GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon2 This module houses the Geometry Collection objects: 3 GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon 4 4 """ 5 from ctypes import c_int, c_uint, byref , cast5 from ctypes import c_int, c_uint, byref 6 6 from types import TupleType, ListType 7 7 from django.contrib.gis.geos.base import GEOSGeometry 8 8 from django.contrib.gis.geos.error import GEOSException, GEOSGeometryIndexError 9 9 from django.contrib.gis.geos.geometries import Point, LineString, LinearRing, Polygon 10 from django.contrib.gis.geos.libgeos import lgeos,get_pointer_arr, GEOM_PTR11 from django.contrib.gis.geos.p ointer import GEOSPointer10 from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOM_PTR 11 from django.contrib.gis.geos.prototypes import create_collection, destroy_geom, geom_clone, geos_typeid, get_cs, get_geomn 12 12 13 13 class GeometryCollection(GEOSGeometry): … … 34 34 # Ensuring that only the permitted geometries are allowed in this collection 35 35 if False in [isinstance(geom, self._allowed) for geom in init_geoms]: 36 raise TypeError , 'Invalid Geometry type encountered in the arguments.'36 raise TypeError('Invalid Geometry type encountered in the arguments.') 37 37 38 # Creating the geometry pointer array, and populating each element in 39 # the array with the address of the Geometry returned by _nullify(). 40 ngeom = len(init_geoms) 41 geoms = get_pointer_arr(ngeom) 42 for i in xrange(ngeom): 43 geoms[i] = cast(init_geoms[i]._nullify(), GEOM_PTR) 44 45 # Calling the parent class, using the pointer returned from the 46 # GEOS createCollection() factory. 47 addr = lgeos.GEOSGeom_createCollection(c_int(self._typeid), 48 byref(geoms), c_uint(ngeom)) 49 super(GeometryCollection, self).__init__(addr, **kwargs) 38 # Creating the geometry pointer array. 39 ngeoms = len(init_geoms) 40 geoms = get_pointer_arr(ngeoms) 41 for i in xrange(ngeoms): geoms[i] = geom_clone(init_geoms[i]._ptr) 42 super(GeometryCollection, self).__init__(create_collection(c_int(self._typeid), byref(geoms), c_uint(ngeoms)), **kwargs) 50 43 51 def __del__(self):52 "Overloaded deletion method for Geometry Collections."53 #print 'collection: Deleting %s (parent=%s, valid=%s)' % (self.__class__.__name__, self._ptr.parent, self._ptr.valid)54 # If this geometry is still valid, it hasn't been modified by others.55 if self._ptr.valid:56 # Nullifying pointers to internal Geometries, preventing any57 # attempted future access.58 for g in self._ptr: g.nullify()59 else:60 # Internal memory has become part of other Geometry objects; must61 # delete the internal objects which are still valid individually,62 # because calling the destructor on the entire geometry will result63 # in an attempted deletion of NULL pointers for the missing64 # components (which may crash Python).65 for g in self._ptr:66 if len(g) > 0:67 # The collection geometry is a Polygon, destroy any leftover68 # LinearRings.69 for r in g: r.destroy()70 g.destroy()71 72 super(GeometryCollection, self).__del__()73 74 44 def __getitem__(self, index): 75 45 "Returns the Geometry from this Collection at the given index (0-based)." 76 46 # Checking the index and returning the corresponding GEOS geometry. 77 47 self._checkindex(index) 78 return GEOSGeometry( self._ptr[index], srid=self.srid)48 return GEOSGeometry(geom_clone(get_geomn(self._ptr, index)), srid=self.srid) 79 49 80 50 def __setitem__(self, index, geom): … … 82 52 self._checkindex(index) 83 53 if not isinstance(geom, self._allowed): 84 raise TypeError, 'Incompatible Geometry for collection.' 54 raise TypeError('Incompatible Geometry for collection.') 55 56 ngeoms = len(self) 57 geoms = get_pointer_arr(ngeoms) 58 for i in xrange(ngeoms): 59 if i == index: 60 geoms[i] = geom_clone(geom._ptr) 61 else: 62 geoms[i] = geom_clone(get_geomn(self._ptr, i)) 63 64 # Creating a new collection, and destroying the contents of the previous poiner. 65 prev_ptr = self._ptr 66 srid = self.srid 67 self._ptr = create_collection(c_int(self._typeid), byref(geoms), c_uint(ngeoms)) 68 if srid: self.srid = srid 69 destroy_geom(prev_ptr) 85 70 86 # Constructing the list of geometries that will go in the collection.87 new_geoms = []88 for i in xrange(len(self)):89 if i == index: new_geoms.append(geom)90 else: new_geoms.append(self[i])91 92 # Creating a new geometry collection from the list, and93 # re-assigning the pointers.94 new_collection = self.__class__(*new_geoms, **{'srid':self.srid})95 self._reassign(new_collection)96 97 71 def __iter__(self): 98 72 "Iterates over each Geometry in the Collection." … … 107 81 "Checks the given geometry index." 108 82 if index < 0 or index >= self.num_geom: 109 raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index) 110 111 def _nullify(self): 112 "Overloaded from base method to nullify geometry references in this Collection." 113 # Nullifying the references to the internal Geometry objects from this Collection. 114 for g in self._ptr: g.nullify() 115 return super(GeometryCollection, self)._nullify() 116 117 def _populate(self): 118 "Internal routine that populates the internal children geometries list." 119 ptr_list = [] 120 for i in xrange(len(self)): 121 # Getting the geometry pointer for the geometry at the index. 122 geom_ptr = lgeos.GEOSGetGeometryN(self._ptr(), c_int(i)) 123 124 # Adding the coordinate sequence to the list, or using None if the 125 # collection Geometry doesn't support coordinate sequences. 126 if lgeos.GEOSGeomTypeId(geom_ptr) in (0, 1, 2): 127 ptr_list.append((geom_ptr, lgeos.GEOSGeom_getCoordSeq(geom_ptr))) 128 else: 129 ptr_list.append((geom_ptr, None)) 130 self._ptr.set_children(ptr_list) 83 raise GEOSGeometryIndexError('invalid GEOS Geometry index: %s' % str(index)) 131 84 132 85 @property django/branches/gis/django/contrib/gis/geos/coordseq.py
r6024 r6653 1 1 """ 2 This module houses the GEOSCoordSeq object, andis used internally3 by GEOSGeometry to house the actual coordinates of the Point,4 LineString, and LinearRing geometries.2 This module houses the GEOSCoordSeq object, which is used internally 3 by GEOSGeometry to house the actual coordinates of the Point, 4 LineString, and LinearRing geometries. 5 5 """ 6 from ctypes import c_double, c_uint, byref 7 from types import ListType, TupleType 6 8 from django.contrib.gis.geos.error import GEOSException, GEOSGeometry
