Changeset 7114
- Timestamp:
- 02/14/08 10:33:16 (5 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/gdal/prototypes/geom.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/geos/base.py (modified) (8 diffs)
- django/branches/gis/django/contrib/gis/geos/collections.py (modified) (1 diff)
- django/branches/gis/django/contrib/gis/geos/coordseq.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/geos/geometries.py (modified) (11 diffs)
- django/branches/gis/django/contrib/gis/tests/test_geos.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/gdal/prototypes/geom.py
r7113 r7114 106 106 107 107 # For retrieving the envelope of the geometry. 108 get_envelope = lgdal.OGR_G_GetEnvelope 109 get_envelope.restype = None 110 get_envelope.argtypes = [c_void_p, POINTER(OGREnvelope)] 111 get_envelope.errcheck = check_envelope 108 get_envelope = env_func(lgdal.OGR_G_GetEnvelope, [c_void_p, POINTER(OGREnvelope)]) 109 django/branches/gis/django/contrib/gis/geos/base.py
r7101 r7114 6 6 import re 7 7 from ctypes import addressof, byref, c_double, c_size_t 8 from types import StringType, UnicodeType, IntType, FloatType, BufferType8 from types import UnicodeType 9 9 10 10 # GEOS-related dependencies. … … 21 21 # try/except since this package may be used outside GeoDjango. 22 22 try: 23 from django.contrib.gis.gdal import OGRGeometry, SpatialReference 24 HAS_GDAL =True23 from django.contrib.gis.gdal import OGRGeometry, SpatialReference, GEOJSON 24 HAS_GDAL = True 25 25 except: 26 HAS_GDAL =False26 HAS_GDAL, GEOJSON = False, False 27 27 28 28 # Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure … … 31 31 hex_regex = re.compile(r'^[0-9A-F]+$', re.I) 32 32 wkt_regex = re.compile(r'^(SRID=(?P<srid>\d+);)?(?P<wkt>(POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+)$', re.I) 33 json_regex = re.compile(r'^\{.+\}$') 33 34 34 35 class GEOSGeometry(object): … … 51 52 (SRID) number for this Geometry. If not set, the SRID will be None. 52 53 """ 53 if isinstance(geo_input, UnicodeType): 54 # Encoding to ASCII, WKT or HEXEWKB doesn't need any more. 55 geo_input = geo_input.encode('ascii') 56 if isinstance(geo_input, StringType): 57 if hex_regex.match(geo_input): 58 # If the regex matches, the geometry is in HEX form. 54 if isinstance(geo_input, basestring): 55 if isinstance(geo_input, UnicodeType): 56 # Encoding to ASCII, WKT or HEXEWKB doesn't need any more. 57 geo_input = geo_input.encode('ascii') 58 59 wkt_m = wkt_regex.match(geo_input) 60 if wkt_m: 61 # Handling WKT input. 62 if wkt_m.group('srid'): srid = int(wkt_m.group('srid')) 63 g = from_wkt(wkt_m.group('wkt')) 64 elif hex_regex.match(geo_input): 65 # Handling HEXEWKB input. 59 66 g = from_hex(geo_input, len(geo_input)) 67 elif GEOJSON and json_regex.match(geo_input): 68 # Handling GeoJSON input. 69 wkb_input = str(OGRGeometry(geo_input).wkb) 70 g = from_wkb(wkb_input, len(wkb_input)) 60 71 else: 61 m = wkt_regex.match(geo_input) 62 if m: 63 if m.group('srid'): srid = int(m.group('srid')) 64 g = from_wkt(m.group('wkt')) 65 else: 66 raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.') 72 raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.') 67 73 elif isinstance(geo_input, GEOM_PTR): 68 74 # When the input is a pointer to a geomtry (GEOM_PTR). 69 75 g = geo_input 70 elif isinstance(geo_input, BufferType):76 elif isinstance(geo_input, buffer): 71 77 # When the input is a buffer (WKB). 72 78 wkb_input = str(geo_input) … … 192 198 def coord_seq(self): 193 199 "Returns a clone of the coordinate sequence for this Geometry." 194 return self._cs.clone() 200 if self.has_cs: 201 return self._cs.clone() 195 202 196 203 #### Geometry Info #### … … 308 315 two Geometries match the elements in pattern. 309 316 """ 310 if not isinstance(pattern, StringType) or len(pattern) > 9:317 if not isinstance(pattern, str) or len(pattern) > 9: 311 318 raise GEOSException('invalid intersection matrix pattern') 312 319 return geos_relatepattern(self.ptr, other.ptr, pattern) … … 360 367 # str(self.wkb).encode('hex') 361 368 return to_hex(self.ptr, byref(c_size_t())) 369 370 @property 371 def json(self): 372 """ 373 Returns GeoJSON representation of this Geometry if GDAL 1.5+ 374 is installed. 375 """ 376 if GEOJSON: return self.ogr.json 377 geojson = json 362 378 363 379 @property … … 523 539 524 540 @property 541 def extent(self): 542 """ 543 Returns the extent of this geometry as a 4-tuple, consisting of 544 (xmin, ymin, xmax, ymax). 545 """ 546 env = self.envelope 547 if isinstance(env, Point): 548 xmin, ymin = env.tuple 549 xmax, ymax = xmin, ymin 550 else: 551 xmin, ymin = env[0][0] 552 xmax, ymax = env[0][2] 553 return (xmin, ymin, xmax, ymax) 554 555 @property 525 556 def length(self): 526 557 """ django/branches/gis/django/contrib/gis/geos/collections.py
r6978 r7114 86 86 def kml(self): 87 87 "Returns the KML for this Geometry Collection." 88 kml = '<MultiGeometry>' 89 for g in self: kml += g.kml 90 return kml + '</MultiGeometry>' 88 return '<MultiGeometry>%s</MultiGeometry>' % ''.join([g.kml for g in self]) 89 90 @property 91 def tuple(self): 92 "Returns a tuple of all the coordinates in this Geometry Collection" 93 return tuple([g.tuple for g in self]) 94 coords = tuple 91 95 92 96 # MultiPoint, MultiLineString, and MultiPolygon class definitions. django/branches/gis/django/contrib/gis/geos/coordseq.py
r6978 r7114 154 154 if self.hasz: substr = '%s,%s,%s ' 155 155 else: substr = '%s,%s,0 ' 156 kml = '<coordinates>' 157 for i in xrange(len(self)): 158 kml += substr % self[i] 159 return kml.strip() + '</coordinates>' 156 return '<coordinates>%s</coordinates>' % \ 157 ''.join([substr % self[i] for i in xrange(len(self))]).strip() 160 158 161 159 @property … … 164 162 n = self.size 165 163 if n == 1: return self[0] 166 else: return tuple( self[i] for i in xrange(n))164 else: return tuple([self[i] for i in xrange(n)]) django/branches/gis/django/contrib/gis/geos/geometries.py
r6978 r7114 5 5 """ 6 6 from ctypes import c_uint, byref 7 from types import FloatType, IntType, ListType, TupleType8 7 from django.contrib.gis.geos.base import GEOSGeometry 9 8 from django.contrib.gis.geos.coordseq import GEOSCoordSeq … … 25 24 """ 26 25 27 if isinstance(x, ( TupleType, ListType)):26 if isinstance(x, (tuple, list)): 28 27 # Here a tuple or list was passed in under the `x` parameter. 29 28 ndim = len(x) … … 31 30 raise TypeError('Invalid sequence parameter: %s' % str(x)) 32 31 coords = x 33 elif isinstance(x, ( IntType, FloatType)) and isinstance(y, (IntType, FloatType)):32 elif isinstance(x, (int, float, long)) and isinstance(y, (int, float, long)): 34 33 # Here X, Y, and (optionally) Z were passed in individually, as parameters. 35 if isinstance(z, ( IntType, FloatType)):34 if isinstance(z, (int, float, long)): 36 35 ndim = 3 37 36 coords = [x, y, z] … … 104 103 # The tuple and coords properties 105 104 tuple = property(get_coords, set_coords) 106 coords = property(get_coords, set_coords)105 coords = tuple 107 106 108 107 class LineString(GEOSGeometry): … … 125 124 else: coords = args 126 125 127 if isinstance(coords, ( TupleType, ListType)):126 if isinstance(coords, (tuple, list)): 128 127 # Getting the number of coords and the number of dimensions -- which 129 128 # must stay the same, e.g., no LineString((1, 2), (1, 2, 3)). … … 134 133 # Incrementing through each of the coordinates and verifying 135 134 for i in xrange(1, ncoords): 136 if not isinstance(coords[i], ( TupleType, ListType, Point)):135 if not isinstance(coords[i], (tuple, list, Point)): 137 136 raise TypeError('each coordinate should be a sequence (list or tuple)') 138 137 if len(coords[i]) != ndim: raise TypeError('Dimension mismatch.') … … 194 193 "Returns a tuple version of the geometry from the coordinate sequence." 195 194 return self._cs.tuple 195 coords = tuple 196 196 197 197 def _listarr(self, func): … … 237 237 """ 238 238 Initializes on an exterior ring and a sequence of holes (both 239 instances of LinearRings. 239 instances may be either LinearRing instances, or a tuple/list 240 that may be constructed into a LinearRing). 240 241 241 Below are some examples of initialization, where shell, hole1, and242 hole2 arevalid LinearRing geometries:242 Examples of initialization, where shell, hole1, and hole2 are 243 valid LinearRing geometries: 243 244 >>> poly = Polygon(shell, hole1, hole2) 244 245 >>> poly = Polygon(shell, (hole1, hole2)) 246 247 Example where a tuple parameters are used: 248 >>> poly = Polygon(((0, 0), (0, 10), (10, 10), (0, 10), (0, 0)), 249 ((4, 4), (4, 6), (6, 6), (6, 4), (4, 4))) 245 250 """ 246 251 if not args: … … 250 255 ext_ring = args[0] 251 256 init_holes = args[1:] 252 if len(init_holes) == 1 and isinstance(init_holes[0], (TupleType, ListType)): 257 n_holes = len(init_holes) 258 259 # If initialized as Polygon(shell, (LinearRing, LinearRing)) [for backward-compatibility] 260 if n_holes == 1 and isinstance(init_holes[0], (tuple, list)) and \ 261 (len(init_holes[0]) == 0 or isinstance(init_holes[0][0], LinearRing)): 253 262 init_holes = init_holes[0] 254 255 # Ensuring the exterior ring parameter is a LinearRing object 256 if not isinstance(ext_ring, LinearRing):257 raise TypeError('First argument for Polygon initialization must be a LinearRing.')258 259 # Making sure all of the holes are LinearRing objects260 if False in [isinstance(hole, LinearRing) for hole in init_holes]:261 raise TypeError('Holes parameter must be a sequence of LinearRings.')262 263 # Getting the holes array.264 nholes = len(init_holes)265 holes = get_pointer_arr(n holes)266 for i in xrange(n holes): holes[i] = geom_clone(init_holes[i].ptr)263 n_holes = len(init_holes) 264 265 # Ensuring the exterior ring and holes parameters are LinearRing objects 266 # or may be instantiated into LinearRings. 267 ext_ring = self._construct_ring(ext_ring, 'Exterior parameter must be a LinearRing or an object that can initialize a LinearRing.') 268 holes_list = [] # Create new list, cause init_holes is a tuple. 269 for i in xrange(n_holes): 270 holes_list.append(self._construct_ring(init_holes[i], 'Holes parameter must be a sequence of LinearRings or objects that can initialize to LinearRings')) 271 272 # Why another loop? Because if a TypeError is raised, cloned pointers will 273 # be around that can't be cleaned up. 274 holes = get_pointer_arr(n_holes) 275 for i in xrange(n_holes): holes[i] = geom_clone(holes_list[i].ptr) 267 276 268 # Getting the shell pointer address ,277 # Getting the shell pointer address. 269 278 shell = geom_clone(ext_ring.ptr) 270 279 271 280 # Calling with the GEOS createPolygon factory. 272 super(Polygon, self).__init__(create_polygon(shell, byref(holes), c_uint(n holes)), **kwargs)281 super(Polygon, self).__init__(create_polygon(shell, byref(holes), c_uint(n_holes)), **kwargs) 273 282 274 283 def __getitem__(self, index): 275 284 """ 276 Returns the ring at the specified index. The first index, 0, will always 277 return the exterior ring. Indices > 0 will return the interior ring. 285 Returns the ring at the specified index. The first index, 0, will 286 always return the exterior ring. Indices > 0 will return the 287 interior ring at the given index (e.g., poly[1] and poly[2] would 288 return the first and second interior ring, respectively). 278 289 """ 279 290 if index == 0: … … 331 342 raise GEOSIndexError('invalid Polygon ring index: %s' % index) 332 343 344 def _construct_ring(self, param, msg=''): 345 "Helper routine for trying to construct a ring from the given parameter." 346 if isinstance(param, LinearRing): return param 347 try: 348 ring = LinearRing(param) 349 return ring 350 except TypeError: 351 raise TypeError(msg) 352 333 353 def get_interior_ring(self, ring_i): 334 354 """ … … 356 376 # properties for the exterior ring/shell 357 377 exterior_ring = property(get_ext_ring, set_ext_ring) 358 shell = property(get_ext_ring, set_ext_ring)378 shell = exterior_ring 359 379 360 380 @property 361 381 def tuple(self): 362 382 "Gets the tuple for each ring in this Polygon." 363 return tuple(self[i].tuple for i in xrange(len(self))) 383 return tuple([self[i].tuple for i in xrange(len(self))]) 384 coords = tuple 364 385 365 386 @property 366 387 def kml(self): 367 388 "Returns the KML representation of this Polygon." 368 inner_kml = '' 369 if self.num_interior_rings > 0: 370 for i in xrange(self.num_interior_rings): 371 inner_kml += "<innerBoundaryIs>%s</innerBoundaryIs>" % self[i+1].kml 389 inner_kml = ''.join(["<innerBoundaryIs>%s</innerBoundaryIs>" % self[i+1].kml 390 for i in xrange(self.num_interior_rings)]) 372 391 return "<Polygon><outerBoundaryIs>%s</outerBoundaryIs>%s</Polygon>" % (self[0].kml, inner_kml) django/branches/gis/django/contrib/gis/tests/test_geos.py
r7101 r7114 6 6 7 7 if HAS_NUMPY: from numpy import array 8 if HAS_GDAL: from django.contrib.gis.gdal import OGRGeometry, SpatialReference, CoordTransform 8 if HAS_GDAL: from django.contrib.gis.gdal import OGRGeometry, SpatialReference, CoordTransform, GEOJSON 9 9 10 10 class GEOSTest(unittest.TestCase): … … 86 86 self.assertEqual(srid, fromstr(poly.ewkt).srid) # Checking export 87 87 88 def test01i_eq(self): 88 def test01i_json(self): 89 "Testing GeoJSON input/output (via GDAL)." 90 if not HAS_GDAL or not GEOJSON: return 91 for g in json_geoms: 92 geom = GEOSGeometry(g.wkt) 93 self.assertEqual(g.json, geom.json) 94 self.assertEqual(g.json, geom.geojson) 95 self.assertEqual(GEOSGeometry(g.wkt), GEOSGeometry(geom.json)) 96 97 def test01j_eq(self): 89 98 "Testing equivalence with WKT." 90 99 p = fromstr('POINT(5 23)') … … 269 278 # Testing __getitem__ and __setitem__ on invalid indices 270 279 self.assertRaises(GEOSIndexError, poly.__getitem__, len(poly)) 271 #self.assertRaises(GEOSIndexError, poly.__setitem__, len(poly), False)280 self.assertRaises(GEOSIndexError, poly.__setitem__, len(poly), False) 272 281 self.assertRaises(GEOSIndexError, poly.__getitem__, -1) 273 282 … … 280 289 self.assertRaises(TypeError, Polygon.__init__, 0, [1, 2, 3]) 281 290 self.assertRaises(TypeError, Polygon.__init__, 'foo') 291 292 # Polygon(shell, (hole1, ... holeN)) 282 293 rings = tuple(r for r in poly) 283 294 self.assertEqual(poly, Polygon(rings[0], rings[1:])) 295 296 # Polygon(shell_tuple, hole_tuple1, ... , hole_tupleN) 297 ring_tuples = tuple(r.tuple for r in poly) 298 self.assertEqual(poly, Polygon(*ring_tuples)) 299 300 # Constructing with tuples of LinearRings. 284 301 self.assertEqual(poly.wkt, Polygon(*tuple(r for r in poly)).wkt) 285 302 self.assertEqual(poly.wkt, Polygon(*tuple(LinearRing(r.tuple) for r in poly)).wkt) … … 687 704 ct = CoordTransform(SpatialReference('WGS84'), SpatialReference(2774)) 688 705 t3.transform(ct) 689 706 prec = 3 690 707 for p in (t1, t2, t3): 691 prec = 3692 708 self.assertAlmostEqual(trans.x, p.x, prec) 693 709 self.assertAlmostEqual(trans.y, p.y, prec) 710 711 def test24_extent(self): 712 "Testing `extent` method." 713 # The xmin, ymin, xmax, ymax of the MultiPoint should be returned. 714 mp = MultiPoint(Point(5, 23), Point(0, 0), Point(10, 50)) 715 self.assertEqual((0.0, 0.0, 10.0, 50.0), mp.extent) 716 pnt = Point(5.23, 17.8) 717 # Extent of points is just the point itself repeated. 718 self.assertEqual((5.23, 17.8, 5.23, 17.8), pnt.extent) 719 # Testing on the 'real world' Polygon. 720 poly = fromstr(polygons[3].wkt) 721 ring = poly.shell 722 x, y = ring.x, ring.y 723 xmin, ymin = min(x), min(y) 724 xmax, ymax = max(x), max(y) 725 self.assertEqual((xmin, ymin, xmax, ymax), poly.extent) 694 726 695 727 def suite():
