Maintenance refactor of the GDAL (OGR) ctypes interface. Changes include:

* All C API method explictly called from their prototype module, no longer imported via *.
* Applied DRY to C pointer management, classes that do so subclass from `GDALBase`.
* `OGRGeometry`: Added `from_bbox` class method (patch from Christopher Schmidt) and `kml` property.
* `SpatialReference`: Now initialize with `SetFromUserInput` (initialization is now more simple and flexible); removed duplicate methods.
* `Envelope`: Added `expand_to_include` method and now allow same coordinates for lower left and upper right points.  Thanks to Paul Smith for tickets and patches.
* `OGRGeomType`: Now treat OGC 'Geometry' type as 'Unknown'.

Fixed #9855, #10368, #10380.  Refs #9806.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@9985 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2009-03-07 23:02:48 +00:00
parent 53da1e4794
commit f1ada99997
22 changed files with 515 additions and 372 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2007, Justin Bronn
Copyright (c) 2007-2009, Justin Bronn
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

View File

@ -0,0 +1,35 @@
from ctypes import c_void_p
from types import NoneType
from django.contrib.gis.gdal.error import GDALException
class GDALBase(object):
"""
Base object for GDAL objects that has a pointer access property
that controls access to the underlying C pointer.
"""
# Initially the pointer is NULL.
_ptr = None
# Default allowed pointer type.
ptr_type = c_void_p
# Pointer access property.
def _get_ptr(self):
# Raise an exception if the pointer isn't valid don't
# want to be passing NULL pointers to routines --
# that's very bad.
if self._ptr: return self._ptr
else: raise GDALException('GDAL %s pointer no longer valid.' % self.__class__.__name__)
def _set_ptr(self, ptr):
# Only allow the pointer to be set with pointers of the
# compatible type or None (NULL).
if isinstance(ptr, int):
self._ptr = self.ptr_type(ptr)
elif isinstance(ptr, (self.ptr_type, NoneType)):
self._ptr = ptr
else:
raise TypeError('Incompatible pointer type')
ptr = property(_get_ptr, _set_ptr)

View File

@ -37,28 +37,23 @@
from ctypes import byref, c_void_p
# The GDAL C library, OGR exceptions, and the Layer object.
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.driver import Driver
from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.layer import Layer
# Getting the ctypes prototypes for the DataSource.
from django.contrib.gis.gdal.prototypes.ds import \
destroy_ds, get_driver_count, register_all, open_ds, release_ds, \
get_ds_name, get_layer, get_layer_count, get_layer_by_name
from django.contrib.gis.gdal.prototypes import ds as capi
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_DS_* routines are relevant here.
class DataSource(object):
class DataSource(GDALBase):
"Wraps an OGR Data Source object."
#### Python 'magic' routines ####
def __init__(self, ds_input, ds_driver=False, write=False):
# DataSource pointer is initially NULL.
self._ptr = None
# The write flag.
if write:
self._write = 1
@ -67,33 +62,34 @@ class DataSource(object):
# Registering all the drivers, this needs to be done
# _before_ we try to open up a data source.
if not get_driver_count(): register_all()
if not capi.get_driver_count():
capi.register_all()
if isinstance(ds_input, basestring):
# The data source driver is a void pointer.
ds_driver = c_void_p()
ds_driver = Driver.ptr_type()
try:
# OGROpen will auto-detect the data source type.
ds = open_ds(ds_input, self._write, byref(ds_driver))
ds = capi.open_ds(ds_input, self._write, byref(ds_driver))
except OGRException:
# Making the error message more clear rather than something
# like "Invalid pointer returned from OGROpen".
raise OGRException('Could not open the datasource at "%s"' % ds_input)
elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p):
elif isinstance(ds_input, self.ptr_type) and isinstance(ds_driver, Driver.ptr_type):
ds = ds_input
else:
raise OGRException('Invalid data source input type: %s' % type(ds_input))
if bool(ds):
self._ptr = ds
self._driver = Driver(ds_driver)
self.ptr = ds
self.driver = Driver(ds_driver)
else:
# Raise an exception if the returned pointer is NULL
raise OGRException('Invalid data source file "%s"' % ds_input)
def __del__(self):
"Destroys this DataStructure object."
if self._ptr: destroy_ds(self._ptr)
if self._ptr: capi.destroy_ds(self._ptr)
def __iter__(self):
"Allows for iteration over the layers in a data source."
@ -103,12 +99,12 @@ class DataSource(object):
def __getitem__(self, index):
"Allows use of the index [] operator to get a layer at the index."
if isinstance(index, basestring):
l = get_layer_by_name(self._ptr, index)
l = capi.get_layer_by_name(self.ptr, index)
if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
elif isinstance(index, int):
if index < 0 or index >= self.layer_count:
raise OGRIndexError('index out of range')
l = get_layer(self._ptr, index)
l = capi.get_layer(self._ptr, index)
else:
raise TypeError('Invalid index type: %s' % type(index))
return Layer(l, self)
@ -121,18 +117,12 @@ class DataSource(object):
"Returns OGR GetName and Driver for the Data Source."
return '%s (%s)' % (self.name, str(self.driver))
#### DataSource Properties ####
@property
def driver(self):
"Returns the Driver object for this Data Source."
return self._driver
@property
def layer_count(self):
"Returns the number of layers in the data source."
return get_layer_count(self._ptr)
return capi.get_layer_count(self._ptr)
@property
def name(self):
"Returns the name of the data source."
return get_ds_name(self._ptr)
return capi.get_ds_name(self._ptr)

View File

@ -1,14 +1,14 @@
# prerequisites imports
from ctypes import c_void_p
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.error import OGRException
from django.contrib.gis.gdal.prototypes.ds import \
get_driver, get_driver_by_name, get_driver_count, get_driver_name, register_all
from django.contrib.gis.gdal.prototypes import ds as capi
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_Dr_* routines are relevant here.
class Driver(object):
class Driver(GDALBase):
"Wraps an OGR Data Source Driver."
# Case-insensitive aliases for OGR Drivers.
@ -24,7 +24,6 @@ class Driver(object):
if isinstance(dr_input, basestring):
# If a string name of the driver was passed in
self._ptr = None # Initially NULL
self._register()
# Checking the alias dictionary (case-insensitive) to see if an alias
@ -35,10 +34,10 @@ class Driver(object):
name = dr_input
# Attempting to get the OGR driver by the string name.
dr = get_driver_by_name(name)
dr = capi.get_driver_by_name(name)
elif isinstance(dr_input, int):
self._register()
dr = get_driver(dr_input)
dr = capi.get_driver(dr_input)
elif isinstance(dr_input, c_void_p):
dr = dr_input
else:
@ -47,20 +46,20 @@ class Driver(object):
# Making sure we get a valid pointer to the OGR Driver
if not dr:
raise OGRException('Could not initialize OGR Driver on input: %s' % str(dr_input))
self._ptr = dr
self.ptr = dr
def __str__(self):
"Returns the string name of the OGR Driver."
return get_driver_name(self._ptr)
return capi.get_driver_name(self.ptr)
def _register(self):
"Attempts to register all the data source drivers."
# Only register all if the driver count is 0 (or else all drivers
# will be registered over and over again)
if not self.driver_count: register_all()
if not self.driver_count: capi.register_all()
# Driver properties
@property
def driver_count(self):
"Returns the number of OGR data source drivers registered."
return get_driver_count()
return capi.get_driver_count()

View File

