19 # All other functions in this module come from the ctypes |
19 # All other functions in this module come from the ctypes |
20 # prototypes module -- which handles all interaction with |
20 # prototypes module -- which handles all interaction with |
21 # the underlying GEOS library. |
21 # the underlying GEOS library. |
22 from django.contrib.gis.geos import prototypes as capi |
22 from django.contrib.gis.geos import prototypes as capi |
23 |
23 |
24 # Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure |
24 # These functions provide access to a thread-local instance |
25 # to prevent potentially malicious input from reaching the underlying C |
25 # of their corresponding GEOS I/O class. |
26 # library. Not a substitute for good web security programming practices. |
26 from django.contrib.gis.geos.prototypes.io import wkt_r, wkt_w, wkb_r, wkb_w, ewkb_w, ewkb_w3d |
27 hex_regex = re.compile(r'^[0-9A-F]+$', re.I) |
27 |
28 wkt_regex = re.compile(r'^(SRID=(?P<srid>\d+);)?(?P<wkt>(POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+)$', re.I) |
28 # For recognizing geometry input. |
|
29 from django.contrib.gis.geometry.regex import hex_regex, wkt_regex, json_regex |
29 |
30 |
30 class GEOSGeometry(GEOSBase, ListMixin): |
31 class GEOSGeometry(GEOSBase, ListMixin): |
31 "A class that, generally, encapsulates a GEOS geometry." |
32 "A class that, generally, encapsulates a GEOS geometry." |
32 |
33 |
33 # Raise GEOSIndexError instead of plain IndexError |
34 # Raise GEOSIndexError instead of plain IndexError |
59 |
60 |
60 wkt_m = wkt_regex.match(geo_input) |
61 wkt_m = wkt_regex.match(geo_input) |
61 if wkt_m: |
62 if wkt_m: |
62 # Handling WKT input. |
63 # Handling WKT input. |
63 if wkt_m.group('srid'): srid = int(wkt_m.group('srid')) |
64 if wkt_m.group('srid'): srid = int(wkt_m.group('srid')) |
64 g = wkt_r.read(wkt_m.group('wkt')) |
65 g = wkt_r().read(wkt_m.group('wkt')) |
65 elif hex_regex.match(geo_input): |
66 elif hex_regex.match(geo_input): |
66 # Handling HEXEWKB input. |
67 # Handling HEXEWKB input. |
67 g = wkb_r.read(geo_input) |
68 g = wkb_r().read(geo_input) |
68 elif gdal.GEOJSON and gdal.geometries.json_regex.match(geo_input): |
69 elif gdal.GEOJSON and json_regex.match(geo_input): |
69 # Handling GeoJSON input. |
70 # Handling GeoJSON input. |
70 g = wkb_r.read(gdal.OGRGeometry(geo_input).wkb) |
71 g = wkb_r().read(gdal.OGRGeometry(geo_input).wkb) |
71 else: |
72 else: |
72 raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.') |
73 raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.') |
73 elif isinstance(geo_input, GEOM_PTR): |
74 elif isinstance(geo_input, GEOM_PTR): |
74 # When the input is a pointer to a geomtry (GEOM_PTR). |
75 # When the input is a pointer to a geomtry (GEOM_PTR). |
75 g = geo_input |
76 g = geo_input |
76 elif isinstance(geo_input, buffer): |
77 elif isinstance(geo_input, buffer): |
77 # When the input is a buffer (WKB). |
78 # When the input is a buffer (WKB). |
78 g = wkb_r.read(geo_input) |
79 g = wkb_r().read(geo_input) |
79 elif isinstance(geo_input, GEOSGeometry): |
80 elif isinstance(geo_input, GEOSGeometry): |
80 g = capi.geom_clone(geo_input.ptr) |
81 g = capi.geom_clone(geo_input.ptr) |
81 else: |
82 else: |
82 # Invalid geometry type. |
83 # Invalid geometry type. |
83 raise TypeError('Improper geometry input type: %s' % str(type(geo_input))) |
84 raise TypeError('Improper geometry input type: %s' % str(type(geo_input))) |
140 return str(self.wkb), self.srid |
141 return str(self.wkb), self.srid |
141 |
142 |
142 def __setstate__(self, state): |
143 def __setstate__(self, state): |
143 # Instantiating from the tuple state that was pickled. |
144 # Instantiating from the tuple state that was pickled. |
144 wkb, srid = state |
145 wkb, srid = state |
145 ptr = capi.from_wkb(wkb, len(wkb)) |
146 ptr = wkb_r().read(buffer(wkb)) |
146 if not ptr: raise GEOSException('Invalid Geometry loaded from pickled state.') |
147 if not ptr: raise GEOSException('Invalid Geometry loaded from pickled state.') |
147 self.ptr = ptr |
148 self.ptr = ptr |
148 self._post_init(srid) |
149 self._post_init(srid) |
149 |
150 |
150 # Comparison operators |
151 # Comparison operators |
355 srid = property(get_srid, set_srid) |
356 srid = property(get_srid, set_srid) |
356 |
357 |
357 #### Output Routines #### |
358 #### Output Routines #### |
358 @property |
359 @property |
359 def ewkt(self): |
360 def ewkt(self): |
360 "Returns the EWKT (WKT + SRID) of the Geometry." |
361 """ |
|
362 Returns the EWKT (WKT + SRID) of the Geometry. Note that Z values |
|
363 are *not* included in this representation because GEOS does not yet |
|
364 support serializing them. |
|
365 """ |
361 if self.get_srid(): return 'SRID=%s;%s' % (self.srid, self.wkt) |
366 if self.get_srid(): return 'SRID=%s;%s' % (self.srid, self.wkt) |
362 else: return self.wkt |
367 else: return self.wkt |
363 |
368 |
364 @property |
369 @property |
365 def wkt(self): |
370 def wkt(self): |
366 "Returns the WKT (Well-Known Text) of the Geometry." |
371 "Returns the WKT (Well-Known Text) representation of this Geometry." |
367 return wkt_w.write(self) |
372 return wkt_w().write(self) |
368 |
373 |
369 @property |
374 @property |
370 def hex(self): |
375 def hex(self): |
371 """ |
376 """ |
372 Returns the HEX of the Geometry -- please note that the SRID is not |
377 Returns the WKB of this Geometry in hexadecimal form. Please note |
373 included in this representation, because the GEOS C library uses |
378 that the SRID and Z values are not included in this representation |
374 -1 by default, even if the SRID is set. |
379 because it is not a part of the OGC specification (use the `hexewkb` |
|
380 property instead). |
375 """ |
381 """ |
376 # A possible faster, all-python, implementation: |
382 # A possible faster, all-python, implementation: |
377 # str(self.wkb).encode('hex') |
383 # str(self.wkb).encode('hex') |
378 return wkb_w.write_hex(self) |
384 return wkb_w().write_hex(self) |
|
385 |
|
386 @property |
|
387 def hexewkb(self): |
|
388 """ |
|
389 Returns the EWKB of this Geometry in hexadecimal form. This is an |
|
390 extension of the WKB specification that includes SRID and Z values |
|
391 that are a part of this geometry. |
|
392 """ |
|
393 if self.hasz: |
|
394 if not GEOS_PREPARE: |
|
395 # See: http://trac.osgeo.org/geos/ticket/216 |
|
396 raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.') |
|
397 return ewkb_w3d().write_hex(self) |
|
398 else: |
|
399 return ewkb_w().write_hex(self) |
379 |
400 |
380 @property |
401 @property |
381 def json(self): |
402 def json(self): |
382 """ |
403 """ |
383 Returns GeoJSON representation of this Geometry if GDAL 1.5+ |
404 Returns GeoJSON representation of this Geometry if GDAL 1.5+ |
384 is installed. |
405 is installed. |
385 """ |
406 """ |
386 if gdal.GEOJSON: |
407 if gdal.GEOJSON: |
387 return self.ogr.json |
408 return self.ogr.json |
388 else: |
409 else: |
389 raise GEOSException('GeoJSON output only supported on GDAL 1.5+.') |
410 raise GEOSException('GeoJSON output only supported on GDAL 1.5+.') |
390 geojson = json |
411 geojson = json |
391 |
412 |
392 @property |
413 @property |
393 def wkb(self): |
414 def wkb(self): |
394 "Returns the WKB of the Geometry as a buffer." |
415 """ |
395 return wkb_w.write(self) |
416 Returns the WKB (Well-Known Binary) representation of this Geometry |
|
417 as a Python buffer. SRID and Z values are not included, use the |
|
418 `ewkb` property instead. |
|
419 """ |
|
420 return wkb_w().write(self) |
|
421 |
|
422 @property |
|
423 def ewkb(self): |
|
424 """ |
|
425 Return the EWKB representation of this Geometry as a Python buffer. |
|
426 This is an extension of the WKB specification that includes any SRID |
|
427 and Z values that are a part of this geometry. |
|
428 """ |
|
429 if self.hasz: |
|
430 if not GEOS_PREPARE: |
|
431 # See: http://trac.osgeo.org/geos/ticket/216 |
|
432 raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D EWKB.') |
|
433 return ewkb_w3d().write(self) |
|
434 else: |
|
435 return ewkb_w().write(self) |
396 |
436 |
397 @property |
437 @property |
398 def kml(self): |
438 def kml(self): |
399 "Returns the KML representation of this Geometry." |
439 "Returns the KML representation of this Geometry." |
400 gtype = self.geom_type |
440 gtype = self.geom_type |
452 if gdal.HAS_GDAL and srid: |
492 if gdal.HAS_GDAL and srid: |
453 # Creating an OGR Geometry, which is then transformed. |
493 # Creating an OGR Geometry, which is then transformed. |
454 g = gdal.OGRGeometry(self.wkb, srid) |
494 g = gdal.OGRGeometry(self.wkb, srid) |
455 g.transform(ct) |
495 g.transform(ct) |
456 # Getting a new GEOS pointer |
496 # Getting a new GEOS pointer |
457 ptr = wkb_r.read(g.wkb) |
497 ptr = wkb_r().read(g.wkb) |
458 if clone: |
498 if clone: |
459 # User wants a cloned transformed geometry returned. |
499 # User wants a cloned transformed geometry returned. |
460 return GEOSGeometry(ptr, srid=g.srid) |
500 return GEOSGeometry(ptr, srid=g.srid) |
461 if ptr: |
501 if ptr: |
462 # Reassigning pointer, and performing post-initialization setup |
502 # Reassigning pointer, and performing post-initialization setup |