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:
parent
53da1e4794
commit
f1ada99997
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()])
|
||||
|
|
|
@ -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.')
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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():
|
|
@ -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
|
|
@ -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())
|
|
@ -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)
|
|
@ -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')
|
||||
|
|
|
@ -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())
|
Loading…
Reference in New Issue