@ -11,7 +11,6 @@
Lower left (min_x, min_y) o----------+
"""
from ctypes import Structure, c_double
from types import TupleType, ListType
from django.contrib.gis.gdal.error import OGRException
# The OGR definition of an Envelope is a C structure containing four doubles.
@ -42,7 +41,7 @@ class Envelope(object):
if isinstance(args[0], OGREnvelope):
# OGREnvelope (a ctypes Structure) was passed in.
self._envelope = args[0]
elif isinstance(args[0], (TupleType, ListType)):
elif isinstance(args[0], (tuple, list)):
# A tuple was passed in.
if len(args[0]) != 4:
raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0]))
@ -58,10 +57,10 @@ class Envelope(object):
raise OGRException('Incorrect number (%d) of arguments.' % len(args))
# Checking the x,y coordinates
if self.min_x >= self.max_x:
raise OGRException('Envelope minimum X >= maximum X.')
if self.min_y >= self.max_y:
raise OGRException('Envelope minimum Y >= maximum Y.')
if self.min_x > self.max_x:
raise OGRException('Envelope minimum X > maximum X.')
if self.min_y > self.max_y:
raise OGRException('Envelope minimum Y > maximum Y.')
def __eq__(self, other):
"""
@ -71,7 +70,7 @@ class Envelope(object):
if isinstance(other, Envelope):
return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \
(self.max_x == other.max_x) and (self.max_y == other.max_y)
elif isinstance(other, TupleType) and len(other) == 4:
elif isinstance(other, tuple) and len(other) == 4:
return (self.min_x == other[0]) and (self.min_y == other[1]) and \
(self.max_x == other[2]) and (self.max_y == other[3])
else:
@ -89,6 +88,48 @@ class Envelope(object):
self._envelope.MaxX = seq[2]
self._envelope.MaxY = seq[3]
def expand_to_include(self, *args):
"""
Modifies the envelope to expand to include the boundaries of
the passed-in 2-tuple (a point), 4-tuple (an extent) or
envelope.
"""
# We provide a number of different signatures for this method,
# and the logic here is all about converting them into a
# 4-tuple single parameter which does the actual work of
# expanding the envelope.
if len(args) == 1:
if isinstance(args[0], Envelope):
return self.expand_to_include(args[0].tuple)
elif hasattr(args[0], 'x') and hasattr(args[0], 'y'):
return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y)
elif isinstance(args[0], (tuple, list)):
# A tuple was passed in.
if len(args[0]) == 2:
return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1]))
elif len(args[0]) == 4:
(minx, miny, maxx, maxy) = args[0]
if minx < self._envelope.MinX:
self._envelope.MinX = minx
if miny < self._envelope.MinY:
self._envelope.MinY = miny
if maxx > self._envelope.MaxX:
self._envelope.MaxX = maxx
if maxy > self._envelope.MaxY:
self._envelope.MaxY = maxy
else:
raise OGRException('Incorrect number of tuple elements (%d).' % len(args[0]))
else:
raise TypeError('Incorrect type of argument: %s' % str(type(args[0])))
elif len(args) == 2:
# An x and an y parameter were passed in
return self.expand_to_include((args[0], args[1], args[0], args[1]))
elif len(args) == 4:
# Individiual parameters passed in.
return self.expand_to_include(args)
else:
raise OGRException('Incorrect number (%d) of arguments.' % len(args[0]))
@property
def min_x(self):
"Returns the value of the minimum X coordinate."

View File

