1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

gis: gdal: refactor of the GDAL ctypes interface

(1) All interactions with the GDAL library take place through predefined ctypes prototypes, abstracting away error-checking.
 (2) Fixed memory leaks by properly freeing pointers allocated w/in GDAL.
 (3) Improved OFTField support, and added support for the OGR date/time fields.
 (4) Significantly improved the OGRGeometry tests.


git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6686 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-11-17 21:38:36 +00:00
parent f66821deae
commit ef32f913a0
20 changed files with 1226 additions and 617 deletions

View File

@ -1,13 +1,3 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, c_void_p, byref, string_at
# The GDAL C library, OGR exceptions, and the Layer object.
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, check_err
from django.contrib.gis.gdal.layer import Layer
from django.contrib.gis.gdal.driver import Driver
"""
DataSource is a wrapper for the OGR Data Source object, which provides
an interface for reading vector geometry data from many different file
@ -43,63 +33,81 @@ from django.contrib.gis.gdal.driver import Driver
# OFTReal returns floats, all else returns string.
val = field.value
"""
# ctypes prerequisites.
from ctypes import byref, c_void_p
# The GDAL C library, OGR exceptions, and the Layer object.
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
# 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):
"Wraps an OGR Data Source object."
#### Python 'magic' routines ####
def __init__(self, ds_input, ds_driver=False):
def __init__(self, ds_input, ds_driver=False, write=False):
self._ds = None # Initially NULL
# DataSource pointer is initially NULL.
self._ptr = None
# The write flag.
if write:
self._write = 1
else:
self._write = 0
# Registering all the drivers, this needs to be done
# _before_ we try to open up a data source.
if not lgdal.OGRGetDriverCount() and not lgdal.OGRRegisterAll():
if not get_driver_count() and not register_all():
raise OGRException('Could not register all the OGR data source drivers!')
if isinstance(ds_input, StringType):
if isinstance(ds_input, basestring):
# The data source driver is a void pointer.
ds_driver = c_void_p()
# OGROpen will auto-detect the data source type.
ds = lgdal.OGROpen(c_char_p(ds_input), c_int(0), byref(ds_driver))
ds = open_ds(ds_input, self._write, byref(ds_driver))
elif isinstance(ds_input, c_void_p) and isinstance(ds_driver, c_void_p):
ds = ds_input
else:
raise OGRException('Invalid data source input type: %s' % str(type(ds_input)))
raise OGRException('Invalid data source input type: %s' % type(ds_input))
# Raise an exception if the returned pointer is NULL
if not ds:
self._ds = False
raise OGRException('Invalid data source file "%s"' % ds_input)
else:
self._ds = ds
if bool(ds):
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):
"This releases the reference to the data source (destroying it if it's the only one)."
if self._ds: lgdal.OGRReleaseDataSource(self._ds)
"Destroys this DataStructure object."
if self._ptr: destroy_ds(self._ptr)
def __iter__(self):
"Allows for iteration over the layers in a data source."
for i in xrange(self.layer_count):
yield self.__getitem__(i)
yield self[i]
def __getitem__(self, index):
"Allows use of the index [] operator to get a layer at the index."
if isinstance(index, StringType):
l = lgdal.OGR_DS_GetLayerByName(self._ds, c_char_p(index))
if isinstance(index, basestring):
l = get_layer_by_name(self._ptr, index)
if not l: raise OGRIndexError('invalid OGR Layer name given: "%s"' % index)
else:
elif isinstance(index, int):
if index < 0 or index >= self.layer_count:
raise OGRIndexError('index out of range')
l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index))
l = get_layer(self._ptr, index)
else:
raise TypeError('Invalid index type: %s' % type(index))
return Layer(l)
def __len__(self):
@ -119,10 +127,9 @@ class DataSource(object):
@property
def layer_count(self):
"Returns the number of layers in the data source."
return lgdal.OGR_DS_GetLayerCount(self._ds)
return get_layer_count(self._ptr)
@property
def name(self):
"Returns the name of the data source."
return string_at(lgdal.OGR_DS_GetName(self._ds))
return get_ds_name(self._ptr)

View File

@ -1,16 +1,13 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, c_void_p, byref, string_at
# The GDAL C library, OGR exceptions, and the Layer object.
from django.contrib.gis.gdal.libgdal import lgdal
# prerequisites imports
from ctypes import c_void_p
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
# 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):
"Wraps an OGR Data Source Driver."
@ -22,64 +19,49 @@ class Driver(object):
'tiger/line' : 'TIGER',
}
def __init__(self, input, ptr=False):
def __init__(self, dr_input):
"Initializes an OGR driver on either a string or integer input."
if isinstance(input, StringType):
if isinstance(dr_input, basestring):
# If a string name of the driver was passed in
self._dr = None # Initially NULL
self._ptr = None # Initially NULL
self._register()
# Checking the alias dictionary (case-insensitive) to see if an alias
# exists for the given driver.
if input.lower() in self._alias:
name = c_char_p(self._alias[input.lower()])
if dr_input.lower() in self._alias:
name = self._alias[dr_input.lower()]
else:
name = c_char_p(input)
name = dr_input
# Attempting to get the OGR driver by the string name.
dr = lgdal.OGRGetDriverByName(name)
elif isinstance(input, int):
dr = get_driver_by_name(name)
elif isinstance(dr_input, int):
self._register()
dr = lgdal.OGRGetDriver(c_int(input))
elif isinstance(input, c_void_p):
dr = input
dr = get_driver(dr_input)
elif isinstance(dr_input, c_void_p):
dr = dr_input
else:
raise OGRException('Unrecognized input type for OGR Driver: %s' % str(type(input)))
raise OGRException('Unrecognized input type for OGR Driver: %s' % str(type(dr_input)))
# 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(input))
self._dr = dr
raise OGRException('Could not initialize OGR Driver on input: %s' % str(dr_input))
self._ptr = dr
def __str__(self):
"Returns the string name of the OGR Driver."
return string_at(lgdal.OGR_Dr_GetName(self._dr))
return 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 and not lgdal.OGRRegisterAll():
if not self.driver_count and not register_all():
raise OGRException('Could not register all the OGR data source drivers!')
# Driver properties
@property
def driver_count(self):
"Returns the number of OGR data source drivers registered."
return lgdal.OGRGetDriverCount()
def create_ds(self, **kwargs):
"Creates a data source using the keyword args as name value options."
raise NotImplementedError
# Getting the options string
#options = ''
#n_opts = len(kwargs)
#for i in xrange(n_opts):
# options += '%s=%s' % (str(k), str(v))
# if i < n_opts-1: options += ','
#opts = c_char_p(options)
return get_driver_count()

View File

@ -9,7 +9,6 @@
| |
| |
Lower left (min_x, min_y) o----------+
"""
from ctypes import Structure, c_double
from types import TupleType, ListType

View File

@ -3,8 +3,7 @@
check_err() routine which checks the status code returned by
OGR methods.
"""
# OGR & SRS Exceptions
#### OGR & SRS Exceptions ####
class OGRException(Exception): pass
class SRSException(Exception): pass
class OGRIndexError(OGRException, KeyError):
@ -16,6 +15,8 @@ class OGRIndexError(OGRException, KeyError):
"""
silent_variable_failure = True
#### OGR error checking codes and routine ####
# OGR Error Codes
OGRERR_DICT = { 1 : (OGRException, 'Not enough data.'),
2 : (OGRException, 'Not enough memory.'),
@ -36,4 +37,4 @@ def check_err(code):
e, msg = OGRERR_DICT[code]
raise e, msg
else:
raise OGRException, 'Unknown error code: "%s"' % code
raise OGRException('Unknown error code: "%s"' % code)

View File

@ -1,14 +1,17 @@
# types and ctypes
from types import StringType
from ctypes import c_char_p, c_int, c_void_p, string_at
# The GDAL C library, OGR exception, and the Field object
from django.contrib.gis.gdal.libgdal import lgdal
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
from django.contrib.gis.gdal.prototypes.geom import clone_geom, get_geom_srs
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
@ -18,33 +21,31 @@ class Feature(object):
#### Python 'magic' routines ####
def __init__(self, feat, fdefn):
"Needs a C pointer (Python integer in ctypes) in order to initialize."
self._feat = None # Initially NULL
self._fdefn = None
"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._feat = feat
self._ptr = feat
self._fdefn = fdefn
def __del__(self):
"Releases a reference to this object."
if self._feat: lgdal.OGR_F_Destroy(self._feat)
if self._ptr: destroy_feature(self._ptr)
def __getitem__(self, index):
"Gets the Field at the specified index."
if isinstance(index, StringType):
if isinstance(index, basestring):
i = self.index(index)
else:
if index < 0 or index > self.num_fields:
raise OGRIndexError('index out of range')
i = index
return Field(lgdal.OGR_F_GetFieldDefnRef(self._feat, c_int(i)),
string_at(lgdal.OGR_F_GetFieldAsString(self._feat, c_int(i))))
return Field(self._ptr, i)
def __iter__(self):
"Iterates over each field in the Feature."
for i in xrange(self.num_fields):
yield self.__getitem__(i)
yield self[i]
def __len__(self):
"Returns the count of fields in this feature."
@ -56,54 +57,49 @@ class Feature(object):
def __eq__(self, other):
"Does equivalence testing on the features."
if lgdal.OGR_F_Equal(self._feat, other._feat):
return True
else:
return False
return bool(feature_equal(self._ptr, other._ptr))
#### Feature Properties ####
@property
def fid(self):
"Returns the feature identifier."
return lgdal.OGR_F_GetFID(self._feat)
return get_fid(self._ptr)
@property
def layer_name(self):
"Returns the name of the layer for the feature."
return string_at(lgdal.OGR_FD_GetName(self._fdefn))
return get_feat_name(self._fdefn)
@property
def num_fields(self):
"Returns the number of fields in the Feature."
return lgdal.OGR_F_GetFieldCount(self._feat)
return get_feat_field_count(self._ptr)
@property
def fields(self):
"Returns a list of fields in the Feature."
return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._fdefn, i)))
return [get_field_name(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 = lgdal.OGR_F_GetGeometryRef(self._feat)
if not geom_ptr:
raise OGRException('Cannot retrieve Geometry from the feature.')
geom_ptr = get_feat_geom_ref(self._ptr)
# Attempting to retrieve the Spatial Reference for the geometry.
srs_ptr = lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(geom_ptr))
if srs_ptr:
srs = SpatialReference(srs_ptr, 'ogr')
else:
try:
srs_ptr = get_geom_srs(geom_ptr)
srs = SpatialReference(clone_srs(srs_ptr))
except OGRException:
srs = None
# Geometry is cloned so the feature isn't invalidated.
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(geom_ptr)), srs)
return OGRGeometry(clone_geom(geom_ptr), srs)
@property
def geom_type(self):
"Returns the OGR Geometry Type for this Feture."
return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._fdefn))
return OGRGeomType(get_fd_geom_type(self._fdefn))
#### Feature Methods ####
def get(self, field):
@ -113,14 +109,10 @@ class Feature(object):
parameters.
"""
field_name = getattr(field, 'name', field)
return self.__getitem__(field_name).value
return self[field_name].value
def index(self, field_name):
"Returns the index of the given field name."
i = lgdal.OGR_F_GetFieldIndex(self._feat, c_char_p(field_name))
i = get_field_index(self._ptr, field_name)
if i < 0: raise OGRIndexError('invalid OFT field name given: "%s"' % field_name)
return i
def clone(self):
"Clones this Feature."
return Feature(lgdal.OGR_F_Clone(self._feat))