@ -4,6 +4,7 @@
OGR methods.
"""
#### OGR & SRS Exceptions ####
class GDALException(Exception): pass
class OGRException(Exception): pass
class SRSException(Exception): pass
class OGRIndexError(OGRException, KeyError):

View File

@ -1,36 +1,31 @@
# The GDAL C library, OGR exception, and the Field object
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.field import Field
from django.contrib.gis.gdal.geometries import OGRGeometry, OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference
# ctypes function prototypes
from django.contrib.gis.gdal.prototypes.ds import \
destroy_feature, feature_equal, get_fd_geom_type, get_feat_geom_ref, \
get_feat_name, get_feat_field_count, get_fid, get_field_defn, \
get_field_index, get_field_name
from django.contrib.gis.gdal.prototypes.geom import clone_geom, get_geom_srs
from django.contrib.gis.gdal.prototypes.srs import clone_srs
from django.contrib.gis.gdal.prototypes import ds as capi, geom as geom_api
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_F_* routines are relevant here.
class Feature(object):
class Feature(GDALBase):
"A class that wraps an OGR Feature, needs to be instantiated from a Layer object."
#### Python 'magic' routines ####
def __init__(self, feat, fdefn):
"Initializes on the pointers for the feature and the layer definition."
self._ptr = None # Initially NULL
if not feat or not fdefn:
raise OGRException('Cannot create OGR Feature, invalid pointer given.')
self._ptr = feat
self.ptr = feat
self._fdefn = fdefn
def __del__(self):
"Releases a reference to this object."
if self._ptr: destroy_feature(self._ptr)
if self._ptr: capi.destroy_feature(self._ptr)
def __getitem__(self, index):
"""
@ -45,7 +40,7 @@ class Feature(object):
if index < 0 or index > self.num_fields:
raise OGRIndexError('index out of range')
i = index
return Field(self._ptr, i)
return Field(self.ptr, i)
def __iter__(self):
"Iterates over each field in the Feature."
@ -62,41 +57,41 @@ class Feature(object):
def __eq__(self, other):
"Does equivalence testing on the features."
return bool(feature_equal(self._ptr, other._ptr))
return bool(capi.feature_equal(self.ptr, other._ptr))
#### Feature Properties ####
@property
def fid(self):
"Returns the feature identifier."
return get_fid(self._ptr)
return capi.get_fid(self.ptr)
@property
def layer_name(self):
"Returns the name of the layer for the feature."
return get_feat_name(self._fdefn)
return capi.get_feat_name(self._fdefn)
@property
def num_fields(self):
"Returns the number of fields in the Feature."
return get_feat_field_count(self._ptr)
return capi.get_feat_field_count(self.ptr)
@property
def fields(self):
"Returns a list of fields in the Feature."
return [get_field_name(get_field_defn(self._fdefn, i))
return [capi.get_field_name(capi.get_field_defn(self._fdefn, i))
for i in xrange(self.num_fields)]
@property
def geom(self):
"Returns the OGR Geometry for this Feature."
# Retrieving the geometry pointer for the feature.
geom_ptr = get_feat_geom_ref(self._ptr)
return OGRGeometry(clone_geom(geom_ptr))
geom_ptr = capi.get_feat_geom_ref(self.ptr)
return OGRGeometry(geom_api.clone_geom(geom_ptr))
@property
def geom_type(self):
"Returns the OGR Geometry Type for this Feture."
return OGRGeomType(get_fd_geom_type(self._fdefn))
return OGRGeomType(capi.get_fd_geom_type(self._fdefn))
#### Feature Methods ####
def get(self, field):
@ -110,6 +105,6 @@ class Feature(object):
def index(self, field_name):
"Returns the index of the given field name."
i = get_field_index(self._ptr, field_name)
i = capi.get_field_index(self.ptr, field_name)
if i < 0: raise OGRIndexError('invalid OFT field name given: "%s"' % field_name)
return i

View File

@ -1,16 +1,14 @@
from ctypes import byref, c_int
from datetime import date, datetime, time
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.error import OGRException
from django.contrib.gis.gdal.prototypes.ds import \
get_feat_field_defn, get_field_as_datetime, get_field_as_double, \
get_field_as_integer, get_field_as_string, get_field_name, get_field_precision, \
get_field_type, get_field_type_name, get_field_width
from django.contrib.gis.gdal.prototypes import ds as capi
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_Fld_* routines are relevant here.
class Field(object):
class Field(GDALBase):
"A class that wraps an OGR Field, needs to be instantiated from a Feature object."
#### Python 'magic' routines ####
@ -24,13 +22,13 @@ class Field(object):
self._index = index
# Getting the pointer for this field.
fld = get_feat_field_defn(feat, index)
if not fld:
fld_ptr = capi.get_feat_field_defn(feat, index)
if not fld_ptr:
raise OGRException('Cannot create OGR Field, invalid pointer given.')
self._ptr = fld
self.ptr = fld_ptr
# Setting the class depending upon the OGR Field Type (OFT)
self.__class__ = FIELD_CLASSES[self.type]
self.__class__ = OGRFieldTypes[self.type]
# OFTReal with no precision should be an OFTInteger.
if isinstance(self, OFTReal) and self.precision == 0:
@ -43,21 +41,21 @@ class Field(object):
#### Field Methods ####
def as_double(self):
"Retrieves the Field's value as a double (float)."
return get_field_as_double(self._feat, self._index)
return capi.get_field_as_double(self._feat, self._index)
def as_int(self):
"Retrieves the Field's value as an integer."
return get_field_as_integer(self._feat, self._index)
return capi.get_field_as_integer(self._feat, self._index)
def as_string(self):
"Retrieves the Field's value as a string."
return get_field_as_string(self._feat, self._index)
return capi.get_field_as_string(self._feat, self._index)
def as_datetime(self):
"Retrieves the Field's value as a tuple of date & time components."
yy, mm, dd, hh, mn, ss, tz = [c_int() for i in range(7)]
status = get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd),
byref(hh), byref(mn), byref(ss), byref(tz))
status = capi.get_field_as_datetime(self._feat, self._index, byref(yy), byref(mm), byref(dd),
byref(hh), byref(mn), byref(ss), byref(tz))
if status:
return (yy, mm, dd, hh, mn, ss, tz)
else:
@ -67,22 +65,22 @@ class Field(object):
@property
def name(self):
"Returns the name of this Field."
return get_field_name(self._ptr)
return capi.get_field_name(self.ptr)
@property
def precision(self):
"Returns the precision of this Field."
return get_field_precision(self._ptr)
return capi.get_field_precision(self.ptr)
@property
def type(self):
"Returns the OGR type of this Field."
return get_field_type(self._ptr)
return capi.get_field_type(self.ptr)
@property
def type_name(self):
"Return the OGR field type name for this Field."
return get_field_type_name(self.type)
return capi.get_field_type_name(self.type)
@property
def value(self):
@ -93,7 +91,7 @@ class Field(object):
@property
def width(self):
"Returns the width of this Field."
return get_field_width(self._ptr)
return capi.get_field_width(self.ptr)
### The Field sub-classes for each OGR Field type. ###
class OFTInteger(Field):
@ -163,8 +161,8 @@ class OFTRealList(Field): pass
class OFTStringList(Field): pass
class OFTWideStringList(Field): pass
# Class mapping dictionary for OFT Types
FIELD_CLASSES = { 0 : OFTInteger,
# Class mapping dictionary for OFT Types and reverse mapping.
OGRFieldTypes = { 0 : OFTInteger,
1 : OFTIntegerList,
2 : OFTReal,
3 : OFTRealList,
@ -177,3 +175,4 @@ FIELD_CLASSES = { 0 : OFTInteger,
10 : OFTTime,
11 : OFTDateTime,
}
ROGRFieldTypes = dict([(cls, num) for num, cls in OGRFieldTypes.items()])

View File

@ -44,14 +44,15 @@ from binascii import a2b_hex
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
# Getting GDAL prerequisites
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
from django.contrib.gis.gdal.geomtype import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference, CoordTransform
# Getting the ctypes prototype functions that interface w/the GDAL C library.
from django.contrib.gis.gdal.prototypes.geom import *
from django.contrib.gis.gdal.prototypes.srs import clone_srs
from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api
GEOJSON = capi.GEOJSON
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
@ -64,13 +65,12 @@ wkt_regex = re.compile(r'^(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOIN
json_regex = re.compile(r'^(\s+)?\{[\s\w,\[\]\{\}\-\."\':]+\}(\s+)?$')
#### OGRGeometry Class ####
class OGRGeometry(object):
class OGRGeometry(GDALBase):
"Generally encapsulates an OGR geometry."
def __init__(self, geom_input, srs=None):
"Initializes Geometry on either WKT or an OGR pointer as input."
self._ptr = c_void_p(None) # Initially NULL
str_instance = isinstance(geom_input, basestring)
# If HEX, unpack input to to a binary buffer.
@ -91,27 +91,27 @@ class OGRGeometry(object):
if wkt_m.group('type').upper() == 'LINEARRING':
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
# See http://trac.osgeo.org/gdal/ticket/1992.
g = create_geom(OGRGeomType(wkt_m.group('type')).num)
import_wkt(g, byref(c_char_p(geom_input)))
g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
capi.import_wkt(g, byref(c_char_p(geom_input)))
else:
g = from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
g = capi.from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
elif json_m:
if GEOJSON:
g = from_json(geom_input)
g = capi.from_json(geom_input)
else:
raise NotImplementedError('GeoJSON input only supported on GDAL 1.5+.')
else:
# Seeing if the input is a valid short-hand string
# (e.g., 'Point', 'POLYGON').
ogr_t = OGRGeomType(geom_input)
g = create_geom(OGRGeomType(geom_input).num)
g = capi.create_geom(OGRGeomType(geom_input).num)
elif isinstance(geom_input, buffer):
# WKB was passed in
g = from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
g = capi.from_wkb(str(geom_input), None, byref(c_void_p()), len(geom_input))
elif isinstance(geom_input, OGRGeomType):
# OGRGeomType was passed in, an empty geometry will be created.
g = create_geom(geom_input.num)
elif isinstance(geom_input, c_void_p):
g = capi.create_geom(geom_input.num)
elif isinstance(geom_input, self.ptr_type):
# OGR pointer (c_void_p) was the input.
g = geom_input
else:
@ -121,7 +121,7 @@ class OGRGeometry(object):
# by setting the pointer for the object.
if not g:
raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input))
self._ptr = g
self.ptr = g
# Assigning the SpatialReference object to the geometry, if valid.
if bool(srs): self.srs = srs
@ -129,9 +129,16 @@ class OGRGeometry(object):
# Setting the class depending upon the OGR Geometry Type
self.__class__ = GEO_CLASSES[self.geom_type.num]
@classmethod
def from_bbox(cls, bbox):
"Constructs a Polygon from a bounding box (4-tuple)."
x0, y0, x1, y1 = bbox
return OGRGeometry( 'POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % (
x0, y0, x0, y1, x1, y1, x1, y0, x0, y0) )
def __del__(self):
"Deletes this Geometry."
if self._ptr: destroy_geom(self._ptr)
if self._ptr: capi.destroy_geom(self._ptr)
### Geometry set-like operations ###
# g = g1 | g2
@ -170,22 +177,22 @@ class OGRGeometry(object):
@property
def dimension(self):
"Returns 0 for points, 1 for lines, and 2 for surfaces."
return get_dims(self._ptr)
return capi.get_dims(self.ptr)
@property
def coord_dim(self):
"Returns the coordinate dimension of the Geometry."
return get_coord_dims(self._ptr)
return capi.get_coord_dims(self.ptr)
@property
def geom_count(self):
"The number of elements in this Geometry."
return get_geom_count(self._ptr)
return capi.get_geom_count(self.ptr)
@property
def point_count(self):
"Returns the number of Points in this Geometry."
return get_point_count(self._ptr)
return capi.get_point_count(self.ptr)
@property
def num_points(self):
@ -201,28 +208,28 @@ class OGRGeometry(object):
def geom_type(self):
"Returns the Type for this Geometry."
try:
return OGRGeomType(get_geom_type(self._ptr))
return OGRGeomType(capi.get_geom_type(self.ptr))
except OGRException:
# VRT datasources return an invalid geometry type
# number, but a valid name -- we'll try that instead.
# See: http://trac.osgeo.org/gdal/ticket/2491
return OGRGeomType(get_geom_name(self._ptr))
return OGRGeomType(capi.get_geom_name(self.ptr))
@property
def geom_name(self):
"Returns the Name of this Geometry."
return get_geom_name(self._ptr)
return capi.get_geom_name(self.ptr)
@property
def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
return get_area(self._ptr)
return capi.get_area(self.ptr)
@property
def envelope(self):
"Returns the envelope for this Geometry."
# TODO: Fix Envelope() for Point geometries.
return Envelope(get_envelope(self._ptr, byref(OGREnvelope())))
return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope())))
@property
def extent(self):
@ -232,39 +239,40 @@ class OGRGeometry(object):
#### SpatialReference-related Properties ####
# The SRS property
def get_srs(self):
def _get_srs(self):
"Returns the Spatial Reference for this Geometry."
try:
srs_ptr = get_geom_srs(self._ptr)
return SpatialReference(clone_srs(srs_ptr))
srs_ptr = capi.get_geom_srs(self.ptr)
return SpatialReference(srs_api.clone_srs(srs_ptr))
except SRSException:
return None
def set_srs(self, srs):
def _set_srs(self, srs):
"Sets the SpatialReference for this geometry."
if isinstance(srs, SpatialReference):
srs_ptr = clone_srs(srs._ptr)
srs_ptr = srs_api.clone_srs(srs.ptr)
elif isinstance(srs, (int, long, basestring)):
sr = SpatialReference(srs)
srs_ptr = clone_srs(sr._ptr)
srs_ptr = srs_api.clone_srs(sr.ptr)
else:
raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
assign_srs(self._ptr, srs_ptr)
capi.assign_srs(self.ptr, srs_ptr)
srs = property(get_srs, set_srs)
srs = property(_get_srs, _set_srs)
# The SRID property
def get_srid(self):
if self.srs: return self.srs.srid
else: return None
def _get_srid(self):
srs = self.srs
if srs: return srs.srid
return None
def set_srid(self, srid):
def _set_srid(self, srid):
if isinstance(srid, (int, long)):
self.srs = srid
else:
raise TypeError('SRID must be set with an integer.')
srid = property(get_srid, set_srid)
srid = property(_get_srid, _set_srid)
#### Output Methods ####
@property
@ -276,7 +284,7 @@ class OGRGeometry(object):
@property
def gml(self):
"Returns the GML representation of the Geometry."
return to_gml(self._ptr)
return capi.to_gml(self.ptr)
@property
def hex(self):
@ -286,16 +294,28 @@ class OGRGeometry(object):
@property
def json(self):
"""
Returns the GeoJSON representation of this Geometry (requires
GDAL 1.5+).
"""
if GEOJSON:
return to_json(self._ptr)
return capi.to_json(self.ptr)
else:
raise NotImplementedError('GeoJSON output only supported on GDAL 1.5+.')
geojson = json
@property
def kml(self):
"Returns the KML representation of the Geometry."
if GEOJSON:
return capi.to_kml(self.ptr, None)
else:
raise NotImplementedError('KML output only supported on GDAL 1.5+.')
@property
def wkb_size(self):
"Returns the size of the WKB buffer."
return get_wkbsize(self._ptr)
return capi.get_wkbsize(self.ptr)
@property
def wkb(self):
@ -307,19 +327,19 @@ class OGRGeometry(object):
sz = self.wkb_size
# Creating the unsigned character buffer, and passing it in by reference.
buf = (c_ubyte * sz)()
wkb = to_wkb(self._ptr, byteorder, byref(buf))
wkb = capi.to_wkb(self.ptr, byteorder, byref(buf))
# Returning a buffer of the string at the pointer.
return buffer(string_at(buf, sz))
@property
def wkt(self):
"Returns the WKT representation of the Geometry."
return to_wkt(self._ptr, byref(c_char_p()))
return capi.to_wkt(self.ptr, byref(c_char_p()))
#### Geometry Methods ####
def clone(self):
"Clones this OGR Geometry."
return OGRGeometry(clone_geom(self._ptr), self.srs)
return OGRGeometry(capi.clone_geom(self.ptr), self.srs)
def close_rings(self):
"""
@ -328,7 +348,7 @@ class OGRGeometry(object):
end.
"""
# Closing the open rings.
geom_close_rings(self._ptr)
capi.geom_close_rings(self.ptr)
def transform(self, coord_trans, clone=False):
"""
@ -344,12 +364,12 @@ class OGRGeometry(object):
klone.transform(coord_trans)
return klone
if isinstance(coord_trans, CoordTransform):
geom_transform(self._ptr, coord_trans._ptr)
capi.geom_transform(self.ptr, coord_trans.ptr)
elif isinstance(coord_trans, SpatialReference):
geom_transform_to(self._ptr, coord_trans._ptr)
capi.geom_transform_to(self.ptr, coord_trans.ptr)
elif isinstance(coord_trans, (int, long, basestring)):
sr = SpatialReference(coord_trans)
geom_transform_to(self._ptr, sr._ptr)
capi.geom_transform_to(self.ptr, sr.ptr)
else:
raise TypeError('Transform only accepts CoordTransform, SpatialReference, string, and integer objects.')
@ -366,52 +386,52 @@ class OGRGeometry(object):
# Returning the output of the given function with the other geometry's
# pointer.
return func(self._ptr, other._ptr)
return func(self.ptr, other.ptr)
def intersects(self, other):
"Returns True if this geometry intersects with the other."
return self._topology(ogr_intersects, other)
return self._topology(capi.ogr_intersects, other)
def equals(self, other):
"Returns True if this geometry is equivalent to the other."
return self._topology(ogr_equals, other)
return self._topology(capi.ogr_equals, other)
def disjoint(self, other):
"Returns True if this geometry and the other are spatially disjoint."
return self._topology(ogr_disjoint, other)
return self._topology(capi.ogr_disjoint, other)
def touches(self, other):
"Returns True if this geometry touches the other."
return self._topology(ogr_touches, other)
return self._topology(capi.ogr_touches, other)
def crosses(self, other):
"Returns True if this geometry crosses the other."
return self._topology(ogr_crosses, other)
return self._topology(capi.ogr_crosses, other)
def within(self, other):
"Returns True if this geometry is within the other."
return self._topology(ogr_within, other)
return self._topology(capi.ogr_within, other)
def contains(self, other):
"Returns True if this geometry contains the other."
return self._topology(ogr_contains, other)
return self._topology(capi.ogr_contains, other)
def overlaps(self, other):
"Returns True if this geometry overlaps the other."
return self._topology(ogr_overlaps, other)
return self._topology(capi.ogr_overlaps, other)
#### Geometry-generation Methods ####
def _geomgen(self, gen_func, other=None):
"A helper routine for the OGR routines that generate geometries."
if isinstance(other, OGRGeometry):
return OGRGeometry(gen_func(self._ptr, other._ptr), self.srs)
return OGRGeometry(gen_func(self.ptr, other.ptr), self.srs)
else:
return OGRGeometry(gen_func(self._ptr), self.srs)
return OGRGeometry(gen_func(self.ptr), self.srs)
@property
def boundary(self):
"Returns the boundary of this geometry."
return self._geomgen(get_boundary)
return self._geomgen(capi.get_boundary)
@property
def convex_hull(self):
@ -419,35 +439,35 @@ class OGRGeometry(object):
Returns the smallest convex Polygon that contains all the points in
this Geometry.
"""
return self._geomgen(geom_convex_hull)
return self._geomgen(capi.geom_convex_hull)
def difference(self, other):
"""
Returns a new geometry consisting of the region which is the difference
of this geometry and the other.
"""
return self._geomgen(geom_diff, other)
return self._geomgen(capi.geom_diff, other)
def intersection(self, other):
"""
Returns a new geometry consisting of the region of intersection of this
geometry and the other.
"""
return self._geomgen(geom_intersection, other)
return self._geomgen(capi.geom_intersection, other)
def sym_difference(self, other):
"""
Returns a new geometry which is the symmetric difference of this
geometry and the other.
"""
return self._geomgen(geom_sym_diff, other)
return self._geomgen(capi.geom_sym_diff, other)
def union(self, other):
"""
Returns a new geometry consisting of the region which is the union of
this geometry and the other.
"""
return self._geomgen(geom_union, other)
return self._geomgen(capi.geom_union, other)
# The subclasses for OGR Geometry.
class Point(OGRGeometry):
@ -455,18 +475,18 @@ class Point(OGRGeometry):
@property
def x(self):
"Returns the X coordinate for this Point."
return getx(self._ptr, 0)
return capi.getx(self.ptr, 0)
@property
def y(self):
"Returns the Y coordinate for this Point."
return gety(self._ptr, 0)
return capi.gety(self.ptr, 0)
@property
def z(self):
"Returns the Z coordinate for this Point."
if self.coord_dim == 3:
return getz(self._ptr, 0)
return capi.getz(self.ptr, 0)
@property
def tuple(self):
@ -483,7 +503,7 @@ class LineString(OGRGeometry):
"Returns the Point at the given index."
if index >= 0 and index < self.point_count:
x, y, z = c_double(), c_double(), c_double()
get_point(self._ptr, index, byref(x), byref(y), byref(z))
capi.get_point(self.ptr, index, byref(x), byref(y), byref(z))
dim = self.coord_dim
if dim == 1:
return (x.value,)
@ -514,23 +534,23 @@ class LineString(OGRGeometry):
Internal routine that returns a sequence (list) corresponding with
the given function.
"""
return [func(self._ptr, i) for i in xrange(len(self))]
return [func(self.ptr, i) for i in xrange(len(self))]
@property
def x(self):
"Returns the X coordinates in a list."
return self._listarr(getx)
return self._listarr(capi.getx)
@property
def y(self):
"Returns the Y coordinates in a list."
return self._listarr(gety)
return self._listarr(capi.gety)
@property
def z(self):
"Returns the Z coordinates in a list."
if self.coord_dim == 3:
return self._listarr(getz)
return self._listarr(capi.getz)
# LinearRings are used in Polygons.
class LinearRing(LineString): pass
@ -551,7 +571,7 @@ class Polygon(OGRGeometry):
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % index)
else:
return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs)
# Polygon Properties
@property
@ -577,7 +597,7 @@ class Polygon(OGRGeometry):
"Returns the centroid (a Point) of this Polygon."
# The centroid is a Point, create a geometry for this.
p = OGRGeometry(OGRGeomType('Point'))
get_centroid(self._ptr, p._ptr)
capi.get_centroid(self.ptr, p.ptr)
return p
# Geometry Collection base class.
@ -589,7 +609,7 @@ class GeometryCollection(OGRGeometry):
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % index)
else:
return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs)
def __iter__(self):
"Iterates over each Geometry."
@ -604,12 +624,12 @@ class GeometryCollection(OGRGeometry):
"Add the geometry to this Geometry Collection."
if isinstance(geom, OGRGeometry):
if isinstance(geom, self.__class__):
for g in geom: add_geom(self._ptr, g._ptr)
for g in geom: capi.add_geom(self.ptr, g.ptr)
else:
add_geom(self._ptr, geom._ptr)
capi.add_geom(self.ptr, geom.ptr)
elif isinstance(geom, basestring):
tmp = OGRGeometry(geom)
add_geom(self._ptr, tmp._ptr)
capi.add_geom(self.ptr, tmp.ptr)
else:
raise OGRException('Must add an OGRGeometry.')

View File

@ -24,7 +24,9 @@ class OGRGeomType(object):
if isinstance(type_input, OGRGeomType):
num = type_input.num
elif isinstance(type_input, basestring):
num = self._str_types.get(type_input.lower(), None)
type_input = type_input.lower()
if type_input == 'geometry': type_input='unknown'
num = self._str_types.get(type_input, None)
if num is None:
raise OGRException('Invalid OGR String Type "%s"' % type_input)
elif isinstance(type_input, int):
@ -67,7 +69,8 @@ class OGRGeomType(object):
def django(self):
"Returns the Django GeometryField for this OGR Type."
s = self.name
if s in ('Unknown', 'LinearRing', 'None'):
if s in ('LinearRing', 'None'):
return None
else:
return s + 'Field'
elif s == 'Unknown':
s = 'Geometry'
return s + 'Field'

View File

@ -2,26 +2,22 @@
from ctypes import byref
# Other GDAL imports.
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
from django.contrib.gis.gdal.feature import Feature
from django.contrib.gis.gdal.field import FIELD_CLASSES
from django.contrib.gis.gdal.field import OGRFieldTypes
from django.contrib.gis.gdal.geometries import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference
# GDAL ctypes function prototypes.
from django.contrib.gis.gdal.prototypes.ds import \
get_extent, get_fd_geom_type, get_fd_name, get_feature, get_feature_count, \
get_field_count, get_field_defn, get_field_name, get_field_precision, \
get_field_width, get_field_type, get_layer_defn, get_layer_srs, \
get_next_feature, reset_reading, test_capability
from django.contrib.gis.gdal.prototypes.srs import clone_srs
from django.contrib.gis.gdal.prototypes import ds as capi, srs as srs_api
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_L_* routines are relevant here.
class Layer(object):
class Layer(GDALBase):
"A class that wraps an OGR Layer, needs to be instantiated from a DataSource object."
#### Python 'magic' routines ####
@ -32,12 +28,11 @@ class Layer(object):
reference to it is kept with this Layer. This prevents garbage
collection of the `DataSource` while this Layer is still active.
"""
self._ptr = None # Initially NULL
if not layer_ptr:
raise OGRException('Cannot create Layer, invalid pointer given')
self._ptr = layer_ptr
self.ptr = layer_ptr
self._ds = ds
self._ldefn = get_layer_defn(self._ptr)
self._ldefn = capi.get_layer_defn(self._ptr)
# Does the Layer support random reading?
self._random_read = self.test_capability('RandomRead')
@ -59,9 +54,9 @@ class Layer(object):
def __iter__(self):
"Iterates over each Feature in the Layer."
# ResetReading() must be called before iteration is to begin.
reset_reading(self._ptr)
capi.reset_reading(self._ptr)
for i in xrange(self.num_feat):
yield Feature(get_next_feature(self._ptr), self._ldefn)
yield Feature(capi.get_next_feature(self._ptr), self._ldefn)
def __len__(self):
"The length is the number of features."
@ -81,7 +76,7 @@ class Layer(object):
if self._random_read:
# If the Layer supports random reading, return.
try:
return Feature(get_feature(self._ptr, feat_id), self._ldefn)
return Feature(capi.get_feature(self.ptr, feat_id), self._ldefn)
except OGRException:
pass
else:
@ -97,35 +92,35 @@ class Layer(object):
def extent(self):
"Returns the extent (an Envelope) of this layer."
env = OGREnvelope()
get_extent(self._ptr, byref(env), 1)
capi.get_extent(self.ptr, byref(env), 1)
return Envelope(env)
@property
def name(self):
"Returns the name of this layer in the Data Source."
return get_fd_name(self._ldefn)
return capi.get_fd_name(self._ldefn)
@property
def num_feat(self, force=1):
"Returns the number of features in the Layer."
return get_feature_count(self._ptr, force)
return capi.get_feature_count(self.ptr, force)
@property
def num_fields(self):
"Returns the number of fields in the Layer."
return get_field_count(self._ldefn)
return capi.get_field_count(self._ldefn)
@property
def geom_type(self):
"Returns the geometry type (OGRGeomType) of the Layer."
return OGRGeomType(get_fd_geom_type(self._ldefn))
return OGRGeomType(capi.get_fd_geom_type(self._ldefn))
@property
def srs(self):
"Returns the Spatial Reference used in this Layer."
try:
ptr = get_layer_srs(self._ptr)
return SpatialReference(clone_srs(ptr))
ptr = capi.get_layer_srs(self.ptr)
return SpatialReference(srs_api.clone_srs(ptr))
except SRSException:
return None
@ -135,7 +130,7 @@ class Layer(object):
Returns a list of string names corresponding to each of the Fields
available in this Layer.
"""
return [get_field_name(get_field_defn(self._ldefn, i))
return [capi.get_field_name(capi.get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields) ]
@property
@ -146,19 +141,19 @@ class Layer(object):
an OGR layer that had an integer, a floating-point, and string
fields.
"""
return [FIELD_CLASSES[get_field_type(get_field_defn(self._ldefn, i))]
return [OGRFieldTypes[capi.get_field_type(capi.get_field_defn(self._ldefn, i))]
for i in xrange(self.num_fields)]
@property
def field_widths(self):
"Returns a list of the maximum field widths for the features."
return [get_field_width(get_field_defn(self._ldefn, i))
return [capi.get_field_width(capi.get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields)]
@property
def field_precisions(self):
"Returns the field precisions for the features."
return [get_field_precision(get_field_defn(self._ldefn, i))
return [capi.get_field_precision(capi.get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields)]
#### Layer Methods ####
@ -190,4 +185,4 @@ class Layer(object):
'FastFeatureCount', 'FastGetExtent', 'CreateField', 'Transactions',
'DeleteFeature', and 'FastSetNextByIndex'.
"""
return bool(test_capability(self._ptr, capability))
return bool(capi.test_capability(self.ptr, capability))

View File

@ -38,9 +38,11 @@ def topology_func(f):
if GEOJSON:
from_json = geom_output(lgdal.OGR_G_CreateGeometryFromJson, [c_char_p])
to_json = string_output(lgdal.OGR_G_ExportToJson, [c_void_p], str_result=True)
to_kml = string_output(lgdal.OGR_G_ExportToKML, [c_void_p, c_char_p], str_result=True)
else:
from_json = False
to_json = False
to_kml = False
# GetX, GetY, GetZ all return doubles.
getx = pnt_func(lgdal.OGR_G_GetX)

View File

@ -36,6 +36,7 @@ from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)])
from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p])
from_epsg = void_output(std_call('OSRImportFromEPSG'), [c_void_p, c_int])
from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p])
from_user_input = void_output(std_call('OSRSetFromUserInput'), [c_void_p, c_char_p])
# Morphing to/from ESRI WKT.
morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p])

View File

@ -27,89 +27,74 @@
NAD83 / Texas South Central
"""
import re
from types import UnicodeType, TupleType
from ctypes import byref, c_char_p, c_int, c_void_p
# Getting the error checking routine and exceptions
from django.contrib.gis.gdal.base import GDALBase
from django.contrib.gis.gdal.error import OGRException, SRSException
from django.contrib.gis.gdal.prototypes.srs import *
from django.contrib.gis.gdal.prototypes import srs as capi
#### Spatial Reference class. ####
class SpatialReference(object):
class SpatialReference(GDALBase):
"""
A wrapper for the OGRSpatialReference object. According to the GDAL website,
the SpatialReference object "provide[s] services to represent coordinate
systems (projections and datums) and to transform between them."
"""
# Well-Known Geographical Coordinate System Name
_well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269}
_epsg_regex = re.compile('^(EPSG:)?(?P<epsg>\d+)$', re.I)
_proj_regex = re.compile(r'^\+proj')
#### Python 'magic' routines ####
def __init__(self, srs_input='', srs_type='wkt'):
def __init__(self, srs_input=''):
"""
Creates a GDAL OSR Spatial Reference object from the given input.
The input may be string of OGC Well Known Text (WKT), an integer
EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand
string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83').
"""
# Intializing pointer and string buffer.
self._ptr = None
buf = c_char_p('')
srs_type = 'user'
if isinstance(srs_input, basestring):
# Encoding to ASCII if unicode passed in.
if isinstance(srs_input, UnicodeType):
if isinstance(srs_input, unicode):
srs_input = srs_input.encode('ascii')
epsg_m = self._epsg_regex.match(srs_input)
proj_m = self._proj_regex.match(srs_input)
if epsg_m:
# Is this an EPSG well known name?
srs_type = 'epsg'
srs_input = int(epsg_m.group('epsg'))
elif proj_m:
# Is the string a PROJ.4 string?
srs_type = 'proj'
elif srs_input in self._well_known:
# Is this a short-hand well known name?
srs_type = 'epsg'
srs_input = self._well_known[srs_input]
elif srs_type == 'proj':
try:
# If SRID is a string, e.g., '4326', then make acceptable
# as user input.
srid = int(srs_input)
srs_input = 'EPSG:%d' % srid
except ValueError:
pass
else:
# Setting the buffer with WKT, PROJ.4 string, etc.
buf = c_char_p(srs_input)
elif isinstance(srs_input, int):
elif isinstance(srs_input, (int, long)):
# EPSG integer code was input.
if srs_type != 'epsg': srs_type = 'epsg'
elif isinstance(srs_input, c_void_p):
srs_type = 'epsg'
elif isinstance(srs_input, self.ptr_type):
srs = srs_input
srs_type = 'ogr'
else:
raise TypeError('Invalid SRS type "%s"' % srs_type)
if srs_type == 'ogr':
# SRS input is OGR pointer
# Input is already an SRS pointer.
srs = srs_input
else:
# Creating a new pointer, using the string buffer.
srs = new_srs(buf)
# Creating a new SRS pointer, using the string buffer.
srs = capi.new_srs(buf)
# If the pointer is NULL, throw an exception.
if not srs:
raise SRSException('Could not create spatial reference from: %s' % srs_input)
else:
self._ptr = srs
self.ptr = srs
# Post-processing if in PROJ.4 or EPSG formats.
if srs_type == 'proj': self.import_proj(srs_input)
elif srs_type == 'epsg': self.import_epsg(srs_input)
# Importing from either the user input string or an integer SRID.
if srs_type == 'user':
self.import_user_input(srs_input)
elif srs_type == 'epsg':
self.import_epsg(srs_input)
def __del__(self):
"Destroys this spatial reference."
if self._ptr: release_srs(self._ptr)
if self._ptr: capi.release_srs(self._ptr)
def __getitem__(self, target):
"""
@ -134,7 +119,7 @@ class SpatialReference(object):
>>> print srs['UNIT|AUTHORITY', 1] # The authority value for the untis
9122
"""
if isinstance(target, TupleType):
if isinstance(target, tuple):
return self.attr_value(*target)
else:
return self.attr_value(target)
@ -149,40 +134,40 @@ class SpatialReference(object):
The attribute value for the given target node (e.g. 'PROJCS'). The index
keyword specifies an index of the child node to return.
"""
if not isinstance(target, str) or not isinstance(index, int):
if not isinstance(target, basestring) or not isinstance(index, int):
raise TypeError
return get_attr_value(self._ptr, target, index)
return capi.get_attr_value(self.ptr, target, index)
def auth_name(self, target):
"Returns the authority name for the given string target node."
return get_auth_name(self._ptr, target)
return capi.get_auth_name(self.ptr, target)
def auth_code(self, target):
"Returns the authority code for the given string target node."
return get_auth_code(self._ptr, target)
return capi.get_auth_code(self.ptr, target)
def clone(self):
"Returns a clone of this SpatialReference object."
return SpatialReference(clone_srs(self._ptr))
return SpatialReference(capi.clone_srs(self.ptr))
def from_esri(self):
"Morphs this SpatialReference from ESRI's format to EPSG."
morph_from_esri(self._ptr)
capi.morph_from_esri(self.ptr)
def identify_epsg(self):
"""
This method inspects the WKT of this SpatialReference, and will
add EPSG authority nodes where an EPSG identifier is applicable.
"""
identify_epsg(self._ptr)
capi.identify_epsg(self.ptr)
def to_esri(self):
"Morphs this SpatialReference to ESRI's format."
morph_to_esri(self._ptr)
capi.morph_to_esri(self.ptr)
def validate(self):
"Checks to see if the given spatial reference is valid."
srs_validate(self._ptr)
capi.srs_validate(self.ptr)
#### Name & SRID properties ####
@property
@ -205,25 +190,25 @@ class SpatialReference(object):
@property
def linear_name(self):
"Returns the name of the linear units."
units, name = linear_units(self._ptr, byref(c_char_p()))
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
return name
@property
def linear_units(self):
"Returns the value of the linear units."
units, name = linear_units(self._ptr, byref(c_char_p()))
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
return units
@property
def angular_name(self):
"Returns the name of the angular units."
units, name = angular_units(self._ptr, byref(c_char_p()))
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
return name
@property
def angular_units(self):
"Returns the value of the angular units."
units, name = angular_units(self._ptr, byref(c_char_p()))
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
return units
@property
@ -234,9 +219,9 @@ class SpatialReference(object):
or angular units.
"""
if self.projected or self.local:
return linear_units(self._ptr, byref(c_char_p()))
return capi.linear_units(self.ptr, byref(c_char_p()))
elif self.geographic:
return angular_units(self._ptr, byref(c_char_p()))
return capi.angular_units(self.ptr, byref(c_char_p()))
else:
return (None, None)
@ -252,17 +237,17 @@ class SpatialReference(object):
@property
def semi_major(self):
"Returns the Semi Major Axis for this Spatial Reference."
return semi_major(self._ptr, byref(c_int()))
return capi.semi_major(self.ptr, byref(c_int()))
@property
def semi_minor(self):
"Returns the Semi Minor Axis for this Spatial Reference."
return semi_minor(self._ptr, byref(c_int()))
return capi.semi_minor(self.ptr, byref(c_int()))
@property
def inverse_flattening(self):
"Returns the Inverse Flattening for this Spatial Reference."
return invflattening(self._ptr, byref(c_int()))
return capi.invflattening(self.ptr, byref(c_int()))
#### Boolean Properties ####
@property
@ -271,12 +256,12 @@ class SpatialReference(object):
Returns True if this SpatialReference is geographic
(root node is GEOGCS).
"""
return bool(isgeographic(self._ptr))
return bool(capi.isgeographic(self.ptr))
@property
def local(self):
"Returns True if this SpatialReference is local (root node is LOCAL_CS)."
return bool(islocal(self._ptr))
return bool(capi.islocal(self.ptr))
@property
def projected(self):
@ -284,40 +269,44 @@ class SpatialReference(object):
Returns True if this SpatialReference is a projected coordinate system
(root node is PROJCS).
"""
return bool(isprojected(self._ptr))
return bool(capi.isprojected(self.ptr))
#### Import Routines #####
def import_wkt(self, wkt):
"Imports the Spatial Reference from OGC WKT (string)"
from_wkt(self._ptr, byref(c_char_p(wkt)))
def import_epsg(self, epsg):
"Imports the Spatial Reference from the EPSG code (an integer)."
capi.from_epsg(self.ptr, epsg)
def import_proj(self, proj):
"Imports the Spatial Reference from a PROJ.4 string."
from_proj(self._ptr, proj)
capi.from_proj(self.ptr, proj)
def import_epsg(self, epsg):
"Imports the Spatial Reference from the EPSG code (an integer)."
from_epsg(self._ptr, epsg)
def import_user_input(self, user_input):
"Imports the Spatial Reference from the given user input string."
capi.from_user_input(self.ptr, user_input)
def import_wkt(self, wkt):
"Imports the Spatial Reference from OGC WKT (string)"
capi.from_wkt(self.ptr, byref(c_char_p(wkt)))
def import_xml(self, xml):
"Imports the Spatial Reference from an XML string."
from_xml(self._ptr, xml)
capi.from_xml(self.ptr, xml)
#### Export Properties ####
@property
def wkt(self):
"Returns the WKT representation of this Spatial Reference."
return to_wkt(self._ptr, byref(c_char_p()))
return capi.to_wkt(self.ptr, byref(c_char_p()))
@property
def pretty_wkt(self, simplify=0):
"Returns the 'pretty' representation of the WKT."
return to_pretty_wkt(self._ptr, byref(c_char_p()), simplify)
return capi.to_pretty_wkt(self.ptr, byref(c_char_p()), simplify)
@property
def proj(self):
"Returns the PROJ.4 representation for this Spatial Reference."
return to_proj(self._ptr, byref(c_char_p()))
return capi.to_proj(self.ptr, byref(c_char_p()))
@property
def proj4(self):
@ -327,34 +316,22 @@ class SpatialReference(object):
@property
def xml(self, dialect=''):
"Returns the XML representation of this Spatial Reference."
# FIXME: This leaks memory, have to figure out why.
return to_xml(self._ptr, byref(c_char_p()), dialect)
return capi.to_xml(self.ptr, byref(c_char_p()), dialect)
def to_esri(self):
"Morphs this SpatialReference to ESRI's format."
morph_to_esri(self._ptr)
def from_esri(self):
"Morphs this SpatialReference from ESRI's format to EPSG."
morph_from_esri(self._ptr)
class CoordTransform(object):
class CoordTransform(GDALBase):
"The coordinate system transformation object."
def __init__(self, source, target):
"Initializes on a source and target SpatialReference objects."
self._ptr = None # Initially NULL
if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference):
raise SRSException('source and target must be of type SpatialReference')
self._ptr = new_ct(source._ptr, target._ptr)
if not self._ptr:
raise SRSException('could not intialize CoordTransform object')
raise TypeError('source and target must be of type SpatialReference')
self.ptr = capi.new_ct(source._ptr, target._ptr)
self._srs1_name = source.name
self._srs2_name = target.name
def __del__(self):
"Deletes this Coordinate Transformation object."
if self._ptr: destroy_ct(self._ptr)
if self._ptr: capi.destroy_ct(self._ptr)
def __str__(self):
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)

View File

@ -5,16 +5,13 @@ of these tests require the use of the database.
from unittest import TestSuite, TextTestRunner
# Importing the GDAL test modules.
from django.contrib.gis.tests import \
test_gdal_driver, test_gdal_ds, test_gdal_envelope, \
test_gdal_geom, test_gdal_srs
import test_driver, test_ds, test_envelope, test_geom, test_srs
test_suites = [test_gdal_driver.suite(),
test_gdal_ds.suite(),
test_gdal_envelope.suite(),
test_gdal_geom.suite(),
test_gdal_srs.suite(),
test_suites = [test_driver.suite(),
test_ds.suite(),
test_envelope.suite(),
test_geom.suite(),
test_srs.suite(),
]
def suite():

View File

@ -1,10 +1,13 @@
import os, os.path, unittest
from django.contrib.gis.gdal import DataSource, Envelope, OGRException, OGRIndexError
from django.contrib.gis.gdal.field import OFTReal, OFTInteger, OFTString
from django.contrib import gis
# Path for SHP files
data_path = os.path.join(os.path.dirname(__file__), 'data')
data_path = os.path.join(os.path.dirname(gis.__file__), 'tests' + os.sep + 'data')
def get_ds_file(name, ext):
return os.sep.join([data_path, name, name + '.%s' % ext])
# Test SHP data source object

View File

@ -0,0 +1,94 @@
import unittest
from django.contrib.gis.gdal import Envelope, OGRException
class TestPoint(object):
def __init__(self, x, y):
self.x = x
self.y = y
class EnvelopeTest(unittest.TestCase):
def setUp(self):
self.e = Envelope(0, 0, 5, 5)
def test01_init(self):
"Testing Envelope initilization."
e1 = Envelope((0, 0, 5, 5))
e2 = Envelope(0, 0, 5, 5)
e3 = Envelope(0, '0', '5', 5) # Thanks to ww for this
e4 = Envelope(e1._envelope)
self.assertRaises(OGRException, Envelope, (5, 5, 0, 0))
self.assertRaises(OGRException, Envelope, 5, 5, 0, 0)
self.assertRaises(OGRException, Envelope, (0, 0, 5, 5, 3))
self.assertRaises(OGRException, Envelope, ())
self.assertRaises(ValueError, Envelope, 0, 'a', 5, 5)
self.assertRaises(TypeError, Envelope, u'foo')
self.assertRaises(OGRException, Envelope, (1, 1, 0, 0))
try:
Envelope(0, 0, 0, 0)
except OGRException:
self.fail("shouldn't raise an exception for min_x == max_x or min_y == max_y")
def test02_properties(self):
"Testing Envelope properties."
e = Envelope(0, 0, 2, 3)
self.assertEqual(0, e.min_x)
self.assertEqual(0, e.min_y)
self.assertEqual(2, e.max_x)
self.assertEqual(3, e.max_y)
self.assertEqual((0, 0), e.ll)
self.assertEqual((2, 3), e.ur)
self.assertEqual((0, 0, 2, 3), e.tuple)
self.assertEqual('POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))', e.wkt)
self.assertEqual('(0.0, 0.0, 2.0, 3.0)', str(e))
def test03_equivalence(self):
"Testing Envelope equivalence."
e1 = Envelope(0.523, 0.217, 253.23, 523.69)
e2 = Envelope((0.523, 0.217, 253.23, 523.69))
self.assertEqual(e1, e2)
self.assertEqual((0.523, 0.217, 253.23, 523.69), e1)
def test04_expand_to_include_pt_2_params(self):
"Testing Envelope expand_to_include -- point as two parameters."
self.e.expand_to_include(2, 6)
self.assertEqual((0, 0, 5, 6), self.e)
self.e.expand_to_include(-1, -1)
self.assertEqual((-1, -1, 5, 6), self.e)
def test05_expand_to_include_pt_2_tuple(self):
"Testing Envelope expand_to_include -- point as a single 2-tuple parameter."
self.e.expand_to_include((10, 10))
self.assertEqual((0, 0, 10, 10), self.e)
self.e.expand_to_include((-10, -10))
self.assertEqual((-10, -10, 10, 10), self.e)
def test06_expand_to_include_extent_4_params(self):
"Testing Envelope expand_to_include -- extent as 4 parameters."
self.e.expand_to_include(-1, 1, 3, 7)
self.assertEqual((-1, 0, 5, 7), self.e)
def test06_expand_to_include_extent_4_tuple(self):
"Testing Envelope expand_to_include -- extent as a single 4-tuple parameter."
self.e.expand_to_include((-1, 1, 3, 7))
self.assertEqual((-1, 0, 5, 7), self.e)
def test07_expand_to_include_envelope(self):
"Testing Envelope expand_to_include with Envelope as parameter."
self.e.expand_to_include(Envelope(-1, 1, 3, 7))
self.assertEqual((-1, 0, 5, 7), self.e)
def test08_expand_to_include_point(self):
"Testing Envelope expand_to_include with Point as parameter."
self.e.expand_to_include(TestPoint(-1, 1))
self.assertEqual((-1, 0, 5, 5), self.e)
self.e.expand_to_include(TestPoint(10, 10))
self.assertEqual((-1, 0, 10, 10), self.e)
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(EnvelopeTest))
return s
def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite())