View File

@ -1,6 +1,10 @@
from ctypes import string_at
from django.contrib.gis.gdal.libgdal import lgdal
from ctypes import byref, c_int
from datetime import date, datetime, time
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
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
@ -10,70 +14,145 @@ class Field(object):
"A class that wraps an OGR Field, needs to be instantiated from a Feature object."
#### Python 'magic' routines ####
def __init__(self, fld, val=''):
"Needs a C pointer (Python integer in ctypes) in order to initialize."
self._fld = None # Initially NULL
def __init__(self, feat, index):
"""
Initializes on the feature pointer and the integer index of
the field within the feature.
"""
# Setting the feature pointer and index.
self._feat = feat
self._index = index
# Getting the pointer for this field.
fld = get_feat_field_defn(feat, index)
if not fld:
raise OGRException('Cannot create OGR Field, invalid pointer given.')
self._fld = fld
self._val = val
self._ptr = fld
# Setting the class depending upon the OGR Field Type (OFT)
self.__class__ = FIELD_CLASSES[self.type]
# OFTReal with no precision should be an OFTInteger.
if isinstance(self, OFTReal) and self.precision == 0:
self.__class__ = OFTInteger
def __str__(self):
"Returns the string representation of the Field."
return '%s (%s)' % (self.name, self.value)
return str(self.value).strip()
#### Field Methods ####
def as_double(self):
"Retrieves the Field's value as a double (float)."
return 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)
def as_string(self):
"Retrieves the Field's value as a string."
return 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))
if status:
return (yy, mm, dd, hh, mn, ss, tz)
else:
raise OGRException('Unable to retrieve date & time information from the field.')
#### Field Properties ####
@property
def name(self):
"Returns the name of the field."
return string_at(lgdal.OGR_Fld_GetNameRef(self._fld))
"Returns the name of this Field."
return get_field_name(self._ptr)
@property
def precision(self):
"Returns the precision of this Field."
return get_field_precision(self._ptr)
@property
def type(self):
"Returns the type of this field."
return lgdal.OGR_Fld_GetType(self._fld)
"Returns the OGR type of this Field."
return 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)
@property
def value(self):
"Returns the value of this type of field."
return self._val
"Returns the value of this Field."
# Default is to get the field as a string.
return self.as_string()
# The Field sub-classes for each OGR Field type.
@property
def width(self):
"Returns the width of this Field."
return get_field_width(self._ptr)
### The Field sub-classes for each OGR Field type. ###
class OFTInteger(Field):
@property
def value(self):
"Returns an integer contained in this field."
try:
return int(self._val)
except ValueError:
return None
class OFTIntegerList(Field): pass
return self.as_int()
@property
def type(self):
"""
GDAL uses OFTReals to represent OFTIntegers in created
shapefiles -- forcing the type here since the underlying field
type may actually be OFTReal.
"""
return 0
class OFTReal(Field):
@property
def value(self):
"Returns a float contained in this field."
try:
return float(self._val)
except ValueError:
return None
class OFTRealList(Field): pass
return self.as_double()
class OFTString(Field):
def __str__(self):
return '%s ("%s")' % (self.name, self.value)
class OFTStringList(Field): pass
# String & Binary fields, just subclasses
class OFTString(Field): pass
class OFTWideString(Field): pass
class OFTWideStringList(Field): pass
class OFTBinary(Field): pass
class OFTDate(Field): pass
class OFTTime(Field): pass
class OFTDateTime(Field): pass
# OFTDate, OFTTime, OFTDateTime fields.
class OFTDate(Field):
@property
def value(self):
"Returns a Python `date` object for the OFTDate field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
return date(yy.value, mm.value, dd.value)
class OFTDateTime(Field):
@property
def value(self):
"Returns a Python `datetime` object for this OFTDateTime field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
# TODO: Adapt timezone information.
# See http://lists.maptools.org/pipermail/gdal-dev/2006-February/007990.html
# The `tz` variable has values of: 0=unknown, 1=localtime (ambiguous),
# 100=GMT, 104=GMT+1, 80=GMT-5, etc.
return datetime(yy.value, mm.value, dd.value, hh.value, mn.value, ss.value)
class OFTTime(Field):
@property
def value(self):
"Returns a Python `time` object for this OFTTime field."
yy, mm, dd, hh, mn, ss, tz = self.as_datetime()
return time(hh.value, mn.value, ss.value)
# List fields are also just subclasses
class OFTIntegerList(Field): pass
class OFTRealList(Field): pass
class OFTStringList(Field): pass
class OFTWideStringList(Field): pass
# Class mapping dictionary for OFT Types
FIELD_CLASSES = { 0 : OFTInteger,

View File

@ -38,42 +38,30 @@
>>> print gt1 == 3, gt1 == 'Polygon' # Equivalence works w/non-OGRGeomType objects
True
"""
# Python library imports
# Python library requisites.
import re, sys
from binascii import a2b_hex, b2a_hex
from ctypes import byref, create_string_buffer, string_at, c_char_p, c_double, c_int, c_void_p
from binascii import a2b_hex
from ctypes import byref, string_at, c_char_p, c_double, c_ubyte, c_void_p
from types import BufferType, IntType, StringType, UnicodeType
# Getting GDAL prerequisites
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import check_err, OGRException, OGRIndexError
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
# For more information, see the OGR C API source code:
# http://www.gdal.org/ogr/ogr__api_8h.html
#
# The OGR_G_* routines are relevant here.
#### ctypes prototypes for functions that return double values ####
def pnt_func(f):
"For accessing point information."
f.restype = c_double
f.argtypes = [c_void_p, c_int]
return f
# GetX, GetY, GetZ all return doubles.
getx = pnt_func(lgdal.OGR_G_GetX)
gety = pnt_func(lgdal.OGR_G_GetY)
getz = pnt_func(lgdal.OGR_G_GetZ)
# GetArea returns a double.
get_area = lgdal.OGR_G_GetArea
get_area.restype = c_double
get_area.argtypes = [c_void_p]
# Regular expression for determining whether the input is HEXEWKB.
# Regular expressions for recognizing HEXEWKB and WKT.
hex_regex = re.compile(r'^[0-9A-F]+$', re.I)
wkt_regex = re.compile(r'^(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)[ACEGIMLONPSRUTY\d,\.\-\(\) ]+$', re.I)
#### OGRGeometry Class ####
class OGRGeometry(object):
@ -82,7 +70,7 @@ class OGRGeometry(object):
def __init__(self, geom_input, srs=None):
"Initializes Geometry on either WKT or an OGR pointer as input."
self._g = c_void_p(None) # Initially NULL
self._ptr = c_void_p(None) # Initially NULL
# Checking if unicode
if isinstance(geom_input, UnicodeType):
@ -94,52 +82,47 @@ class OGRGeometry(object):
geom_input = buffer(a2b_hex(geom_input.upper()))
if isinstance(geom_input, StringType):
# First, trying the input as WKT
buf = c_char_p(geom_input)
g = c_void_p()
try:
check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), c_void_p(), byref(g)))
except OGRException:
try:
m = wkt_regex.match(geom_input)
if m:
if 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(m.group('type')).num)
import_wkt(g, byref(c_char_p(geom_input)))
else:
g = from_wkt(byref(c_char_p(geom_input)), None, byref(c_void_p()))
else:
# Seeing if the input is a valid short-hand string
# (e.g., 'Point', 'POLYGON').
ogr_t = OGRGeomType(geom_input)
g = lgdal.OGR_G_CreateGeometry(ogr_t.num)
except:
raise OGRException('Could not initialize OGR Geometry from: %s' % geom_input)
g = create_geom(OGRGeomType(geom_input).num)
elif isinstance(geom_input, BufferType):
# WKB was passed in
g = c_void_p()
check_err(lgdal.OGR_G_CreateFromWkb(c_char_p(str(geom_input)), c_void_p(), byref(g), len(geom_input)))
g = 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 = lgdal.OGR_G_CreateGeometry(geom_input.num)
g = create_geom(geom_input.num)
elif isinstance(geom_input, c_void_p):
# OGR pointer (c_void_p) was the input.
g = geom_input
else:
raise OGRException('Type of input cannot be determined!')
# Assigning the SpatialReference object to the geometry, if valid.
if bool(srs):
if isinstance(srs, SpatialReference):
srs_ptr = srs._srs
else:
sr = SpatialReference(srs)
srs_ptr = sr._srs
lgdal.OGR_G_AssignSpatialReference(g, srs_ptr)
raise OGRException('Invalid input type for OGR Geometry construction: %s' % type(geom_input))
# Now checking the Geometry pointer before finishing initialization
# by setting the pointer for the object.
if not g:
raise OGRException('Cannot create OGR Geometry from input: %s' % str(geom_input))
self._g = g
self._ptr = g
# Assigning the SpatialReference object to the geometry, if valid.
if bool(srs): self.srs = srs
# Setting the class depending upon the OGR Geometry Type
self.__class__ = GEO_CLASSES[self.geom_type.num]
def __del__(self):
"Deletes this Geometry."
if self._g: lgdal.OGR_G_DestroyGeometry(self._g)
if self._ptr: destroy_geom(self._ptr)
### Geometry set-like operations ###
# g = g1 | g2
@ -178,71 +161,74 @@ class OGRGeometry(object):
@property
def dimension(self):
"Returns 0 for points, 1 for lines, and 2 for surfaces."
return lgdal.OGR_G_GetDimension(self._g)
return get_dims(self._ptr)
@property
def coord_dim(self):
"Returns the coordinate dimension of the Geometry."
return lgdal.OGR_G_GetCoordinateDimension(self._g)
return get_coord_dims(self._ptr)
@property
def geom_count(self):
"The number of elements in this Geometry."
return lgdal.OGR_G_GetGeometryCount(self._g)
return get_geom_count(self._ptr)
@property
def point_count(self):
"Returns the number of Points in this Geometry."
return lgdal.OGR_G_GetPointCount(self._g)
return get_point_count(self._ptr)
@property
def num_points(self):
"Alias for `point_count` (same name method in GEOS API.)"
return self.point_count
@property
def num_coords(self):
"Returns the number of Points in this Geometry."
"Alais for `point_count`."
return self.point_count
@property
def geom_type(self):
"Returns the Type for this Geometry."
return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g))
return OGRGeomType(get_geom_type(self._ptr))
@property
def geom_name(self):
"Returns the Name of this Geometry."
return string_at(lgdal.OGR_G_GetGeometryName(self._g))
return get_geom_name(self._ptr)
@property
def area(self):
"Returns the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
return get_area(self._g)
return get_area(self._ptr)
@property
def envelope(self):
"Returns the envelope for this Geometry."
env = OGREnvelope()
lgdal.OGR_G_GetEnvelope(self._g, byref(env))
return Envelope(env)
return Envelope(get_envelope(self._ptr, byref(OGREnvelope())))
#### SpatialReference-related Properties ####
# The SRS property
def get_srs(self):
"Returns the Spatial Reference for this Geometry."
srs_ptr = lgdal.OGR_G_GetSpatialReference(self._g)
if srs_ptr:
return SpatialReference(lgdal.OSRClone(srs_ptr), 'ogr')
else:
try:
srs_ptr = get_geom_srs(self._ptr)
return SpatialReference(clone_srs(srs_ptr))
except SRSException:
return None
def set_srs(self, srs):
"Sets the SpatialReference for this geometry."
if isinstance(srs, SpatialReference):
srs_ptr = lgdal.OSRClone(srs._srs)
srs_ptr = clone_srs(srs._ptr)
elif isinstance(srs, (StringType, UnicodeType, IntType)):
sr = SpatialReference(srs)
srs_ptr = lgdal.OSRClone(sr._srs)
srs_ptr = clone_srs(sr._ptr)
else:
raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
lgdal.OGR_G_AssignSpatialReference(self._g, srs_ptr)
assign_srs(self._ptr, srs_ptr)
srs = property(get_srs, set_srs)
@ -260,151 +246,168 @@ class OGRGeometry(object):
srid = property(get_srid, set_srid)
#### Output Methods ####
@property
def geos(self):
"Returns a GEOSGeometry object from this OGRGeometry."
from django.contrib.gis.geos import GEOSGeometry
return GEOSGeometry(self.wkb, self.srid)
@property
def gml(self):
"Returns the GML representation of the Geometry."
buf = lgdal.OGR_G_ExportToGML(self._g)
if buf: return string_at(buf)
else: return None
return to_gml(self._ptr)
@property
def hex(self):
"Returns the hexadecimal representation of the WKB (a string)."
return b2a_hex(self.wkb).upper()
return str(self.wkb).encode('hex').upper()
#return b2a_hex(self.wkb).upper()
@property
def wkb_size(self):
"Returns the size of the WKB buffer."
return lgdal.OGR_G_WkbSize(self._g)
return get_wkbsize(self._ptr)
@property
def wkb(self):
"Returns the WKB representation of the Geometry."
if sys.byteorder == 'little':
byteorder = c_int(1) # wkbNDR (from ogr_core.h)
byteorder = 1 # wkbNDR (from ogr_core.h)
else:
byteorder = c_int(0) # wkbXDR (from ogr_core.h)
# Creating a mutable string buffer of the given size, exporting
# to WKB, and returning a Python buffer of the WKB.
byteorder = 0 # wkbXDR
sz = self.wkb_size
wkb = create_string_buffer(sz)
check_err(lgdal.OGR_G_ExportToWkb(self._g, byteorder, byref(wkb)))
return buffer(string_at(wkb, sz))
# Creating the unsigned character buffer, and passing it in by reference.
buf = (c_ubyte * sz)()
wkb = 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."
buf = c_char_p()
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf)))
return string_at(buf)
return to_wkt(self._ptr, byref(c_char_p()))
#### Geometry Methods ####
def clone(self):
"Clones this OGR Geometry."
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(self._g)))
return OGRGeometry(clone_geom(self._ptr), self.srs)
def close_rings(self):
"""If there are any rings within this geometry that have not been
"""
If there are any rings within this geometry that have not been
closed, this routine will do so by adding the starting point at the
end."""
end.
"""
# Closing the open rings.
lgdal.OGR_G_CloseRings(self._g)
geom_close_rings(self._ptr)
def transform(self, coord_trans):
"Transforms this Geometry with the given CoordTransform object."
if not isinstance(coord_trans, CoordTransform):
raise OGRException('CoordTransform object required for transform.')
check_err(lgdal.OGR_G_Transform(self._g, coord_trans._ct))
"""
Transforms this geometry to a different spatial reference system. May take
either a CoordTransform object or a SpatialReference object.
"""
if isinstance(coord_trans, CoordTransform):
geom_transform(self._ptr, coord_trans._ptr)
elif isinstance(coord_trans, SpatialReference):
geom_transform_to(self._ptr, coord_trans._ptr)
else:
raise TypeError('Either a CoordTransform or a SpatialReference object required for transformation.')
def transform_to(self, srs):
"Transforms this Geometry with the given SpatialReference."
if not isinstance(srs, SpatialReference):
raise OGRException('SpatialReference object required for transform_to.')
check_err(lgdal.OGR_G_TransformTo(self._g, srs._srs))
"For backwards-compatibility."
self.transform(srs)
#### Topology Methods ####
def _topology(self, topo_func, other):
def _topology(self, func, other):
"""A generalized function for topology operations, takes a GDAL function and
the other geometry to perform the operation on."""
if not isinstance(other, OGRGeometry):
raise OGRException('Must use another OGRGeometry object for topology operations!')
raise TypeError('Must use another OGRGeometry object for topology operations!')
# Calling the passed-in topology function with the other geometry
status = topo_func(self._g, other._g)
# Returning based on the status code (an integer)
if status: return True
else: return False
# Returning the output of the given function with the other geometry's
# pointer.
return func(self._ptr, other._ptr)
def intersects(self, other):
"Returns True if this geometry intersects with the other."
return self._topology(lgdal.OGR_G_Intersects, other)
return self._topology(ogr_intersects, other)
def equals(self, other):
"Returns True if this geometry is equivalent to the other."
return self._topology(lgdal.OGR_G_Equals, other)
return self._topology(ogr_equals, other)
def disjoint(self, other):
"Returns True if this geometry and the other are spatially disjoint."
return self._topology(lgdal.OGR_G_Disjoint, other)
return self._topology(ogr_disjoint, other)
def touches(self, other):
"Returns True if this geometry touches the other."
return self._topology(lgdal.OGR_G_Touches, other)
return self._topology(ogr_touches, other)
def crosses(self, other):
"Returns True if this geometry crosses the other."
return self._topology(lgdal.OGR_G_Crosses, other)
return self._topology(ogr_crosses, other)
def within(self, other):
"Returns True if this geometry is within the other."
return self._topology(lgdal.OGR_G_Within, other)
return self._topology(ogr_within, other)
def contains(self, other):
"Returns True if this geometry contains the other."
return self._topology(lgdal.OGR_G_Contains, other)
return self._topology(ogr_contains, other)
def overlaps(self, other):
"Returns True if this geometry overlaps the other."
return self._topology(lgdal.OGR_G_Overlaps, other)
return self._topology(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(c_void_p(gen_func(self._g, other._g)))
return OGRGeometry(gen_func(self._ptr, other._ptr), self.srs)
else:
return OGRGeometry(c_void_p(gen_func(self._g)))
return OGRGeometry(gen_func(self._ptr), self.srs)
@property
def boundary(self):
"Returns the boundary of this geometry."
return self._geomgen(lgdal.OGR_G_GetBoundary)
return self._geomgen(get_boundary)
@property
def convex_hull(self):
"Returns the smallest convex Polygon that contains all the points in the Geometry."
return self._geomgen(lgdal.OGR_G_ConvexHull)
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(lgdal.OGR_G_Union, other)
"""
Returns the smallest convex Polygon that contains all the points in
this Geometry.
"""
return self._geomgen(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(lgdal.OGR_G_Difference, other)
def sym_difference(self, other):
"""Returns a new geometry which is the symmetric difference of this
geometry and the other."""
return self._geomgen(lgdal.OGR_G_SymmetricDifference, 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)
def intersection(self, other):
"""Returns a new geometry consisting of the region of intersection of this
geometry and the other."""
return self._geomgen(lgdal.OGR_G_Intersection, other)
"""
Returns a new geometry consisting of the region of intersection of this
geometry and the other.
"""
return self._geomgen(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)
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)
# The subclasses for OGR Geometry.
class Point(OGRGeometry):
@ -412,17 +415,17 @@ class Point(OGRGeometry):
@property
def x(self):
"Returns the X coordinate for this Point."
return getx(self._g, c_int(0))
return getx(self._ptr, 0)
@property
def y(self):
"Returns the Y coordinate for this Point."
return gety(self._g, c_int(0))
return gety(self._ptr, 0)
@property
def z(self):
"Returns the Z coordinate for this Point."
return getz(self._g, c_int(0))
return getz(self._ptr, 0)
@property
def tuple(self):
@ -436,17 +439,15 @@ class LineString(OGRGeometry):
def __getitem__(self, index):
"Returns the Point at the given index."
if index > 0 or index < self.point_count:
x = c_double()
y = c_double()
z = c_double()
lgdal.OGR_G_GetPoint(self._g, c_int(index),
byref(x), byref(y), byref(z))
if self.coord_dim == 1:
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))
dim = self.coord_dim
if dim == 1:
return (x.value,)
elif self.coord_dim == 2:
elif dim == 2:
return (x.value, y.value)
elif self.coord_dim == 3:
elif dim == 3:
return (x.value, y.value, z.value)
else:
raise OGRIndexError('index out of range: %s' % str(index))
@ -454,16 +455,16 @@ class LineString(OGRGeometry):
def __iter__(self):
"Iterates over each point in the LineString."
for i in xrange(self.point_count):
yield self.__getitem__(i)
yield self[i]
def __len__(self, index):
def __len__(self):
"The length returns the number of points in the LineString."
return self.point_count
@property
def tuple(self):
"Returns the tuple representation of this LineString."
return tuple(self.__getitem__(i) for i in xrange(self.point_count))
return tuple(self[i] for i in xrange(len(self)))
# LinearRings are used in Polygons.
class LinearRing(LineString): pass
@ -477,38 +478,38 @@ class Polygon(OGRGeometry):
def __iter__(self):
"Iterates through each ring in the Polygon."
for i in xrange(self.geom_count):
yield self.__getitem__(i)
yield self[i]
def __getitem__(self, index):
"Gets the ring at the specified index."
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index))
raise OGRIndexError('index out of range: %s' % index)
else:
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs)
return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
# Polygon Properties
@property
def shell(self):
"Returns the shell of this Polygon."
return self.__getitem__(0) # First ring is the shell
return self[0] # First ring is the shell
@property
def tuple(self):
"Returns a tuple of LinearRing coordinate tuples."
return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count))
return tuple(self[i].tuple for i in xrange(self.geom_count))
@property
def point_count(self):
"The number of Points in this Polygon."
# Summing up the number of points in each ring of the Polygon.
return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)])
return sum([self[i].point_count for i in xrange(self.geom_count)])
@property
def centroid(self):
"Returns the centroid (a Point) of this Polygon."
# The centroid is a Point, create a geometry for this.
p = OGRGeometry(OGRGeomType('Point'))
check_err(lgdal.OGR_G_Centroid(self._g, p._g))
get_centroid(self._ptr, p._ptr)
return p
# Geometry Collection base class.
@ -518,14 +519,14 @@ class GeometryCollection(OGRGeometry):
def __getitem__(self, index):
"Gets the Geometry at the specified index."
if index < 0 or index >= self.geom_count:
raise OGRIndexError('index out of range: %s' % str(index))
raise OGRIndexError('index out of range: %s' % index)
else:
return OGRGeometry(c_void_p(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index)))), self.srs)
return OGRGeometry(clone_geom(get_geom_ref(self._ptr, index)), self.srs)
def __iter__(self):
"Iterates over each Geometry."
for i in xrange(self.geom_count):
yield self.__getitem__(i)
yield self[i]
def __len__(self):
"The number of geometries in this Geometry Collection."
@ -534,24 +535,24 @@ class GeometryCollection(OGRGeometry):
def add(self, geom):
"Add the geometry to this Geometry Collection."
if isinstance(geom, OGRGeometry):
ptr = geom._g
ptr = geom._ptr
elif isinstance(geom, (StringType, UnicodeType)):
tmp = OGRGeometry(geom)
ptr = tmp._g
ptr = tmp._ptr
else:
raise OGRException('Must add an OGRGeometry.')
lgdal.OGR_G_AddGeometry(self._g, ptr)
add_geom(self._ptr, ptr)
@property
def point_count(self):
"The number of Points in this Geometry Collection."
# Summing up the number of points in each geometry in this collection
return sum([self.__getitem__(i).point_count for i in xrange(self.geom_count)])
return sum([self[i].point_count for i in xrange(self.geom_count)])
@property
def tuple(self):
"Returns a tuple representation of this Geometry Collection."
return tuple(self.__getitem__(i).tuple for i in xrange(self.geom_count))
return tuple(self[i].tuple for i in xrange(self.geom_count))
# Multiple Geometry types.
class MultiPoint(GeometryCollection): pass
@ -566,4 +567,5 @@ GEO_CLASSES = {1 : Point,
5 : MultiLineString,
6 : MultiPolygon,
7 : GeometryCollection,
101: LinearRing,
}

View File

@ -1,4 +1,3 @@
from types import StringType
from django.contrib.gis.gdal.error import OGRException
#### OGRGeomType ####
@ -15,7 +14,7 @@ class OGRGeomType(object):
"Figures out the correct OGR Type based upon the input."
if isinstance(type_input, OGRGeomType):
self._index = type_input._index
elif isinstance(type_input, StringType):
elif isinstance(type_input, basestring):
idx = self._has_str(self.__ogr_str, type_input)
if idx == None:
raise OGRException('Invalid OGR String Type "%s"' % type_input)
@ -25,18 +24,20 @@ class OGRGeomType(object):
raise OGRException('Invalid OGR Integer Type: %d' % type_input)
self._index = self.__ogr_int.index(type_input)
else:
raise TypeError('Invalid OGR Input type given!')
raise TypeError('Invalid OGR input type given.')
def __str__(self):
"Returns a short-hand string form of the OGR Geometry type."
return self.__ogr_str[self._index]
def __eq__(self, other):
"""Does an equivalence test on the OGR type with the given
other OGRGeomType, the short-hand string, or the integer."""
"""
Does an equivalence test on the OGR type with the given
other OGRGeomType, the short-hand string, or the integer.
"""
if isinstance(other, OGRGeomType):
return self._index == other._index
elif isinstance(other, StringType):
elif isinstance(other, basestring):
idx = self._has_str(self.__ogr_str, other)
if not (idx == None): return self._index == idx
return False

View File

@ -1,38 +1,36 @@
# Needed ctypes routines
from ctypes import c_int, c_long, c_void_p, byref, string_at
# The GDAL C Library
from django.contrib.gis.gdal.libgdal import lgdal
from ctypes import byref
# Other GDAL imports.
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import OGRException, OGRIndexError
from django.contrib.gis.gdal.feature import Feature
from django.contrib.gis.gdal.geometries import OGRGeomType
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, check_err
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_layer_defn, get_layer_srs, get_next_feature, \
reset_reading
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# 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.
# function prototype for obtaining the spatial reference system
get_srs = lgdal.OGR_L_GetSpatialRef
get_srs.restype = c_void_p
get_srs.argtypes = [c_void_p]
class Layer(object):
"A class that wraps an OGR Layer, needs to be instantiated from a DataSource object."
#### Python 'magic' routines ####
def __init__(self, l):
def __init__(self, layer_ptr):
"Needs a C pointer (Python/ctypes integer) in order to initialize."
self._layer = None # Initially NULL
self._ldefn = None
if not l:
raise OGRException, 'Cannot create Layer, invalid pointer given'
self._layer = l
self._ldefn = lgdal.OGR_L_GetLayerDefn(l)
self._ptr = None # Initially NULL
if not layer_ptr:
raise OGRException('Cannot create Layer, invalid pointer given')
self._ptr = layer_ptr
self._ldefn = get_layer_defn(self._ptr)
def __getitem__(self, index):
"Gets the Feature at the specified index."
@ -44,7 +42,7 @@ class Layer(object):
if index < 0:
index = end - index
if index < 0 or index >= self.num_feat:
raise OGRIndexError, 'index out of range'
raise OGRIndexError('index out of range')
return self._make_feature(index)
else:
# A slice was given
@ -54,9 +52,9 @@ class Layer(object):
def __iter__(self):
"Iterates over each Feature in the Layer."
# ResetReading() must be called before iteration is to begin.
lgdal.OGR_L_ResetReading(self._layer)
reset_reading(self._ptr)
for i in range(self.num_feat):
yield Feature(lgdal.OGR_L_GetNextFeature(self._layer), self._ldefn)
yield Feature(get_next_feature(self._ptr), self._ldefn)
def __len__(self):
"The length is the number of features."
@ -68,76 +66,80 @@ class Layer(object):
def _make_feature(self, offset):
"Helper routine for __getitem__ that makes a feature from an offset."
return Feature(lgdal.OGR_L_GetFeature(self._layer, c_long(offset)), self._ldefn)
return Feature(get_feature(self._ptr, offset), self._ldefn)
#### Layer properties ####
@property
def extent(self):
"Returns the extent (an Envelope) of this layer."
env = OGREnvelope()
check_err(lgdal.OGR_L_GetExtent(self._layer, byref(env), c_int(1)))
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 string_at(lgdal.OGR_FD_GetName(self._ldefn))
return get_fd_name(self._ldefn)
@property
def num_feat(self, force=1):
"Returns the number of features in the Layer."
return lgdal.OGR_L_GetFeatureCount(self._layer, c_int(force))
return get_feature_count(self._ptr, force)
@property
def num_fields(self):
"Returns the number of fields in the Layer."
return lgdal.OGR_FD_GetFieldCount(self._ldefn)
return get_field_count(self._ldefn)
@property
def geom_type(self):
"Returns the geometry type (OGRGeomType) of the Layer."
return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._ldefn))
return OGRGeomType(get_fd_geom_type(self._ldefn))
@property
def srs(self):
"Returns the Spatial Reference used in this Layer."
ptr = lgdal.OGR_L_GetSpatialRef(self._layer)
ptr = get_layer_srs(self._ptr)
if ptr:
return SpatialReference(lgdal.OSRClone(ptr), 'ogr')
return SpatialReference(clone_srs(ptr))
else:
return None
@property
def fields(self):
"Returns a list of the fields available in this Layer."
return [ string_at(lgdal.OGR_Fld_GetNameRef(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i)))
return [get_field_name(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 [ int(lgdal.OGR_Fld_GetWidth(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i)))
return [get_field_width(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 [ int(lgdal.OGR_Fld_GetPrecision(lgdal.OGR_FD_GetFieldDefn(self._ldefn, i)))
return [get_field_precision(get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields)]
#### Layer Methods ####
def get_fields(self, field_name):
"""Returns a list containing the given field name for every Feature
in the Layer."""
"""
Returns a list containing the given field name for every Feature
in the Layer.
"""
if not field_name in self.fields:
raise OGRException, 'invalid field name: %s' % field_name
raise OGRException('invalid field name: %s' % field_name)
return [feat.get(field_name) for feat in self]
def get_geoms(self, geos=False):
"""Returns a list containing the OGRGeometry for every Feature in
the Layer."""
"""
Returns a list containing the OGRGeometry for every Feature in
the Layer.
"""
if geos:
from django.contrib.gis.geos import fromstr
return [fromstr(feat.geom.wkt) for feat in self]
from django.contrib.gis.geos import GEOSGeometry
return [GEOSGeometry(feat.geom.wkb) for feat in self]
else:
return [feat.geom for feat in self]

View File

@ -1,5 +1,6 @@
import os, sys
from ctypes import CDLL, string_at
from ctypes.util import find_library
from django.contrib.gis.gdal.error import OGRException
if os.name == 'nt':
@ -7,16 +8,14 @@ if os.name == 'nt':
lib_name = 'libgdal-1.dll'
elif os.name == 'posix':
platform = os.uname()[0]
if platform in ('Linux', 'SunOS'):
# Linux or Solaris shared library
lib_name = 'libgdal.so'
elif platform == 'Darwin':
if platform == 'Darwin':
# Mac OSX shared library
lib_name = 'libgdal.dylib'
else:
raise OGRException, 'Unknown POSIX platform "%s"' % platform
# Attempting to use .so extension for all other platforms.
lib_name = 'libgdal.so'
else:
raise OGRException, 'Unsupported OS "%s"' % os.name
raise OGRException('Unsupported OS "%s"' % os.name)
# This loads the GDAL/OGR C library
lgdal = CDLL(lib_name)

View File

@ -0,0 +1,16 @@
"""
This routine provides shortuct functions to generate ctypes prototypes
for the GDAL routines.
"""
# OGR Geometry prototypes.
from django.contrib.gis.gdal.prototypes.geom import \
assign_srs, clone_geom, create_geom, destroy_geom, from_wkb, from_wkt, \
get_area, get_coord_dims, get_dims, get_envelope, get_geom_count, get_geom_name, get_geom_srs, get_geom_type, get_point_count, get_wkbsize, \
getx, get_geom_ref, gety, getz, to_gml, to_wkt
# Spatial Reference prototypes.
from django.contrib.gis.gdal.prototypes.srs import \
clone_srs
# TEMPORARY
from generation import double_output, string_output, void_output

View File

@ -0,0 +1,67 @@
"""
This module houses the ctypes function prototypes for OGR DataSource
related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*,
OGR_Fld_* routines are relevant here.
"""
from ctypes import c_char_p, c_int, c_long, c_void_p, POINTER
from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, geom_output, int_output, \
srs_output, void_output, voidptr_output
c_int_p = POINTER(c_int) # shortcut type
### Driver Routines ###
register_all = void_output(lgdal.OGRRegisterAll, [], errcheck=False)
cleanup_all = void_output(lgdal.OGRCleanupAll, [], errcheck=False)
get_driver = voidptr_output(lgdal.OGRGetDriver, [c_int])
get_driver_by_name = voidptr_output(lgdal.OGRGetDriverByName, [c_char_p])
get_driver_count = int_output(lgdal.OGRGetDriverCount, [])
get_driver_name = const_string_output(lgdal.OGR_Dr_GetName, [c_void_p])
### DataSource ###
open_ds = voidptr_output(lgdal.OGROpen, [c_char_p, c_int, POINTER(c_void_p)])
destroy_ds = void_output(lgdal.OGR_DS_Destroy, [c_void_p], errcheck=False)
release_ds = void_output(lgdal.OGRReleaseDataSource, [c_void_p])
get_ds_name = const_string_output(lgdal.OGR_DS_GetName, [c_void_p])
get_layer = voidptr_output(lgdal.OGR_DS_GetLayer, [c_void_p, c_int])
get_layer_by_name = voidptr_output(lgdal.OGR_DS_GetLayerByName, [c_void_p, c_char_p])
get_layer_count = int_output(lgdal.OGR_DS_GetLayerCount, [c_void_p])
### Layer Routines ###
get_extent = void_output(lgdal.OGR_L_GetExtent, [c_void_p, POINTER(OGREnvelope), c_int])
get_feature = voidptr_output(lgdal.OGR_L_GetFeature, [c_void_p, c_long])
get_feature_count = int_output(lgdal.OGR_L_GetFeatureCount, [c_void_p, c_int])
get_layer_defn = voidptr_output(lgdal.OGR_L_GetLayerDefn, [c_void_p])
get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p])
get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p])
reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False)
### Feature Definition Routines ###
get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p])
get_fd_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
get_feat_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
get_field_count = int_output(lgdal.OGR_FD_GetFieldCount, [c_void_p])
get_field_defn = voidptr_output(lgdal.OGR_FD_GetFieldDefn, [c_void_p, c_int])
### Feature Routines ###
clone_feature = voidptr_output(lgdal.OGR_F_Clone, [c_void_p])
destroy_feature = void_output(lgdal.OGR_F_Destroy, [c_void_p])
feature_equal = int_output(lgdal.OGR_F_Equal, [c_void_p, c_void_p])
get_feat_geom_ref = geom_output(lgdal.OGR_F_GetGeometryRef, [c_void_p])
get_feat_field_count = int_output(lgdal.OGR_F_GetFieldCount, [c_void_p])
get_feat_field_defn = voidptr_output(lgdal.OGR_F_GetFieldDefnRef, [c_void_p, c_int])
get_fid = int_output(lgdal.OGR_F_GetFID, [c_void_p])
get_field_as_datetime = int_output(lgdal.OGR_F_GetFieldAsDateTime, [c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p])
get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int])
get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int])
get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int])
get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p])
### Field Routines ###
get_field_name = const_string_output(lgdal.OGR_Fld_GetNameRef, [c_void_p])
get_field_precision = int_output(lgdal.OGR_Fld_GetPrecision, [c_void_p])
get_field_type = int_output(lgdal.OGR_Fld_GetType, [c_void_p])
get_field_type_name = const_string_output(lgdal.OGR_GetFieldTypeName, [c_int])
get_field_width = int_output(lgdal.OGR_Fld_GetWidth, [c_void_p])

View File

@ -0,0 +1,125 @@
"""
This module houses the error-checking routines used by the GDAL
ctypes prototypes.
"""
from ctypes import c_void_p, string_at
from django.contrib.gis.gdal.error import check_err, OGRException, SRSException
from django.contrib.gis.gdal.libgdal import lgdal
# Helper routines for retrieving pointers and/or values from
# arguments passed in by reference.
def arg_byref(args, offset=-1):
"Returns the pointer argument's by-refernece value."
return args[offset]._obj.value
def ptr_byref(args, offset=-1):
"Returns the pointer argument passed in by-reference."
return args[offset]._obj
def check_bool(result, func, cargs):
"Returns the boolean evaluation of the value."
if bool(result): return True
else: return False
### String checking Routines ###
def check_const_string(result, func, cargs, offset=None):
"""
Similar functionality to `check_string`, but does not free the pointer.
"""
if offset:
check_err(result)
ptr = ptr_byref(cargs, offset)
return ptr.value
else:
return result
def check_string(result, func, cargs, offset=-1, str_result=False):
"""
Checks the string output returned from the given function, and frees
the string pointer allocated by OGR. The `str_result` keyword
may be used when the result is the string pointer, otherwise
the OGR error code is assumed. The `offset` keyword may be used
to extract the string pointer passed in by-reference at the given
slice offset in the function arguments.
"""
if str_result:
# For routines that return a string.
ptr = result
if not ptr: s = None
else: s = string_at(result)
else:
# Error-code return specified.
check_err(result)
ptr = ptr_byref(cargs, offset)
# Getting the string value
s = ptr.value
# Correctly freeing the allocated memory beind GDAL pointer
# w/the VSIFree routine.
if ptr: lgdal.VSIFree(ptr)
return s
### DataSource, Layer error-checking ###
### Envelope checking ###
def check_envelope(result, func, cargs, offset=-1):
"Checks a function that returns an OGR Envelope by reference."
env = ptr_byref(cargs, offset)
return env
### Geometry error-checking routines ###
def check_geom(result, func, cargs):
"Checks a function that returns a geometry."
# OGR_G_Clone may return an integer, even though the
# restype is set to c_void_p
if isinstance(result, int):
result = c_void_p(result)
if not result:
raise OGRException('Invalid geometry pointer returned from "%s".' % func.__name__)
return result
def check_geom_offset(result, func, cargs, offset=-1):
"Chcks the geometry at the given offset in the C parameter list."
check_err(result)
geom = ptr_byref(cargs, offset=offset)
return check_geom(geom, func, cargs)
### Spatial Reference error-checking routines ###
def check_srs(result, func, cargs):
if isinstance(result, int):
result = c_void_p(result)
if not result:
raise SRSException('Invalid spatial reference pointer returned from "%s".' % func.__name__)
return result
### Other error-checking routines ###
def check_arg_errcode(result, func, cargs):
"""
The error code is returned in the last argument, by reference.
Check its value with `check_err` before returning the result.
"""
check_err(arg_byref(cargs))
return result
def check_errcode(result, func, cargs):
"""
Check the error code returned (c_int).
"""
check_err(result)
return
def check_pointer(result, func, cargs):
"Makes sure the result pointer is valid."
if bool(result):
return result
else:
raise OGRException('Invalid pointer returned from "%s"' % func.__name__)
def check_str_arg(result, func, cargs):
"""
This is for the OSRGet[Angular|Linear]Units functions, which
require that the returned string pointer not be freed. This
returns both the double and tring values.
"""
dbl = result
ptr = cargs[-1]._obj
return dbl, ptr.value

View File

@ -0,0 +1,113 @@
"""
This module contains functions that generate ctypes prototypes for the
GDAL routines.
"""
from ctypes import c_char_p, c_double, c_int, c_void_p
from django.contrib.gis.gdal.prototypes.errcheck import \
check_arg_errcode, check_errcode, check_geom, check_geom_offset, \
check_pointer, check_srs, check_str_arg, check_string, check_const_string
def double_output(func, argtypes, errcheck=False, strarg=False):
"Generates a ctypes function that returns a double value."
func.argtypes = argtypes
func.restype = c_double
if errcheck: func.errcheck = check_arg_errcode
if strarg: func.errcheck = check_str_arg
return func
def geom_output(func, argtypes, offset=None):
"""
Generates a function that returns a Geometry either by reference
or directly (if the return_geom keyword is set to True).
"""
# Setting the argument types
func.argtypes = argtypes
if not offset:
# When a geometry pointer is directly returned.
func.restype = c_void_p
func.errcheck = check_geom
else:
# Error code returned, geometry is returned by-reference.
func.restype = c_int
def geomerrcheck(result, func, cargs):
return check_geom_offset(result, func, cargs, offset)
func.errcheck = geomerrcheck
return func
def int_output(func, argtypes):
"Generates a ctypes function that returns an integer value."
func.argtypes = argtypes
func.restype = c_int
return func
def srs_output(func, argtypes):
"""
Generates a ctypes prototype for the given function with
the given C arguments that returns a pointer to an OGR
Spatial Reference System.
"""
func.argtypes = argtypes
func.restype = c_void_p
func.errcheck = check_srs
return func
def const_string_output(func, argtypes, offset=None):
func.argtypes = argtypes
if offset:
func.restype = c_int
else:
func.restype = c_char_p
def _check_const(result, func, cargs):
return check_const_string(result, func, cargs, offset=offset)
func.errcheck = _check_const
return func
def string_output(func, argtypes, offset=-1, str_result=False):
"""
Generates a ctypes prototype for the given function with the
given argument types that returns a string from a GDAL pointer.
The `const` flag indicates whether the allocated pointer should
be freed via the GDAL library routine VSIFree -- but only applies
only when `str_result` is True.
"""
func.argtypes = argtypes
if str_result:
# String is the result, don't explicitly define
# the argument type so we can get the pointer.
pass
else:
# Error code is returned
func.restype = c_int
# Dynamically defining our error-checking function with the
# given offset.
def _check_str(result, func, cargs):
return check_string(result, func, cargs,
offset=offset, str_result=str_result)
func.errcheck = _check_str
return func
def void_output(func, argtypes, errcheck=True):
"""
For functions that don't only return an error code that needs to
be examined.
"""
if argtypes: func.argtypes = argtypes
if errcheck:
# `errcheck` keyword may be set to False for routines that
# return void, rather than a status code.
func.restype = c_int
func.errcheck = check_errcode
return func
def voidptr_output(func, argtypes):
"For functions that return c_void_p."
func.argtypes = argtypes
func.restype = c_void_p
func.errcheck = check_pointer
return func

View File

@ -0,0 +1,95 @@
from ctypes import c_char, c_char_p, c_double, c_int, c_ubyte, c_void_p, POINTER
from django.contrib.gis.gdal.envelope import OGREnvelope
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.errcheck import check_bool, check_envelope
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, geom_output, int_output, \
srs_output, string_output, void_output
### Generation routines specific to this module ###
def env_func(f, argtypes):
"For getting OGREnvelopes."
f.argtypes = argtypes
f.restype = None
f.errcheck = check_envelope
return f
def pnt_func(f):
"For accessing point information."
return double_output(f, [c_void_p, c_int])
def topology_func(f):
f.argtypes = [c_void_p, c_void_p]
f.restype = c_int
f.errchck = check_bool
return f
### OGR_G ctypes function prototypes ###
# GetX, GetY, GetZ all return doubles.
getx = pnt_func(lgdal.OGR_G_GetX)
gety = pnt_func(lgdal.OGR_G_GetY)
getz = pnt_func(lgdal.OGR_G_GetZ)
# Geometry creation routines.
from_wkb = geom_output(lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2)
from_wkt = geom_output(lgdal.OGR_G_CreateFromWkt, [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], offset=-1)
create_geom = geom_output(lgdal.OGR_G_CreateGeometry, [c_int])
clone_geom = geom_output(lgdal.OGR_G_Clone, [c_void_p])
get_geom_ref = geom_output(lgdal.OGR_G_GetGeometryRef, [c_void_p, c_int])
get_boundary = geom_output(lgdal.OGR_G_GetBoundary, [c_void_p])
geom_convex_hull = geom_output(lgdal.OGR_G_ConvexHull, [c_void_p])
geom_diff = geom_output(lgdal.OGR_G_Difference, [c_void_p, c_void_p])
geom_intersection = geom_output(lgdal.OGR_G_Intersection, [c_void_p, c_void_p])
geom_sym_diff = geom_output(lgdal.OGR_G_SymmetricDifference, [c_void_p, c_void_p])
geom_union = geom_output(lgdal.OGR_G_Union, [c_void_p, c_void_p])
# Geometry modification routines.
add_geom = void_output(lgdal.OGR_G_AddGeometry, [c_void_p, c_void_p])
import_wkt = void_output(lgdal.OGR_G_ImportFromWkt, [c_void_p, POINTER(c_char_p)])
# Destroys a geometry
destroy_geom = void_output(lgdal.OGR_G_DestroyGeometry, [c_void_p], errcheck=False)
# Geometry export routines.
to_wkb = void_output(lgdal.OGR_G_ExportToWkb, None, errcheck=True) # special handling for WKB.
to_wkt = string_output(lgdal.OGR_G_ExportToWkt, [c_void_p, POINTER(c_char_p)])
to_gml = string_output(lgdal.OGR_G_ExportToGML, [c_void_p], str_result=True)
get_wkbsize = int_output(lgdal.OGR_G_WkbSize, [c_void_p])
# Geometry spatial-reference related routines.
assign_srs = void_output(lgdal.OGR_G_AssignSpatialReference, [c_void_p, c_void_p], errcheck=False)
get_geom_srs = srs_output(lgdal.OGR_G_GetSpatialReference, [c_void_p])
# Geometry properties
get_area = double_output(lgdal.OGR_G_GetArea, [c_void_p])
get_centroid = void_output(lgdal.OGR_G_Centroid, [c_void_p, c_void_p])
get_dims = int_output(lgdal.OGR_G_GetDimension, [c_void_p])
get_coord_dims = int_output(lgdal.OGR_G_GetCoordinateDimension, [c_void_p])
get_geom_count = int_output(lgdal.OGR_G_GetGeometryCount, [c_void_p])
get_geom_name = const_string_output(lgdal.OGR_G_GetGeometryName, [c_void_p])
get_geom_type = int_output(lgdal.OGR_G_GetGeometryType, [c_void_p])
get_point_count = int_output(lgdal.OGR_G_GetPointCount, [c_void_p])
get_point = void_output(lgdal.OGR_G_GetPoint, [c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)], errcheck=False)
geom_close_rings = void_output(lgdal.OGR_G_CloseRings, [c_void_p])
# Topology routines.
ogr_contains = topology_func(lgdal.OGR_G_Contains)
ogr_crosses = topology_func(lgdal.OGR_G_Crosses)
ogr_disjoint = topology_func(lgdal.OGR_G_Disjoint)
ogr_equals = topology_func(lgdal.OGR_G_Equals)
ogr_intersects = topology_func(lgdal.OGR_G_Intersects)
ogr_overlaps = topology_func(lgdal.OGR_G_Overlaps)
ogr_touches = topology_func(lgdal.OGR_G_Touches)
ogr_within = topology_func(lgdal.OGR_G_Within)
# Transformation routines.
geom_transform = void_output(lgdal.OGR_G_Transform, [c_void_p, c_void_p], errcheck=True)
geom_transform_to = void_output(lgdal.OGR_G_TransformTo, [c_void_p, c_void_p], errcheck=True)
# For retrieving the envelope of the geometry.
get_envelope = lgdal.OGR_G_GetEnvelope
get_envelope.restype = None
get_envelope.argtypes = [c_void_p, POINTER(OGREnvelope)]
get_envelope.errcheck = check_envelope

View File

@ -0,0 +1,70 @@
from ctypes import c_char_p, c_int, c_void_p, POINTER
from django.contrib.gis.gdal.libgdal import lgdal
from django.contrib.gis.gdal.prototypes.generation import \
const_string_output, double_output, int_output, \
srs_output, string_output, void_output
## Shortcut generation for routines with known parameters.
def srs_double(f):
"""
Creates a function prototype for the OSR routines that take
the OSRSpatialReference object and
"""
return double_output(f, [c_void_p, POINTER(c_int)], errcheck=True)
def units_func(f):
"""
Creates a ctypes function prototype for OSR units functions, e.g.,
OSRGetAngularUnits, OSRGetLinearUnits.
"""
return double_output(f, [c_void_p, POINTER(c_char_p)], strarg=True)
# Creation & destruction.
clone_srs = srs_output(lgdal.OSRClone, [c_void_p])
new_srs = srs_output(lgdal.OSRNewSpatialReference, [c_char_p])
release_srs = void_output(lgdal.OSRRelease, [c_void_p])
srs_validate = void_output(lgdal.OSRValidate, [c_void_p], errcheck=True)
# Getting the semi_major, semi_minor, and flattening functions.
semi_major = srs_double(lgdal.OSRGetSemiMajor)
semi_minor = srs_double(lgdal.OSRGetSemiMinor)
invflattening = srs_double(lgdal.OSRGetInvFlattening)
# WKT, PROJ, EPSG, XML importation routines.
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(lgdal.OSRImportFromEPSG, [c_void_p, c_int])
from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p])
# Morphing to/from ESRI WKT.
morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p])
morph_from_esri = void_output(lgdal.OSRMorphFromESRI, [c_void_p])
# Identifying the EPSG
identify_epsg = void_output(lgdal.OSRAutoIdentifyEPSG, [c_void_p])
# Getting the angular_units, linear_units functions
linear_units = units_func(lgdal.OSRGetLinearUnits)
angular_units = units_func(lgdal.OSRGetAngularUnits)
# For exporting to WKT, PROJ.4, "Pretty" WKT, and XML.
to_wkt = string_output(lgdal.OSRExportToWkt, [c_void_p, POINTER(c_char_p)])
to_proj = string_output(lgdal.OSRExportToProj4, [c_void_p, POINTER(c_char_p)])
to_pretty_wkt = string_output(lgdal.OSRExportToPrettyWkt, [c_void_p, POINTER(c_char_p), c_int], offset=-2)
# FIXME: This leaks memory, still don't know why.
to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2)
# String attribute retrival routines.
get_attr_value = const_string_output(lgdal.OSRGetAttrValue, [c_void_p, c_char_p, c_int])
get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p])
get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p])
# SRS Properties
isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p])
islocal = int_output(lgdal.OSRIsLocal, [c_void_p])
isprojected = int_output(lgdal.OSRIsProjected, [c_void_p])
# Coordinate transformation
new_ct= srs_output(lgdal.OCTNewCoordinateTransformation, [c_void_p, c_void_p])
destroy_ct = void_output(lgdal.OCTDestroyCoordinateTransformation, [c_void_p])

View File

@ -28,50 +28,18 @@
"""
import re
from types import StringType, UnicodeType, TupleType
from ctypes import \
c_char_p, c_int, c_double, c_void_p, POINTER, \
byref, string_at, create_string_buffer
# Getting the GDAL C Library
from django.contrib.gis.gdal.libgdal import lgdal
from ctypes import byref, c_char_p, c_int, c_void_p
# Getting the error checking routine and exceptions
from django.contrib.gis.gdal.error import check_err, OGRException, SRSException
#### ctypes function prototypes ####
def ellipsis_func(f):
"""
Creates a ctypes function prototype for OSR ellipsis property functions, e.g.,
OSRGetSemiMajor, OSRGetSemiMinor, OSRGetInvFlattening.
"""
f.restype = c_double
f.argtypes = [c_void_p, POINTER(c_int)]
return f
# Getting the semi_major, semi_minor, and flattening functions.
semi_major = ellipsis_func(lgdal.OSRGetSemiMajor)
semi_minor = ellipsis_func(lgdal.OSRGetSemiMinor)
invflattening = ellipsis_func(lgdal.OSRGetInvFlattening)
def units_func(f):
"""
Creates a ctypes function prototype for OSR units functions, e.g.,
OSRGetAngularUnits, OSRGetLinearUnits.
"""
f.restype = c_double
f.argtypes = [c_void_p, POINTER(c_char_p)]
return f
# Getting the angular_units, linear_units functions
linear_units = units_func(lgdal.OSRGetLinearUnits)
angular_units = units_func(lgdal.OSRGetAngularUnits)
from django.contrib.gis.gdal.error import OGRException, SRSException
from django.contrib.gis.gdal.prototypes.srs import *
#### Spatial Reference class. ####
class SpatialReference(object):
"""
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.'
the SpatialReference object "provide[s] services to represent coordinate
systems (projections and datums) and to transform between them."
"""
# Well-Known Geographical Coordinate System Name
@ -82,9 +50,8 @@ class SpatialReference(object):
def __init__(self, srs_input='', srs_type='wkt'):
"Creates a spatial reference object from the given OGC Well Known Text (WKT)."
self._srs = None # Initially NULL
# Creating an initial empty string buffer.
# Intializing pointer and string buffer.
self._ptr = None
buf = c_char_p('')
# Encoding to ASCII if unicode passed in.
@ -92,37 +59,40 @@ class SpatialReference(object):
srs_input = srs_input.encode('ascii')
if isinstance(srs_input, StringType):
# Is this an EPSG well known name?
m = self._epsg_regex.match(srs_input)
if m:
# Is this an EPSG well known name?
srs_type = 'epsg'
srs_input = int(m.group('epsg'))
# Is this a short-hand well known name?
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':
pass
else:
# Setting the buffer with WKT, PROJ.4 string, etc.
buf = c_char_p(srs_input)
elif isinstance(srs_input, int):
if srs_type == 'wkt': srs_type = 'epsg' # want to try epsg if only integer provided
if srs_type not in ('epsg', 'ogr'):
raise SRSException('Integer input requires SRS type of "ogr" or "epsg".')
# EPSG integer code was input.
if srs_type != 'epsg': srs_type = 'epsg'
elif isinstance(srs_input, c_void_p):
srs_type = 'ogr'
else:
raise TypeError('Invalid SRS type "%s"' % srs_type)
# Calling OSRNewSpatialReference with the string buffer.
if srs_type == 'ogr':
srs = srs_input # SRS input is OGR pointer
# SRS input is OGR pointer
srs = srs_input
else:
srs = lgdal.OSRNewSpatialReference(buf)
# Creating a new pointer, using the string buffer.
srs = 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._srs = srs
self._ptr = srs
# Post-processing if in PROJ.4 or EPSG formats.
if srs_type == 'proj': self.import_proj(srs_input)
@ -130,7 +100,7 @@ class SpatialReference(object):
def __del__(self):
"Destroys this spatial reference."
if self._srs: lgdal.OSRRelease(self._srs)
if self._ptr: release_srs(self._ptr)
def __getitem__(self, target):
"""
@ -172,43 +142,48 @@ class SpatialReference(object):
"The string representation uses 'pretty' WKT."
return self.pretty_wkt
def _string_ptr(self, ptr):
"""
Returns the string at the pointer if it is valid, None if the pointer
is NULL.
"""
if not ptr: return None
else: return string_at(ptr)
#### SpatialReference Methods ####
def auth_name(self, target):
"Getting the authority name for the target node."
ptr = lgdal.OSRGetAuthorityName(self._srs, c_char_p(target))
return self._string_ptr(ptr)
def auth_code(self, target):
"Getting the authority code for the given target node."
ptr = lgdal.OSRGetAuthorityCode(self._srs, c_char_p(target))
return self._string_ptr(ptr)
def attr_value(self, target, index=0):
"""
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):
raise TypeError('Attribute target must be a string')
ptr = lgdal.OSRGetAttrValue(self._srs, c_char_p(target), c_int(index))
return self._string_ptr(ptr)
if not isinstance(target, str) or not isinstance(index, int):
raise TypeError
return 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)
def auth_code(self, target):
"Returns the authority code for the given string target node."
return get_auth_code(self._ptr, target)
def clone(self):
"Returns a clone of this SpatialReference object."
return SpatialReference(clone_srs(self._ptr))
def from_esri(self):
"Morphs this SpatialReference from ESRI's format to EPSG."
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)
def to_esri(self):
"Morphs this SpatialReference to ESRI's format."
morph_to_esri(self._ptr)
def validate(self):
"Checks to see if the given spatial reference is valid."
check_err(lgdal.OSRValidate(self._srs))
def clone(self):
"Returns a clone of this Spatial Reference."
return SpatialReference(lgdal.OSRClone(self._srs), 'ogr')
srs_validate(self._ptr)
#### Name & SRID properties ####
@property
def name(self):
"Returns the name of this Spatial Reference."
@ -226,43 +201,29 @@ class SpatialReference(object):
return None
#### Unit Properties ####
def _cache_linear(self):
"Caches the linear units value and name."
if not hasattr(self, '_linear_units') or not hasattr(self, '_linear_name'):
name_buf = c_char_p()
self._linear_units = linear_units(self._srs, byref(name_buf))
self._linear_name = string_at(name_buf)
@property
def linear_name(self):
"Returns the name of the linear units."
self._cache_linear()
return self._linear_name
units, name = linear_units(self._ptr, byref(c_char_p()))
return name
@property
def linear_units(self):
"Returns the value of the linear units."
self._cache_linear()
return self._linear_units
def _cache_angular(self):
"Caches the angular units value and name."
name_buf = c_char_p()
if not hasattr(self, '_angular_units') or not hasattr(self, '_angular_name'):
self._angular_units = angular_units(self._srs, byref(name_buf))
self._angular_name = string_at(name_buf)
units, name = linear_units(self._ptr, byref(c_char_p()))
return units
@property
def angular_name(self):
"Returns the name of the angular units."
self._cache_angular()
return self._angular_name
units, name = angular_units(self._ptr, byref(c_char_p()))
return name
@property
def angular_units(self):
"Returns the value of the angular units."
self._cache_angular()
return self._angular_units
units, name = angular_units(self._ptr, byref(c_char_p()))
return units
#### Spheroid/Ellipsoid Properties ####
@property
@ -276,26 +237,17 @@ class SpatialReference(object):
@property
def semi_major(self):
"Returns the Semi Major Axis for this Spatial Reference."
err = c_int(0)
sm = semi_major(self._srs, byref(err))
check_err(err.value)
return sm
return semi_major(self._ptr, byref(c_int()))
@property
def semi_minor(self):
"Returns the Semi Minor Axis for this Spatial Reference."
err = c_int()
sm = semi_minor(self._srs, byref(err))
check_err(err.value)
return sm
return semi_minor(self._ptr, byref(c_int()))
@property
def inverse_flattening(self):
"Returns the Inverse Flattening for this Spatial Reference."
err = c_int()
inv_flat = invflattening(self._srs, byref(err))
check_err(err.value)
return inv_flat
return invflattening(self._ptr, byref(c_int()))
#### Boolean Properties ####
@property
@ -304,14 +256,12 @@ class SpatialReference(object):
Returns True if this SpatialReference is geographic
(root node is GEOGCS).
"""
if lgdal.OSRIsGeographic(self._srs): return True
else: return False
return bool(isgeographic(self._ptr))
@property
def local(self):
"Returns True if this SpatialReference is local (root node is LOCAL_CS)."
if lgdal.OSRIsLocal(self._srs): return True
else: return False
return bool(islocal(self._ptr))
@property
def projected(self):
@ -319,48 +269,40 @@ class SpatialReference(object):
Returns True if this SpatialReference is a projected coordinate system
(root node is PROJCS).
"""
if lgdal.OSRIsProjected(self._srs): return True
else: return False
return bool(isprojected(self._ptr))
#### Import Routines #####
def import_wkt(self, wkt):
"Imports the Spatial Reference from OGC WKT (string)"
buf = create_string_buffer(wkt)
check_err(lgdal.OSRImportFromWkt(self._srs, byref(buf)))
from_wkt(self._ptr, byref(c_char_p(wkt)))
def import_proj(self, proj):
"Imports the Spatial Reference from a PROJ.4 string."
check_err(lgdal.OSRImportFromProj4(self._srs, create_string_buffer(proj)))
from_proj(self._ptr, proj)
def import_epsg(self, epsg):
"Imports the Spatial Reference from the EPSG code (an integer)."
check_err(lgdal.OSRImportFromEPSG(self._srs, c_int(epsg)))
from_epsg(self._ptr, epsg)
def import_xml(self, xml):
"Imports the Spatial Reference from an XML string."
check_err(lgdal.OSRImportFromXML(self._srs, create_string_buffer(xml)))
from_xml(self._ptr, xml)
#### Export Properties ####
@property
def wkt(self):
"Returns the WKT representation of this Spatial Reference."
w = c_char_p()
check_err(lgdal.OSRExportToWkt(self._srs, byref(w)))
if w: return string_at(w)
return to_wkt(self._ptr, byref(c_char_p()))
@property
def pretty_wkt(self, simplify=0):
"Returns the 'pretty' representation of the WKT."
w = c_char_p()
check_err(lgdal.OSRExportToPrettyWkt(self._srs, byref(w), c_int(simplify)))
if w: return string_at(w)
return to_pretty_wkt(self._ptr, byref(c_char_p()), simplify)
@property
def proj(self):
"Returns the PROJ.4 representation for this Spatial Reference."
w = c_char_p()
check_err(lgdal.OSRExportToProj4(self._srs, byref(w)))
if w: return string_at(w)
return to_proj(self._ptr, byref(c_char_p()))
@property
def proj4(self):
@ -370,28 +312,34 @@ class SpatialReference(object):
@property
def xml(self, dialect=''):
"Returns the XML representation of this Spatial Reference."
w = c_char_p()
check_err(lgdal.OSRExportToXML(self._srs, byref(w), create_string_buffer(dialect)))
return string_at(w)
# FIXME: This leaks memory, have to figure out why.
return 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):
"A coordinate system transformation object."
"The coordinate system transformation object."
def __init__(self, source, target):
"Initializes on a source and target SpatialReference objects."
self._ct = 0 # Initially NULL
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')
ct = lgdal.OCTNewCoordinateTransformation(source._srs, target._srs)
if not ct:
self._ptr = new_ct(source._ptr, target._ptr)
if not self._ptr:
raise SRSException('could not intialize CoordTransform object')
self._ct = ct
self._srs1_name = source.name
self._srs2_name = target.name
def __del__(self):
"Deletes this Coordinate Transformation object."
if self._ct: lgdal.OCTDestroyCoordinateTransformation(self._ct)
if self._ptr: destroy_ct(self._ptr)
def __str__(self):
return 'Transform from "%s" to "%s"' % (str(self._srs1_name), str(self._srs2_name))
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)