View File

@ -7,7 +7,7 @@ from django.contrib.gis.tests.geometries import *
class OGRGeomTest(unittest.TestCase):
"This tests the OGR Geometry."
def test00_geomtype(self):
def test00a_geomtype(self):
"Testing OGRGeomType object."
# OGRGeomType should initialize on all these inputs.
@ -22,9 +22,9 @@ class OGRGeomTest(unittest.TestCase):
self.fail('Could not create an OGRGeomType object!')
# Should throw TypeError on this input
self.assertRaises(TypeError, OGRGeomType.__init__, 23)
self.assertRaises(TypeError, OGRGeomType.__init__, 'fooD')
self.assertRaises(TypeError, OGRGeomType.__init__, 9)
self.assertRaises(OGRException, OGRGeomType, 23)
self.assertRaises(OGRException, OGRGeomType, 'fooD')
self.assertRaises(OGRException, OGRGeomType, 9)
# Equivalence can take strings, ints, and other OGRGeomTypes
self.assertEqual(True, OGRGeomType(1) == OGRGeomType(1))
@ -38,9 +38,14 @@ class OGRGeomTest(unittest.TestCase):
# Testing the Django field name equivalent property.
self.assertEqual('PointField', OGRGeomType('Point').django)
self.assertEqual(None, OGRGeomType('Unknown').django)
self.assertEqual('GeometryField', OGRGeomType('Unknown').django)
self.assertEqual(None, OGRGeomType('none').django)
# 'Geometry' initialization implies an unknown geometry type.
gt = OGRGeomType('Geometry')
self.assertEqual(0, gt.num)
self.assertEqual('Unknown', gt.name)
def test01a_wkt(self):
"Testing WKT output."
for g in wkt_out:
@ -165,6 +170,12 @@ class OGRGeomTest(unittest.TestCase):
def test07a_polygons(self):
"Testing Polygon objects."
# Testing `from_bbox` class method
bbox = (-180,-90,180,90)
p = OGRGeometry.from_bbox( bbox )
self.assertEqual(bbox, p.extent)
prev = OGRGeometry('POINT(0 0)')
for p in polygons:
poly = OGRGeometry(p.wkt)