View File

@ -15,10 +15,10 @@ class TestSHP:
setattr(self, key, value)
# List of acceptable data sources.
ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, fields={'dbl' : OFTReal, 'int' : OFTReal, 'str' : OFTString,},
ds_list = (TestSHP('test_point', nfeat=5, nfld=3, geom='POINT', gtype=1, fields={'dbl' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
extent=(-1.35011,0.166623,-0.524093,0.824508), # Got extent from QGIS
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, fields={'float' : OFTReal, 'int' : OFTReal, 'str' : OFTString,},
TestSHP('test_poly', nfeat=3, nfld=3, geom='POLYGON', gtype=3, fields={'float' : OFTReal, 'int' : OFTInteger, 'str' : OFTString,},
extent=(-1.01513,-0.558245,0.161876,0.839637), # Got extent from QGIS
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
)
@ -101,17 +101,12 @@ class DataSourceTest(unittest.TestCase):
# Making sure the fields match to an appropriate OFT type.
for k, v in source.fields.items():
fld = feat[k] # Indexing with string value
# Asserting the string representation, and making sure we get
# the proper OGR Field instance.
if isinstance(fld, OFTString): fmt = '%s ("%s")'
else: fmt = '%s (%s)'
self.assertEqual(fmt % (k, fld.value), str(fld))
self.assertEqual(True, isinstance(fld, v))
# Making sure we get the proper OGR Field instance, using
# a string value index for the feature.
self.assertEqual(True, isinstance(feat[k], v))
# Testing __iter__ on the Feature
for fld in feat: self.assertEqual(fld.name in source.fields.keys(), True)
for fld in feat: self.assertEqual(True, fld.name in source.fields.keys())
def test05_geometries(self):
"Testing Geometries from Data Source Features."

View File

@ -1,5 +1,6 @@
import unittest
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException, SpatialReference
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, \
OGRException, OGRIndexError, SpatialReference
from django.contrib.gis.tests.geometries import *
class OGRGeomTest(unittest.TestCase):
@ -94,41 +95,78 @@ class OGRGeomTest(unittest.TestCase):
def test04_linestring(self):
"Testing LineString objects."
prev = OGRGeometry('POINT(0 0)')
for ls in linestrings:
linestr = OGRGeometry(ls.wkt)
self.assertEqual(2, linestr.geom_type)
self.assertEqual('LINESTRING', linestr.geom_name)
self.assertEqual(ls.n_p, linestr.point_count)
self.assertEqual(ls.tup, linestr.tuple)
self.assertEqual(True, linestr == OGRGeometry(ls.wkt))
self.assertEqual(True, linestr != prev)
self.assertRaises(OGRIndexError, linestr.__getitem__, len(linestr))
prev = linestr
def test05_multilinestring(self):
"Testing MultiLineString objects."
prev = OGRGeometry('POINT(0 0)')
for mls in multilinestrings:
mlinestr = OGRGeometry(mls.wkt)
self.assertEqual(5, mlinestr.geom_type)
self.assertEqual('MULTILINESTRING', mlinestr.geom_name)
self.assertEqual(mls.n_p, mlinestr.point_count)
self.assertEqual(mls.tup, mlinestr.tuple)
self.assertEqual(True, mlinestr == OGRGeometry(mls.wkt))
self.assertEqual(True, mlinestr != prev)
prev = mlinestr
for ls in mlinestr:
self.assertEqual(2, ls.geom_type)
self.assertEqual('LINESTRING', ls.geom_name)
self.assertRaises(OGRIndexError, mlinestr.__getitem__, len(mlinestr))
def test06_polygons(self):
def test06_linearring(self):
"Testing LinearRing objects."
prev = OGRGeometry('POINT(0 0)')
for rr in linearrings:
lr = OGRGeometry(rr.wkt)
#self.assertEqual(101, lr.geom_type.num)
self.assertEqual('LINEARRING', lr.geom_name)
self.assertEqual(rr.n_p, len(lr))
self.assertEqual(True, lr == OGRGeometry(rr.wkt))
self.assertEqual(True, lr != prev)
prev = lr
def test07a_polygons(self):
"Testing Polygon objects."
prev = OGRGeometry('POINT(0 0)')
for p in polygons:
poly = OGRGeometry(p.wkt)
self.assertEqual(3, poly.geom_type)
self.assertEqual('POLYGON', poly.geom_name)
self.assertEqual(p.n_p, poly.point_count)
first = True
self.assertEqual(p.n_i + 1, len(poly))
# Testing area & centroid.
self.assertAlmostEqual(p.area, poly.area, 9)
x, y = poly.centroid.tuple
self.assertAlmostEqual(p.centroid[0], x, 9)
self.assertAlmostEqual(p.centroid[1], y, 9)
# Testing equivalence
self.assertEqual(True, poly == OGRGeometry(p.wkt))
self.assertEqual(True, poly != prev)
if p.ext_ring_cs:
ring = poly[0]
self.assertEqual(p.ext_ring_cs, ring.tuple)
self.assertEqual(p.ext_ring_cs, poly[0].tuple)
self.assertEqual(len(p.ext_ring_cs), ring.point_count)
for r in poly:
if first and p.ext_ring_cs:
first = False
# Testing the equivilance of the exerior rings
# since the first iteration will be the exterior ring.
self.assertEqual(len(p.ext_ring_cs), r.point_count)
self.assertEqual(p.ext_ring_cs, r.tuple)
self.assertEqual('LINEARRING', r.geom_name)
def test07_closepolygons(self):
def test07b_closepolygons(self):
"Testing closing Polygon objects."
# Both rings in this geometry are not closed.
poly = OGRGeometry('POLYGON((0 0, 5 0, 5 5, 0 5), (1 1, 2 1, 2 2, 2 1))')
self.assertEqual(8, poly.point_count)
@ -149,6 +187,7 @@ class OGRGeomTest(unittest.TestCase):
def test08_multipolygons(self):
"Testing MultiPolygon objects."
prev = OGRGeometry('POINT(0 0)')
for mp in multipolygons:
mpoly = OGRGeometry(mp.wkt)
self.assertEqual(6, mpoly.geom_type)
@ -156,27 +195,53 @@ class OGRGeomTest(unittest.TestCase):
if mp.valid:
self.assertEqual(mp.n_p, mpoly.point_count)
self.assertEqual(mp.num_geom, len(mpoly))
self.assertRaises(OGRIndexError, mpoly.__getitem__, len(mpoly))
for p in mpoly:
self.assertEqual('POLYGON', p.geom_name)
self.assertEqual(3, p.geom_type)
self.assertEqual(mpoly.wkt, OGRGeometry(mp.wkt).wkt)
def test09_srs(self):
"Testing OGR Geometries with Spatial Reference objects."
for mp in multipolygons:
# Creating a geometry w/spatial reference
sr = SpatialReference('WGS84')
mpoly = OGRGeometry(mp.wkt, sr)
self.assertEqual(sr.wkt, mpoly.srs.wkt)
# Ensuring that SRS is propagated to clones.
klone = mpoly.clone()
self.assertEqual(sr.wkt, klone.srs.wkt)
# Ensuring all children geometries (polygons and their rings) all
# return the assigned spatial reference as well.
for poly in mpoly:
self.assertEqual(sr.wkt, poly.srs.wkt)
for ring in poly:
self.assertEqual(sr.wkt, ring.srs.wkt)
# Ensuring SRS propagate in topological ops.
a, b = topology_geoms[0]
a, b = OGRGeometry(a.wkt, sr), OGRGeometry(b.wkt, sr)
diff = a.difference(b)
union = a.union(b)
self.assertEqual(sr.wkt, diff.srs.wkt)
self.assertEqual(sr.srid, union.srs.srid)
# Instantiating w/an integer SRID
mpoly = OGRGeometry(mp.wkt, 4326)
self.assertEqual(4326, mpoly.srid)
mpoly.srs = SpatialReference(4269)
self.assertEqual(4269, mpoly.srid)
self.assertEqual('NAD83', mpoly.srs.name)
# Incrementing through the multipolyogn after the spatial reference
# has been re-assigned.
for poly in mpoly:
self.assertEqual(mpoly.srs.wkt, poly.srs.wkt)
poly.srs = 32140
for ring in poly:
# Changing each ring in the polygon
self.assertEqual(32140, ring.srs.srid)
self.assertEqual('NAD83 / Texas South Central', ring.srs.name)
ring.srs = str(SpatialReference(4326)) # back to WGS84
@ -187,7 +252,58 @@ class OGRGeomTest(unittest.TestCase):
self.assertEqual('WGS 72', ring.srs.name)
self.assertEqual(4322, ring.srid)
def test10_difference(self):
"Testing difference()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
d1 = OGRGeometry(diff_geoms[i].wkt)
d2 = a.difference(b)
self.assertEqual(d1, d2)
self.assertEqual(d1, a - b) # __sub__ is difference operator
a -= b # testing __isub__
self.assertEqual(d1, a)
def test11_intersection(self):
"Testing intersects() and intersection()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
i1 = OGRGeometry(intersect_geoms[i].wkt)
self.assertEqual(True, a.intersects(b))
i2 = a.intersection(b)
self.assertEqual(i1, i2)
self.assertEqual(i1, a & b) # __and__ is intersection operator
a &= b # testing __iand__
self.assertEqual(i1, a)
def test12_symdifference(self):
"Testing sym_difference()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
d1 = OGRGeometry(sdiff_geoms[i].wkt)
d2 = a.sym_difference(b)
self.assertEqual(d1, d2)
self.assertEqual(d1, a ^ b) # __xor__ is symmetric difference operator
a ^= b # testing __ixor__
self.assertEqual(d1, a)
def test13_union(self):
"Testing union()."
for i in xrange(len(topology_geoms)):
g_tup = topology_geoms[i]
a = OGRGeometry(g_tup[0].wkt)
b = OGRGeometry(g_tup[1].wkt)
u1 = OGRGeometry(union_geoms[i].wkt)
u2 = a.union(b)
self.assertEqual(u1, u2)
self.assertEqual(u1, a | b) # __or__ is union operator
a |= b # testing __ior__
self.assertEqual(u1, a)
def suite():
s = unittest.TestSuite()