View File

@ -9,42 +9,45 @@ def geo_suite():
"""
from django.conf import settings
from django.contrib.gis.tests.utils import mysql, oracle, postgis
from django.contrib.gis.gdal import HAS_GDAL
from django.contrib.gis.utils import HAS_GEOIP
from django.contrib.gis import gdal, utils
# Tests that require use of a spatial database (e.g., creation of models)
test_models = ['geoapp',]
# The test suite.
s = unittest.TestSuite()
# Tests that do not require setting up and tearing down a spatial database.
# Adding the GEOS tests. (__future__)
#from django.contrib.gis.geos import tests as geos_tests
#s.addTest(geos_tests.suite())
# Test apps that require use of a spatial database (e.g., creation of models)
test_apps = ['geoapp', 'relatedapp']
if oracle or postgis:
test_apps.append('distapp')
# Tests that do not require setting up and tearing down a spatial database
# and are modules in `django.contrib.gis.tests`.
test_suite_names = [
'test_geos',
'test_measure',
]
if HAS_GDAL:
if oracle or postgis:
test_models += ['distapp', 'layermap', 'relatedapp']
elif mysql:
test_models += ['relatedapp', 'layermap']
test_suite_names += [
'test_gdal_driver',
'test_gdal_ds',
'test_gdal_envelope',
'test_gdal_geom',
'test_gdal_srs',
'test_spatialrefsys',
]
if gdal.HAS_GDAL:
# These tests require GDAL.
test_suite_names.append('test_spatialrefsys')
test_apps.append('layermap')
# Adding the GDAL tests.
from django.contrib.gis.gdal import tests as gdal_tests
s.addTest(gdal_tests.suite())
else:
print >>sys.stderr, "GDAL not available - no GDAL tests will be run."
if HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'):
if utils.HAS_GEOIP and hasattr(settings, 'GEOIP_PATH'):
test_suite_names.append('test_geoip')
s = unittest.TestSuite()
for test_suite in test_suite_names:
tsuite = getattr(__import__('django.contrib.gis.tests', globals(), locals(), [test_suite]),test_suite)
for suite_name in test_suite_names:
tsuite = getattr(__import__('django.contrib.gis.tests', globals(), locals(), [suite_name]), suite_name)
s.addTest(tsuite.suite())
return s, test_models
return s, test_apps
def run_gis_tests(test_labels, **kwargs):
"""
@ -80,9 +83,9 @@ def run_gis_tests(test_labels, **kwargs):
# Creating the test suite, adding the test models to INSTALLED_APPS, and
# adding the model test suites to our suite package.
gis_suite, test_models = geo_suite()
for test_model in test_models:
module_name = 'django.contrib.gis.tests.%s' % test_model
gis_suite, test_apps = geo_suite()
for test_app in test_apps:
module_name = 'django.contrib.gis.tests.%s' % test_app
if mysql:
test_module_name = 'tests_mysql'
else:
@ -90,13 +93,13 @@ def run_gis_tests(test_labels, **kwargs):
new_installed.append(module_name)
# Getting the model test suite
tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_model, globals(), locals(), [test_module_name]),
tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_app, globals(), locals(), [test_module_name]),
test_module_name)
gis_suite.addTest(tsuite.suite())
# Resetting the loaded flag to take into account what we appended to
# the INSTALLED_APPS (since this routine is invoked through
# django/core/management, it caches the apps; this ensures that syncdb
# Resetting the loaded flag to take into account what we appended to
# the INSTALLED_APPS (since this routine is invoked through
# django/core/management, it caches the apps; this ensures that syncdb
# will see our appended models)
settings.INSTALLED_APPS = new_installed
loading.cache.loaded = False
@ -198,3 +201,25 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[], suite=
# Returning the total failures and errors
return len(result.failures) + len(result.errors)
# Class for creating a fake module with a run method. This is for the
# GEOS and GDAL tests that were moved to their respective modules.
class _DeprecatedTestModule(object):
def __init__(self, tests, mod):
self.tests = tests
self.mod = mod
def run(self):
from warnings import warn
warn('This test module is deprecated because it has moved to ' \
'`django.contrib.gis.%s.tests` and will disappear in 1.2.' %
self.mod, DeprecationWarning)
self.tests.run()
#from django.contrib.gis.geos import tests as _tests
#test_geos = _DeprecatedTestModule(_tests, 'geos')
from django.contrib.gis.gdal import HAS_GDAL
if HAS_GDAL:
from django.contrib.gis.gdal import tests as _tests
test_gdal = _DeprecatedTestModule(_tests, 'gdal')

View File

@ -1,45 +0,0 @@
import unittest
from django.contrib.gis.gdal import Envelope, OGRException
class EnvelopeTest(unittest.TestCase):
def test01_init(self):
"Testing Envelope initilization."
e1 = Envelope((0, 0, 5, 5))
e2 = Envelope(0, 0, 5, 5)
e3 = Envelope(0, '0', '5', 5) # Thanks to ww for this
e4 = Envelope(e1._envelope)
self.assertRaises(OGRException, Envelope, (5, 5, 0, 0))
self.assertRaises(OGRException, Envelope, 5, 5, 0, 0)
self.assertRaises(OGRException, Envelope, (0, 0, 5, 5, 3))
self.assertRaises(OGRException, Envelope, ())
self.assertRaises(ValueError, Envelope, 0, 'a', 5, 5)
self.assertRaises(TypeError, Envelope, u'foo')
def test02_properties(self):
"Testing Envelope properties."
e = Envelope(0, 0, 2, 3)
self.assertEqual(0, e.min_x)
self.assertEqual(0, e.min_y)
self.assertEqual(2, e.max_x)
self.assertEqual(3, e.max_y)
self.assertEqual((0, 0), e.ll)
self.assertEqual((2, 3), e.ur)
self.assertEqual((0, 0, 2, 3), e.tuple)
self.assertEqual('POLYGON((0.0 0.0,0.0 3.0,2.0 3.0,2.0 0.0,0.0 0.0))', e.wkt)
self.assertEqual('(0.0, 0.0, 2.0, 3.0)', str(e))
def test03_equivalence(self):
"Testing Envelope equivalence."
e1 = Envelope(0.523, 0.217, 253.23, 523.69)
e2 = Envelope((0.523, 0.217, 253.23, 523.69))
self.assertEqual(e1, e2)
self.assertEqual((0.523, 0.217, 253.23, 523.69), e1)
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(EnvelopeTest))
return s
def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite())