mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
gis: Added an intial ctypes interface to GDAL/OGR
(1) SpatialReference allows for rich access to properties of spatial reference systems. (2) OGRGeometry provides access to the OGR Geometry classes -- may be accessed from models with the get_GEOM_ogr() extra instance method. git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5397 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
38ff3cff45
commit
b962a44a4d
@ -1,5 +1,6 @@
|
||||
# GEOS Routines
|
||||
from django.contrib.gis.geos import GEOSGeometry, hex_to_wkt, centroid, area
|
||||
from django.contrib.gis.gdal import OGRGeometry, SpatialReference
|
||||
|
||||
# Until model subclassing is a possibility, a mixin class is used to add
|
||||
# the necessary functions that may be contributed for geographic objects.
|
||||
@ -12,6 +13,11 @@ class GeoMixin:
|
||||
"Gets a GEOS Python object for the geometry."
|
||||
return GEOSGeometry(getattr(self, field.attname), 'hex')
|
||||
|
||||
def _get_GEOM_ogr(self, field, srid):
|
||||
"Gets an OGR Python object for the geometry."
|
||||
return OGRGeometry(hex_to_wkt(getattr(self, field.attname)),
|
||||
SpatialReference('EPSG:%d' % srid))
|
||||
|
||||
def _get_GEOM_wkt(self, field):
|
||||
"Gets the WKT of the geometry."
|
||||
hex = getattr(self, field.attname)
|
||||
|
@ -6,7 +6,7 @@ from django.utils.functional import curry
|
||||
from django.contrib.gis.geos import GEOSGeometry, GEOSException
|
||||
|
||||
#TODO: Flesh out widgets.
|
||||
#TODO: geos operations through fields as proxy.
|
||||
#TODO: GEOS and GDAL/OGR operations through fields as proxy.
|
||||
#TODO: pythonic usage, like "for point in zip.polygon" and "if point in polygon".
|
||||
|
||||
class GeometryField(Field):
|
||||
@ -38,8 +38,7 @@ class GeometryField(Field):
|
||||
AddGeometryColumn(...) PostGIS (and OGC standard) function.
|
||||
|
||||
Takes the style object (provides syntax highlighting) as well as the
|
||||
database table and field. The dimensions can be specified via
|
||||
the dim keyword as well.
|
||||
database table and field.
|
||||
"""
|
||||
sql = style.SQL_KEYWORD('SELECT ') + \
|
||||
style.SQL_TABLE('AddGeometryColumn') + '(' + \
|
||||
@ -81,8 +80,9 @@ class GeometryField(Field):
|
||||
def contribute_to_class(self, cls, name):
|
||||
super(GeometryField, self).contribute_to_class(cls, name)
|
||||
|
||||
# Adding the WKT accessor function for geometry
|
||||
# Adding needed accessor functions
|
||||
setattr(cls, 'get_%s_geos' % self.name, curry(cls._get_GEOM_geos, field=self))
|
||||
setattr(cls, 'get_%s_ogr' % self.name, curry(cls._get_GEOM_ogr, field=self, srid=self._srid))
|
||||
setattr(cls, 'get_%s_wkt' % self.name, curry(cls._get_GEOM_wkt, field=self))
|
||||
setattr(cls, 'get_%s_centroid' % self.name, curry(cls._get_GEOM_centroid, field=self))
|
||||
setattr(cls, 'get_%s_area' % self.name, curry(cls._get_GEOM_area, field=self))
|
||||
|
@ -1,6 +1,5 @@
|
||||
# This module is meant to re-define the helper routines used by the
|
||||
# django.db.models.query objects to be customized for PostGIS.
|
||||
from copy import copy
|
||||
from django.db import backend
|
||||
from django.db.models.query import LOOKUP_SEPARATOR, find_field, FieldFound, QUERY_TERMS, get_where_clause
|
||||
from django.utils.datastructures import SortedDict
|
||||
|
103
django/contrib/gis/gdal/DataSource.py
Normal file
103
django/contrib/gis/gdal/DataSource.py
Normal file
@ -0,0 +1,103 @@
|
||||
# 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.OGRError import OGRException, check_err
|
||||
from django.contrib.gis.gdal.Layer import Layer
|
||||
|
||||
"""
|
||||
DataSource is a wrapper for the OGR Data Source object, which provides
|
||||
an interface for reading vector geometry data from many different file
|
||||
formats (including ESRI shapefiles).
|
||||
|
||||
Example:
|
||||
ds = DataSource('/home/foo/bar.shp')
|
||||
for layer in ds:
|
||||
for feature in layer:
|
||||
# Getting the geometry for the feature.
|
||||
g = feature.geom
|
||||
|
||||
# Getting the 'description' field for the feature.
|
||||
desc = feature['description']
|
||||
|
||||
More documentation forthcoming.
|
||||
"""
|
||||
|
||||
# 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."
|
||||
|
||||
_ds = 0 # Initially NULL
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, ds_file):
|
||||
|
||||
# Registering all the drivers, this needs to be done
|
||||
# _before_ we try to open up a data source.
|
||||
if not lgdal.OGRRegisterAll():
|
||||
raise OGRException, 'Could not register all data source drivers!'
|
||||
|
||||
# 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_file), c_int(0), byref(ds_driver))
|
||||
|
||||
# Raise an exception if the returned pointer is NULL
|
||||
if not ds:
|
||||
self._ds = False
|
||||
raise OGRException, 'Invalid data source file "%s"' % ds_file
|
||||
else:
|
||||
self._ds = ds
|
||||
self._driver = ds_driver
|
||||
|
||||
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)
|
||||
|
||||
def __iter__(self):
|
||||
"Allows for iteration over the layers in a data source."
|
||||
for i in xrange(self.layer_count):
|
||||
yield self.__getitem__(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 not l: raise IndexError, 'invalid OGR Layer name given: "%s"' % index
|
||||
else:
|
||||
if index < 0 or index >= self.layer_count:
|
||||
raise IndexError, 'index out of range'
|
||||
l = lgdal.OGR_DS_GetLayer(self._ds, c_int(index))
|
||||
return Layer(l)
|
||||
|
||||
def __len__(self):
|
||||
"Returns the number of layers within the data source."
|
||||
return self.layer_count
|
||||
|
||||
def __str__(self):
|
||||
"Returns OGR GetName and Driver for the Data Source."
|
||||
return '%s (%s)' % (self.name, self.driver)
|
||||
|
||||
#### DataSource Properties ####
|
||||
@property
|
||||
def driver(self):
|
||||
"Returns the name of the data source driver."
|
||||
return string_at(lgdal.OGR_Dr_GetName(self._driver))
|
||||
|
||||
@property
|
||||
def layer_count(self):
|
||||
"Returns the number of layers in the data source."
|
||||
return lgdal.OGR_DS_GetLayerCount(self._ds)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"Returns the name of the data source."
|
||||
return string_at(lgdal.OGR_DS_GetName(self._ds))
|
||||
|
104
django/contrib/gis/gdal/Feature.py
Normal file
104
django/contrib/gis/gdal/Feature.py
Normal file
@ -0,0 +1,104 @@
|
||||
# types and ctypes
|
||||
import types
|
||||
from ctypes import c_char_p, c_int, 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.OGRError import OGRException
|
||||
from django.contrib.gis.gdal.Field import Field
|
||||
from django.contrib.gis.gdal.OGRGeometry import OGRGeometry, OGRGeomType
|
||||
|
||||
# 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):
|
||||
"A class that wraps an OGR Feature, needs to be instantiated from a Layer object."
|
||||
|
||||
_feat = 0 # Initially NULL
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, f):
|
||||
"Needs a C pointer (Python integer in ctypes) in order to initialize."
|
||||
if not f:
|
||||
raise OGRException, 'Cannot create OGR Feature, invalid pointer given.'
|
||||
self._feat = f
|
||||
self._fdefn = lgdal.OGR_F_GetDefnRef(f)
|
||||
|
||||
def __del__(self):
|
||||
"Releases a reference to this object."
|
||||
if self._fdefn: lgdal.OGR_FD_Release(self._fdefn)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Gets the Field at the specified index."
|
||||
if isinstance(index, types.StringType):
|
||||
i = self.index(index)
|
||||
else:
|
||||
if index < 0 or index > self.num_fields:
|
||||
raise IndexError, '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))))
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over each field in the Feature."
|
||||
for i in xrange(self.num_fields):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __len__(self):
|
||||
"Returns the count of fields in this feature."
|
||||
return self.num_fields
|
||||
|
||||
def __str__(self):
|
||||
"The string name of the feature."
|
||||
return 'Feature FID %d in Layer<%s>' % (self.fid, self.layer_name)
|
||||
|
||||
def __eq__(self, other):
|
||||
"Does equivalence testing on the features."
|
||||
if lgdal.OGR_F_Equal(self._feat, other._feat):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#### Feature Properties ####
|
||||
@property
|
||||
def fid(self):
|
||||
"Returns the feature identifier."
|
||||
return lgdal.OGR_F_GetFID(self._feat)
|
||||
|
||||
@property
|
||||
def layer_name(self):
|
||||
"Returns the name of the layer for the feature."
|
||||
return string_at(lgdal.OGR_FD_GetName(self._fdefn))
|
||||
|
||||
@property
|
||||
def num_fields(self):
|
||||
"Returns the number of fields in the Feature."
|
||||
return lgdal.OGR_F_GetFieldCount(self._feat)
|
||||
|
||||
@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)))
|
||||
for i in xrange(self.num_fields) ]
|
||||
@property
|
||||
def geom(self):
|
||||
"Returns the OGR Geometry for this Feature."
|
||||
# A clone is used, so destruction of the Geometry won't bork the Feature.
|
||||
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_F_GetGeometryRef(self._feat)))
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Returns the OGR Geometry Type for this Feture."
|
||||
return OGRGeomType(lgdal.OGR_FD_GetGeomType(self._fdefn))
|
||||
|
||||
#### Feature Methods ####
|
||||
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))
|
||||
if i < 0: raise IndexError, 'invalid OFT field name given: "%s"' % field_name
|
||||
return i
|
||||
|
||||
def clone(self):
|
||||
"Clones this Feature."
|
||||
return Feature(lgdal.OGR_F_Clone(self._feat))
|
83
django/contrib/gis/gdal/Field.py
Normal file
83
django/contrib/gis/gdal/Field.py
Normal file
@ -0,0 +1,83 @@
|
||||
from ctypes import string_at
|
||||
|
||||
from django.contrib.gis.gdal.libgdal import lgdal
|
||||
from django.contrib.gis.gdal.OGRError import OGRException
|
||||
|
||||
# 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):
|
||||
"A class that wraps an OGR Field."
|
||||
|
||||
_fld = 0 # Initially NULL
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, fld, val=''):
|
||||
"Needs a C pointer (Python integer in ctypes) in order to initialize."
|
||||
if not fld:
|
||||
raise OGRException, 'Cannot create OGR Field, invalid pointer given.'
|
||||
self._fld = fld
|
||||
self._val = val
|
||||
|
||||
# Setting the class depending upon the OGR Field Type (OFT)
|
||||
self.__class__ = FIELD_CLASSES[self.type]
|
||||
|
||||
def __str__(self):
|
||||
"Returns the string representation of the Field."
|
||||
return '%s (%s)' % (self.name, self.__class__.__name__)
|
||||
|
||||
#### Field Properties ####
|
||||
@property
|
||||
def name(self):
|
||||
"Returns the name of the field."
|
||||
return string_at(lgdal.OGR_Fld_GetNameRef(self._fld))
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"Returns the type of this field."
|
||||
return lgdal.OGR_Fld_GetType(self._fld)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
"Returns the value of this type of field."
|
||||
return self._val
|
||||
|
||||
# The Field sub-classes for each OGR Field type.
|
||||
class OFTInteger(Field):
|
||||
@property
|
||||
def value(self):
|
||||
"Returns an integer contained in this field."
|
||||
return int(self._val)
|
||||
|
||||
class OFTIntegerList(Field): pass
|
||||
class OFTReal(Field):
|
||||
@property
|
||||
def value(self):
|
||||
"Returns a float contained in this field."
|
||||
return float(self._val)
|
||||
|
||||
class OFTRealList(Field): pass
|
||||
class OFTString(Field): pass
|
||||
class OFTStringList(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
|
||||
|
||||
# Class mapping dictionary for OFT Types
|
||||
FIELD_CLASSES = { 0 : OFTInteger,
|
||||
1 : OFTIntegerList,
|
||||
2 : OFTReal,
|
||||
3 : OFTRealList,
|
||||
4 : OFTString,
|
||||
5 : OFTStringList,
|
||||
6 : OFTWideString,
|
||||
7 : OFTWideStringList,
|
||||
8 : OFTBinary,
|
||||
9 : OFTDate,
|
||||
10 : OFTTime,
|
||||
11 : OFTDateTime,
|
||||
}
|
28
django/contrib/gis/gdal/LICENSE
Normal file
28
django/contrib/gis/gdal/LICENSE
Normal file
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2007, Justin Bronn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of OGRGeometry nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
90
django/contrib/gis/gdal/Layer.py
Normal file
90
django/contrib/gis/gdal/Layer.py
Normal file
@ -0,0 +1,90 @@
|
||||
# Needed ctypes routines
|
||||
from ctypes import c_int, c_long, c_void_p, string_at
|
||||
|
||||
# The GDAL C Library
|
||||
from django.contrib.gis.gdal.libgdal import lgdal
|
||||
|
||||
# Other GDAL imports.
|
||||
from django.contrib.gis.gdal.Feature import Feature
|
||||
from django.contrib.gis.gdal.OGRError import OGRException
|
||||
from django.contrib.gis.gdal.SpatialReference import SpatialReference
|
||||
|
||||
# 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.
|
||||
|
||||
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."
|
||||
|
||||
_layer = 0 # Initially NULL
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, l):
|
||||
"Needs a C pointer (Python/ctypes integer) in order to initialize."
|
||||
if not l:
|
||||
raise OGRException, 'Cannot create Layer, invalid pointer given'
|
||||
self._layer = l
|
||||
self._ldefn = lgdal.OGR_L_GetLayerDefn(l)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Gets the Feature at the specified index."
|
||||
if index < 0 or index >= self.num_feat:
|
||||
raise IndexError, 'index out of range'
|
||||
return Feature(lgdal.OGR_L_GetFeature(self._layer, c_long(index)))
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over each Feature in the Layer."
|
||||
|
||||
# Resetting the Layer before beginning iteration
|
||||
lgdal.OGR_L_ResetReading(self._layer)
|
||||
|
||||
# Incrementing over each feature in the layer, and yielding
|
||||
# to the caller of the function.
|
||||
for i in xrange(self.num_feat):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __len__(self):
|
||||
"The length is the number of features."
|
||||
return self.num_feat
|
||||
|
||||
def __str__(self):
|
||||
"The string name of the layer."
|
||||
return self.name
|
||||
|
||||
#### Layer properties ####
|
||||
@property
|
||||
def name(self):
|
||||
"Returns the name of this layer in the Data Source."
|
||||
return string_at(lgdal.OGR_FD_GetName(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))
|
||||
|
||||
@property
|
||||
def num_fields(self):
|
||||
"Returns the number of fields in the Layer."
|
||||
return lgdal.OGR_FD_GetFieldCount(self._ldefn)
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Returns the geometry type (OGRGeomType) of the Layer."
|
||||
return lgdal.OGR_FD_GetGeomType(self._ldefn)
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"Returns the Spatial Reference used in this Layer."
|
||||
return SpatialReference(lgdal.OSRClone(lgdal.OGR_L_GetSpatialRef(self._layer)), 'ogr')
|
||||
|
||||
@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)))
|
||||
for i in xrange(self.num_fields) ]
|
||||
|
35
django/contrib/gis/gdal/OGRError.py
Normal file
35
django/contrib/gis/gdal/OGRError.py
Normal file
@ -0,0 +1,35 @@
|
||||
# OGR Error Codes
|
||||
OGRERR_NONE = 0
|
||||
OGRERR_NOT_ENOUGH_DATA = 1
|
||||
OGRERR_NOT_ENOUGH_MEMORY = 2
|
||||
OGRERR_UNSUPPORTED_GEOMETRY_TYPE = 3
|
||||
OGRERR_UNSUPPORTED_OPERATION = 4
|
||||
OGRERR_CORRUPT_DATA = 5
|
||||
OGRERR_FAILURE = 6
|
||||
OGRERR_UNSUPPORTED_SRS = 7
|
||||
|
||||
# OGR & SRS Exceptions
|
||||
class OGRException(Exception): pass
|
||||
class SRSException(Exception): pass
|
||||
|
||||
def check_err(code, msg=False):
|
||||
"Checks the given OGRERR, and raises an exception where appropriate."
|
||||
|
||||
if code == OGRERR_NONE:
|
||||
return
|
||||
elif code == OGRERR_NOT_ENOUGH_DATA:
|
||||
raise OGRException, 'Not enough data!'
|
||||
elif code == OGRERR_NOT_ENOUGH_MEMORY:
|
||||
raise OGRException, 'Not enough memory!'
|
||||
elif code == OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
|
||||
raise OGRException, 'Unsupported Geometry Type!'
|
||||
elif code == OGRERR_UNSUPPORTED_OPERATION:
|
||||
raise OGRException, 'Unsupported Operation!'
|
||||
elif code == OGRERR_CORRUPT_DATA:
|
||||
raise OGRException, 'Corrupt Data!'
|
||||
elif code == OGRERR_FAILURE:
|
||||
raise OGRException, 'OGR Failure!'
|
||||
elif code == OGRERR_UNSUPPORTED_SRS:
|
||||
raise SRSException, 'Unsupported SRS!'
|
||||
else:
|
||||
raise OGRException, 'Unknown error code: "%s"' % str(code)
|
340
django/contrib/gis/gdal/OGRGeometry.py
Normal file
340
django/contrib/gis/gdal/OGRGeometry.py
Normal file
@ -0,0 +1,340 @@
|
||||
# types & ctypes
|
||||
from types import StringType
|
||||
from ctypes import \
|
||||
byref, string_at, create_string_buffer, POINTER, \
|
||||
c_char_p, c_double, c_int, c_void_p
|
||||
|
||||
# Getting the GDAL C library and error checking facilities
|
||||
from django.contrib.gis.gdal.libgdal import lgdal
|
||||
from django.contrib.gis.gdal.OGRError import check_err, OGRException
|
||||
from django.contrib.gis.gdal.SpatialReference import SpatialReference, CoordTransform
|
||||
|
||||
# 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 ####
|
||||
def pnt_func(f):
|
||||
"For accessing point information."
|
||||
f.restype = c_double
|
||||
f.argtypes = [c_void_p, c_int]
|
||||
return f
|
||||
getx = pnt_func(lgdal.OGR_G_GetX)
|
||||
gety = pnt_func(lgdal.OGR_G_GetY)
|
||||
getz = pnt_func(lgdal.OGR_G_GetZ)
|
||||
|
||||
#### OGRGeomType ####
|
||||
class OGRGeomType(object):
|
||||
"Encapulates OGR Geometry Types."
|
||||
|
||||
# Ordered array of acceptable strings and their corresponding OGRwkbGeometryType
|
||||
__ogr_str = ['Unknown', 'Point', 'LineString', 'Polygon', 'MultiPoint',
|
||||
'MultiLineString', 'MultiPolygon', 'GeometryCollection',
|
||||
'None', 'LinearRing']
|
||||
__ogr_int = [0, 1, 2, 3, 4, 5, 6, 7, 100, 101]
|
||||
|
||||
def __init__(self, input):
|
||||
"Figures out the correct OGR Type based upon the input."
|
||||
if isinstance(input, OGRGeomType):
|
||||
self._index = input._index
|
||||
elif isinstance(input, StringType):
|
||||
idx = self._has_str(self.__ogr_str, input)
|
||||
if not idx:
|
||||
raise OGRException, 'Invalid OGR String Type "%s"' % input
|
||||
self._index = idx
|
||||
elif isinstance(input, int):
|
||||
if not input in self.__ogr_int:
|
||||
raise OGRException, 'Invalid OGR Integer Type: %d' % input
|
||||
self._index = self.__ogr_int.index(input)
|
||||
else:
|
||||
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."""
|
||||
if isinstance(other, OGRGeomType):
|
||||
return self._index == other._index
|
||||
elif isinstance(other, StringType):
|
||||
idx = self._has_str(self.__ogr_str, other)
|
||||
if idx: return self._index == idx
|
||||
return False
|
||||
elif isinstance(other, int):
|
||||
if not other in self.__ogr_int: return False
|
||||
return self.__ogr_int.index(other) == self._index
|
||||
else:
|
||||
raise TypeError, 'Cannot compare with type: %s' % str(type(other))
|
||||
|
||||
def _has_str(self, arr, s):
|
||||
slow = s.lower()
|
||||
for i in xrange(len(arr)):
|
||||
if slow == arr[i].lower(): return i
|
||||
return None
|
||||
|
||||
@property
|
||||
def django(self):
|
||||
"Returns the Django GeometryField for this OGR Type."
|
||||
s = self.__ogr_str[self._index]
|
||||
if s in ('Unknown', 'LinearRing'):
|
||||
return None
|
||||
else:
|
||||
return s + 'Field'
|
||||
|
||||
@property
|
||||
def num(self):
|
||||
"Returns the OGRwkbGeometryType number for the OGR Type."
|
||||
return self.__ogr_int[self._index]
|
||||
|
||||
#### OGRGeometry Class ####
|
||||
class OGRGeometry(object):
|
||||
"Generally encapsulates an OGR geometry."
|
||||
|
||||
_g = 0 # Initially NULL
|
||||
|
||||
def __init__(self, input, srs=False):
|
||||
"Initializes Geometry on either WKT or an OGR pointer as input."
|
||||
|
||||
if isinstance(input, StringType):
|
||||
# First, trying the input as WKT
|
||||
buf = c_char_p(input)
|
||||
g = c_void_p()
|
||||
|
||||
# Getting the spatial
|
||||
if not isinstance(srs, SpatialReference):
|
||||
s = SpatialReference() # creating an empty spatial reference
|
||||
else:
|
||||
s = srs.clone() # cloning the given spatial reference
|
||||
|
||||
try:
|
||||
check_err(lgdal.OGR_G_CreateFromWkt(byref(buf), s._srs, byref(g)))
|
||||
except OGRException, msg:
|
||||
try:
|
||||
ogr_t = OGRGeomType(input) # Seeing if the input is a valid short-hand string
|
||||
g = lgdal.OGR_G_CreateGeometry(ogr_t.num)
|
||||
except:
|
||||
raise OGRException, 'Could not initialize on WKT "%s"' % input
|
||||
elif isinstance(input, OGRGeomType):
|
||||
g = lgdal.OGR_G_CreateGeometry(input.num)
|
||||
elif isinstance(input, int):
|
||||
# OGR Pointer (integer) was the input
|
||||
g = input
|
||||
else:
|
||||
raise OGRException, 'Type of input cannot be determined!'
|
||||
|
||||
# Now checking the Geometry pointer before finishing initialization
|
||||
if not g:
|
||||
raise OGRException, 'Cannot create OGR Geometry from input: %s' % str(input)
|
||||
self._g = g
|
||||
|
||||
# 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)
|
||||
|
||||
def __eq__(self, other):
|
||||
"Is this Geometry equal to the other?"
|
||||
return lgdal.OGR_G_Equals(self._g, other._g)
|
||||
|
||||
def __str__(self):
|
||||
"WKT is used for the string representation."
|
||||
return self.wkt
|
||||
|
||||
#### Geometry Properties ####
|
||||
@property
|
||||
def dimension(self):
|
||||
"Returns 0 for points, 1 for lines, and 2 for surfaces."
|
||||
return lgdal.OGR_G_GetDimension(self._g)
|
||||
|
||||
@property
|
||||
def coord_dim(self):
|
||||
"Returns the coordinate dimension of the Geometry."
|
||||
return lgdal.OGR_G_GetCoordinateDimension(self._g)
|
||||
|
||||
@property
|
||||
def geom_count(self):
|
||||
"The number of elements in this Geometry."
|
||||
return lgdal.OGR_G_GetGeometryCount(self._g)
|
||||
|
||||
@property
|
||||
def point_count(self):
|
||||
"The number of Points in this Geometry."
|
||||
return lgdal.OGR_G_GetPointCount(self._g)
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"Returns the Spatial Reference for this Geometry."
|
||||
return SpatialReference(lgdal.OSRClone(lgdal.OGR_G_GetSpatialReference(self._g)), 'ogr')
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Returns the Type for this Geometry."
|
||||
return OGRGeomType(lgdal.OGR_G_GetGeometryType(self._g))
|
||||
|
||||
@property
|
||||
def geom_name(self):
|
||||
"Returns the Name of this Geometry."
|
||||
return string_at(lgdal.OGR_G_GetGeometryName(self._g))
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Returns the WKT form of the Geometry."
|
||||
buf = c_char_p()
|
||||
check_err(lgdal.OGR_G_ExportToWkt(self._g, byref(buf)))
|
||||
return string_at(buf)
|
||||
|
||||
#### Geometry Methods ####
|
||||
def clone(self):
|
||||
"Clones this OGR Geometry."
|
||||
return OGRGeometry(lgdal.OGR_G_Clone(self._g))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
# The subclasses for OGR Geometry.
|
||||
class Point(OGRGeometry):
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"Returns the X coordinate for this Point."
|
||||
return getx(self._g, c_int(0))
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"Returns the Y coordinate for this Point."
|
||||
return gety(self._g, c_int(0))
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"Returns the Z coordinate for this Point."
|
||||
return getz(self._g, c_int(0))
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Returns the tuple of this point."
|
||||
if self.coord_dim == 1:
|
||||
return (self.x,)
|
||||
elif self.coord_dim == 2:
|
||||
return (self.x, self.y)
|
||||
elif self.coord_dim == 3:
|
||||
return (self.x, self.y, self.z)
|
||||
|
||||
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:
|
||||
return (x.value,)
|
||||
elif self.coord_dim == 2:
|
||||
return (x.value, y.value)
|
||||
elif self.coord_dim == 3:
|
||||
return (x.value, y.value, z.value)
|
||||
else:
|
||||
raise IndexError, 'index out of range'
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over each point in the LineString."
|
||||
for i in xrange(self.point_count):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __len__(self, index):
|
||||
"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))
|
||||
|
||||
# LinearRings are used in Polygons.
|
||||
class LinearRing(LineString): pass
|
||||
|
||||
class Polygon(OGRGeometry):
|
||||
|
||||
def __len__(self):
|
||||
"The number of interior rings in this Polygon."
|
||||
return self.geom_count
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates through each ring in the Polygon."
|
||||
for i in xrange(self.geom_count):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Gets the ring at the specified index."
|
||||
if index < 0 or index >= self.geom_count:
|
||||
raise IndexError, 'index out of range'
|
||||
else:
|
||||
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))))
|
||||
|
||||
# Polygon Properties
|
||||
@property
|
||||
def shell(self):
|
||||
"Returns the shell of this Polygon."
|
||||
return self.__getitem__(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))
|
||||
|
||||
# Geometry Collection base class.
|
||||
class GeometryCollection(OGRGeometry):
|
||||
"The Geometry Collection class."
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Gets the Geometry at the specified index."
|
||||
if index < 0 or index >= self.geom_count:
|
||||
raise IndexError, 'index out of range'
|
||||
else:
|
||||
return OGRGeometry(lgdal.OGR_G_Clone(lgdal.OGR_G_GetGeometryRef(self._g, c_int(index))))
|
||||
|
||||
def __iter__(self):
|
||||
"Iterates over each Geometry."
|
||||
for i in xrange(self.geom_count):
|
||||
yield self.__getitem__(i)
|
||||
|
||||
def __len__(self):
|
||||
"The number of geometries in this Geometry Collection."
|
||||
return self.geom_count
|
||||
|
||||
def add(self, geom):
|
||||
"Add the geometry to this Geometry Collection."
|
||||
if not isinstance(geom, OGRGeometry):
|
||||
raise OGRException, 'Must add an OGRGeometry.'
|
||||
lgdal.OGR_G_AddGeometry(self._g, geom._g)
|
||||
|
||||
# Multiple Geometry types.
|
||||
class MultiPoint(GeometryCollection): pass
|
||||
class MultiLineString(GeometryCollection): pass
|
||||
class MultiPolygon(GeometryCollection): pass
|
||||
|
||||
# Class mapping dictionary (using the OGRwkbGeometryType as the key)
|
||||
GEO_CLASSES = {1 : Point,
|
||||
2 : LineString,
|
||||
3 : Polygon,
|
||||
4 : MultiPoint,
|
||||
5 : MultiLineString,
|
||||
6 : MultiPolygon,
|
||||
7 : GeometryCollection,
|
||||
}
|
299
django/contrib/gis/gdal/SpatialReference.py
Normal file
299
django/contrib/gis/gdal/SpatialReference.py
Normal file
@ -0,0 +1,299 @@
|
||||
# Getting what we need from ctypes
|
||||
import re
|
||||
from types import StringType, 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
|
||||
|
||||
# Getting the error checking routine and exceptions
|
||||
from django.contrib.gis.gdal.OGRError 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)
|
||||
|
||||
#### 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.'"""
|
||||
|
||||
_srs = 0 # Initially NULL
|
||||
|
||||
# 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)
|
||||
|
||||
#### Python 'magic' routines ####
|
||||
def __init__(self, input='', srs_type='wkt'):
|
||||
"Creates a spatial reference object from the given OGC Well Known Text (WKT)."
|
||||
|
||||
# Creating an initial empty string buffer.
|
||||
buf = c_char_p('')
|
||||
|
||||
if isinstance(input, StringType):
|
||||
# Is this an EPSG well known name?
|
||||
m = self._epsg_regex.match(input)
|
||||
if m:
|
||||
srs_type = 'epsg'
|
||||
input = int(m.group('epsg'))
|
||||
# Is this a short-hand well known name?
|
||||
elif input in self._well_known:
|
||||
srs_type = 'epsg'
|
||||
input = self._well_known[input]
|
||||
elif srs_type == 'proj':
|
||||
pass
|
||||
else:
|
||||
buf = c_char_p(input)
|
||||
elif isinstance(input, int):
|
||||
if srs_type not in ('epsg', 'ogr'):
|
||||
raise SRSException, 'Integer input requires SRS type of "ogr" or "epsg".'
|
||||
else:
|
||||
raise TypeError, 'Invalid SRS type "%s"' % srs_type
|
||||
|
||||
# Calling OSRNewSpatialReference with the string buffer.
|
||||
if srs_type == 'ogr':
|
||||
srs = input # Input is OGR pointer
|
||||
else:
|
||||
srs = lgdal.OSRNewSpatialReference(buf)
|
||||
|
||||
# If the pointer is NULL, throw an exception.
|
||||
if not srs:
|
||||
raise SRSException, 'Could not create spatial reference from WKT!'
|
||||
else:
|
||||
self._srs = srs
|
||||
|
||||
# Post-processing if in PROJ.4 or EPSG formats.
|
||||
if srs_type == 'proj': self.import_proj(input)
|
||||
elif srs_type == 'epsg': self.import_epsg(input)
|
||||
|
||||
def __del__(self):
|
||||
"Destroys this spatial reference."
|
||||
if self._srs: lgdal.OSRRelease(self._srs)
|
||||
|
||||
def __getitem__(self, target):
|
||||
"""Returns the value of the given string attribute node, None if the node doesn't exist.
|
||||
Can also take a tuple as a parameter, (target, child), where child is the child index to get."""
|
||||
if isinstance(target, TupleType):
|
||||
return self.attr_value(*target)
|
||||
else:
|
||||
return self.attr_value(target)
|
||||
|
||||
def __str__(self):
|
||||
"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."""
|
||||
ptr = lgdal.OSRGetAttrValue(self._srs, c_char_p(target), c_int(index))
|
||||
return self._string_ptr(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')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"Returns the name of this Spatial Reference."
|
||||
if self.projected: return self.attr_value('PROJCS')
|
||||
elif self.geographic: return self.attr_value('GEOGCS')
|
||||
elif self.local: return self.attr_value('LOCAL_CS')
|
||||
else: 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
|
||||
|
||||
@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)
|
||||
|
||||
@property
|
||||
def angular_name(self):
|
||||
"Returns the name of the angular units."
|
||||
self._cache_angular()
|
||||
return self._angular_name
|
||||
|
||||
@property
|
||||
def angular_units(self):
|
||||
"Returns the value of the angular units."
|
||||
self._cache_angular()
|
||||
return self._angular_units
|
||||
|
||||
#### Spheroid/Ellipsis Properties ####
|
||||
@property
|
||||
def semi_major(self):
|
||||
"Gets 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
|
||||
|
||||
@property
|
||||
def semi_minor(self):
|
||||
"Gets the Semi Minor Axis for this Spatial Reference."
|
||||
err = c_int()
|
||||
sm = semi_minor(self._srs, byref(err))
|
||||
check_err(err.value)
|
||||
return sm
|
||||
|
||||
@property
|
||||
def inverse_flattening(self):
|
||||
"Gets the Inverse Flattening for this Spatial Reference."
|
||||
err = c_int()
|
||||
inv_flat = invflattening(self._srs, byref(err))
|
||||
check_err(err.value)
|
||||
return inv_flat
|
||||
|
||||
#### Boolean Properties ####
|
||||
@property
|
||||
def geographic(self):
|
||||
"Returns True if this SpatialReference is geographic (root node is GEOGCS)."
|
||||
if lgdal.OSRIsGeographic(self._srs): return True
|
||||
else: return False
|
||||
|
||||
@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
|
||||
|
||||
@property
|
||||
def projected(self):
|
||||
"Returns True if this SpatialReference is a projected coordinate system (root node is PROJCS)."
|
||||
if lgdal.OSRIsProjected(self._srs): return True
|
||||
else: return False
|
||||
|
||||
#### 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)))
|
||||
|
||||
def import_proj(self, proj):
|
||||
"Imports the Spatial Reference from a PROJ.4 string."
|
||||
check_err(lgdal.OSRImportFromProj4(self._srs, create_string_buffer(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)))
|
||||
|
||||
def import_xml(self, xml):
|
||||
"Imports the Spatial Reference from an XML string."
|
||||
check_err(lgdal.OSRImportFromXML(self._srs, create_string_buffer(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)))
|
||||
return string_at(w)
|
||||
|
||||
@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)))
|
||||
return string_at(w)
|
||||
|
||||
@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)))
|
||||
return string_at(w)
|
||||
|
||||
@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)
|
||||
|
||||
class CoordTransform(object):
|
||||
"A coordinate system transformation object."
|
||||
|
||||
_ct = 0 # Initially NULL
|
||||
|
||||
def __init__(self, source, target):
|
||||
"Initializes on a source and target SpatialReference objects."
|
||||
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:
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
return 'Transform from "%s" to "%s"' % (str(self._srs1_name), str(self._srs2_name))
|
||||
|
5
django/contrib/gis/gdal/__init__.py
Normal file
5
django/contrib/gis/gdal/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from DataSource import DataSource
|
||||
from SpatialReference import SpatialReference, CoordTransform
|
||||
from OGRGeometry import OGRGeometry, OGRGeomType
|
||||
from OGRError import check_err, OGRException, SRSException
|
||||
|
22
django/contrib/gis/gdal/libgdal.py
Normal file
22
django/contrib/gis/gdal/libgdal.py
Normal file
@ -0,0 +1,22 @@
|
||||
import os, sys
|
||||
from ctypes import CDLL
|
||||
|
||||
if os.name == 'nt':
|
||||
# Windows NT library
|
||||
lib_name = 'libgdal-1.dll'
|
||||
elif os.name == 'posix':
|
||||
platform = os.uname()[0]
|
||||
if platform == 'Linux':
|
||||
# Linux shared library
|
||||
lib_name = 'libgdal.so'
|
||||
elif platform == 'Darwin':
|
||||
# Mac OSX Shared Library
|
||||
lib_name = 'libgdal.dylib'
|
||||
else:
|
||||
raise GDALException, 'Unknown POSIX platform "%s"' % platform
|
||||
else:
|
||||
raise GDALException, 'Unsupported OS "%s"' % os.name
|
||||
|
||||
# The GDAL C library
|
||||
lgdal = CDLL(lib_name)
|
||||
|
@ -1,26 +1,29 @@
|
||||
import re
|
||||
from django.db import models
|
||||
|
||||
# Checking for the presence of GDAL
|
||||
try:
|
||||
from django.contrib.gis.gdal import SpatialReference
|
||||
HAS_OSR = True
|
||||
except ImportError:
|
||||
HAS_OSR = False
|
||||
|
||||
"""
|
||||
Models for the PostGIS/OGC database tables.
|
||||
"""
|
||||
|
||||
# For pulling out the spheroid from the spatial reference string.
|
||||
# For pulling out the spheroid from the spatial reference string. This
|
||||
# regular expression is used only if the user does not have GDAL installed.
|
||||
# TODO: Flattening not used in all ellipsoids, could also be a minor axis, or 'b'
|
||||
# parameter.
|
||||
spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
|
||||
|
||||
# For pulling out the projected coordinate system units. Python regexs are greedy
|
||||
# by default, so this should get the units of the projection instead (PROJCS)
|
||||
# of the units for the geographic coordinate system (GEOGCS).
|
||||
unit_regex = re.compile(r'^PROJCS.+UNIT\[\"(?P<units>[a-z]+)\", ?(?P<conversion>[0-9\.]+), ?(AUTHORITY\[\"(?P<authority>[a-z0-9 \.]+)\",[ ]?\"(?P<code>\d+)\"\])?', re.I)
|
||||
|
||||
# This is the global 'geometry_columns' from PostGIS.
|
||||
# See PostGIS Documentation at Ch. 4.2.2
|
||||
class GeometryColumns(models.Model):
|
||||
f_table_catalog = models.CharField(maxlength=256)
|
||||
f_table_schema = models.CharField(maxlength=256)
|
||||
f_table_name = models.CharField(maxlength=256)
|
||||
f_table_name = models.CharField(maxlength=256, primary_key=True)
|
||||
f_geometry_column = models.CharField(maxlength=256)
|
||||
coord_dimension = models.IntegerField()
|
||||
srid = models.IntegerField()
|
||||
@ -29,6 +32,9 @@ class GeometryColumns(models.Model):
|
||||
class Meta:
|
||||
db_table = 'geometry_columns'
|
||||
|
||||
def __str__(self):
|
||||
return "%s.%s - %dD %s field (SRID: %d)" % (self.f_table_name, self.f_geometry_column, self.coord_dimension, self.type, self.srid)
|
||||
|
||||
# This is the global 'spatial_ref_sys' table from PostGIS.
|
||||
# See PostGIS Documentation at Ch. 4.2.1
|
||||
class SpatialRefSys(models.Model):
|
||||
@ -36,32 +42,117 @@ class SpatialRefSys(models.Model):
|
||||
auth_name = models.CharField(maxlength=256)
|
||||
auth_srid = models.IntegerField()
|
||||
srtext = models.CharField(maxlength=2048)
|
||||
proj4text = models.CharField(maxlength=2048)
|
||||
proj4 = models.CharField(maxlength=2048, db_column='proj4text')
|
||||
|
||||
class Meta:
|
||||
db_table = 'spatial_ref_sys'
|
||||
|
||||
def _cache_osr(self):
|
||||
"Caches a GDAL OSR object for this Spatial Reference."
|
||||
if HAS_OSR:
|
||||
if not hasattr(self, '_srs'):
|
||||
# Trying to get from WKT first
|
||||
try:
|
||||
self._srs = SpatialReference(self.srtext, 'wkt')
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
# Trying the proj4 text next
|
||||
try:
|
||||
self._srs = SpatialReference(self.proj4, 'proj4')
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
raise Exception, 'Could not get a OSR Spatial Reference.'
|
||||
else:
|
||||
raise Exception, 'GDAL is not installed!'
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
self._cache_osr()
|
||||
return self._srs.clone()
|
||||
|
||||
@property
|
||||
def spheroid(self):
|
||||
"Pulls out the spheroid from the srtext."
|
||||
m = spheroid_regex.match(self.srtext)
|
||||
if m:
|
||||
return (m.group('name'), float(m.group('major')), float(m.group('flattening')))
|
||||
def ellipsoid(self):
|
||||
"""Returns a tuple of the ellipsoid parameters:
|
||||
(semimajor axis, semiminor axis, and inverse flattening)."""
|
||||
if HAS_OSR:
|
||||
# Setting values initially to False
|
||||
self._cache_osr()
|
||||
major = self._srs.semi_major
|
||||
minor = self._srs.semi_minor
|
||||
invflat = self._srs.inverse_flattening
|
||||
return (major, minor, invflat)
|
||||
else:
|
||||
return None
|
||||
m = spheroid_regex.match(self.srtext)
|
||||
if m: return (float(m.group('major')), float(m.group('flattening')))
|
||||
else: return None
|
||||
|
||||
@property
|
||||
def projected_units(self):
|
||||
"If the spatial reference system is projected, get the units or return None."
|
||||
m = unit_regex.match(self.srtext)
|
||||
if m:
|
||||
if m.group('authority'):
|
||||
authority = (m.group('authority'), int(m.group('code')))
|
||||
else:
|
||||
authority = None
|
||||
return (m.group('units'), float(m.group('conversion')), authority)
|
||||
else:
|
||||
return None
|
||||
def name(self):
|
||||
"Returns the projection name."
|
||||
self._cache_osr()
|
||||
return self._srs.name
|
||||
|
||||
@property
|
||||
def spheroid(self):
|
||||
"Returns the spheroid for this spatial reference."
|
||||
self._cache_osr()
|
||||
return self._srs['spheroid']
|
||||
|
||||
@property
|
||||
def datum(self):
|
||||
"Returns the datum for this spatial reference."
|
||||
self._cache_osr()
|
||||
return self._srs['datum']
|
||||
|
||||
@property
|
||||
def projected(self):
|
||||
"Is this Spatial Reference projected?"
|
||||
self._cache_osr()
|
||||
return self._srs.projected
|
||||
|
||||
@property
|
||||
def local(self):
|
||||
"Is this Spatial Reference local?"
|
||||
self._cache_osr()
|
||||
return self._srs.local
|
||||
|
||||
@property
|
||||
def geographic(self):
|
||||
"Is this Spatial Reference geographic?"
|
||||
self._cache_osr()
|
||||
return self._srs.geographic
|
||||
|
||||
@property
|
||||
def linear_name(self):
|
||||
"Returns the linear units name."
|
||||
self._cache_osr()
|
||||
return self._srs.linear_name
|
||||
|
||||
@property
|
||||
def linear_units(self):
|
||||
"Returns the linear units."
|
||||
self._cache_osr()
|
||||
return self._srs.linear_units
|
||||
|
||||
@property
|
||||
def angular_units(self):
|
||||
"Returns the angular units."
|
||||
self._cache_osr()
|
||||
return self._srs.angular_units
|
||||
|
||||
@property
|
||||
def angular_name(self):
|
||||
"Returns the name of the angular units."
|
||||
self._cache_osr()
|
||||
return self._srs.angular_name
|
||||
|
||||
def __str__(self):
|
||||
return "%d - %s " % (self.srid, self.auth_name)
|
||||
"Returns the string representation. If GDAL is installed, it will be 'pretty' OGC WKT."
|
||||
if HAS_OSR:
|
||||
self._cache_osr()
|
||||
if hasattr(self, '_srs'): return str(self._srs)
|
||||
return "%d:%s " % (self.srid, self.auth_name)
|
||||
|
@ -0,0 +1,14 @@
|
||||
from unittest import TestSuite, makeSuite, TextTestRunner
|
||||
import test_geos, test_gdal_ds, test_gdal_srs, test_gdal_geom, test_spatialrefsys
|
||||
|
||||
def suite():
|
||||
s = TestSuite()
|
||||
s.addTest(test_geos.suite())
|
||||
s.addTest(test_gdal_ds.suite())
|
||||
s.addTest(test_gdal_srs.suite())
|
||||
s.addTest(test_gdal_geom.suite())
|
||||
s.addTest(test_spatialrefsys.suite())
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
TextTestRunner(verbosity=verbosity).run(suite())
|
125
django/contrib/gis/tests/test_gdal_ds.py
Normal file
125
django/contrib/gis/tests/test_gdal_ds.py
Normal file
@ -0,0 +1,125 @@
|
||||
import os, os.path, unittest
|
||||
from django.contrib.gis.gdal import DataSource, OGRException
|
||||
from django.contrib.gis.gdal.Field import OFTReal, OFTInteger, OFTString
|
||||
|
||||
# Path for SHP files
|
||||
shp_path = os.path.dirname(__file__)
|
||||
def get_shp(name):
|
||||
return shp_path + os.sep + name + os.sep + name + '.shp'
|
||||
|
||||
# Test SHP data source object
|
||||
class TestSHP:
|
||||
def __init__(self, shp, **kwargs):
|
||||
self.ds = get_shp(shp)
|
||||
for key, value in kwargs.items():
|
||||
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,},
|
||||
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,},
|
||||
srs_wkt='GEOGCS["GCS_WGS_1984",DATUM["WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]'),
|
||||
)
|
||||
|
||||
bad_ds = (TestSHP('foo'),
|
||||
)
|
||||
|
||||
class DataSourceTest(unittest.TestCase):
|
||||
|
||||
def test01_valid_shp(self):
|
||||
"Testing valid SHP Data Source files."
|
||||
|
||||
for source in ds_list:
|
||||
# Loading up the data source
|
||||
ds = DataSource(source.ds)
|
||||
|
||||
# Making sure the layer count is what's expected (only 1 layer in a SHP file)
|
||||
self.assertEqual(1, len(ds))
|
||||
|
||||
# Making sure GetName works
|
||||
self.assertEqual(source.ds, ds.name)
|
||||
|
||||
# Making sure the driver name matches up
|
||||
self.assertEqual('ESRI Shapefile', ds.driver)
|
||||
|
||||
# Making sure indexing works
|
||||
try:
|
||||
ds[len(ds)]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
self.fail('Expected an IndexError!')
|
||||
|
||||
def test02_invalid_shp(self):
|
||||
"Testing invalid SHP files for the Data Source."
|
||||
for source in bad_ds:
|
||||
self.assertRaises(OGRException, DataSource, source.ds)
|
||||
|
||||
def test03_layers(self):
|
||||
"Testing Data Source Layers."
|
||||
|
||||
for source in ds_list:
|
||||
ds = DataSource(source.ds)
|
||||
|
||||
# Incrementing through each layer, this tests __iter__
|
||||
for layer in ds:
|
||||
# Making sure we get the number of features we expect
|
||||
self.assertEqual(len(layer), source.nfeat)
|
||||
|
||||
# Making sure we get the number of fields we expect
|
||||
self.assertEqual(layer.num_fields, source.nfld)
|
||||
self.assertEqual(len(layer.fields), source.nfld)
|
||||
|
||||
# Now checking the field names.
|
||||
flds = layer.fields
|
||||
for f in flds: self.assertEqual(True, f in source.fields)
|
||||
|
||||
def test04_features(self):
|
||||
"Testing Data Source Features."
|
||||
for source in ds_list:
|
||||
ds = DataSource(source.ds)
|
||||
|
||||
# Incrementing through each layer
|
||||
for layer in ds:
|
||||
# Incrementing through each feature in the layer
|
||||
for feat in layer:
|
||||
# Making sure the number of fields is what's expected.
|
||||
self.assertEqual(source.nfld, len(feat))
|
||||
self.assertEqual(source.gtype, feat.geom_type)
|
||||
|
||||
# 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 (which asserts the type)
|
||||
self.assertEqual('%s (%s)' % (k, v.__name__), str(fld))
|
||||
|
||||
# Testing __iter__ on the Feature
|
||||
for fld in feat: self.assertEqual(fld.name in source.fields.keys(), True)
|
||||
|
||||
def test05_geometries(self):
|
||||
"Testing Geometries from Data Source Features."
|
||||
for source in ds_list:
|
||||
ds = DataSource(source.ds)
|
||||
|
||||
# Incrementing through each layer and feature.
|
||||
for layer in ds:
|
||||
for feat in layer:
|
||||
g = feat.geom
|
||||
|
||||
# Making sure we get the right Geometry name & type
|
||||
self.assertEqual(source.geom, g.geom_name)
|
||||
self.assertEqual(source.gtype, g.geom_type)
|
||||
|
||||
# Making sure the SpatialReference is as expected.
|
||||
self.assertEqual(source.srs_wkt, g.srs.wkt)
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(DataSourceTest))
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
unittest.TextTestRunner(verbosity=verbosity).run(suite())
|
||||
|
||||
|
79
django/contrib/gis/tests/test_gdal_geom.py
Normal file
79
django/contrib/gis/tests/test_gdal_geom.py
Normal file
@ -0,0 +1,79 @@
|
||||
import unittest
|
||||
from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, OGRException
|
||||
from geometries import *
|
||||
|
||||
class OGRGeomTest(unittest.TestCase):
|
||||
"This tests the OGR Geometry."
|
||||
|
||||
def test00_geomtype(self):
|
||||
"Testing OGRGeomType object."
|
||||
|
||||
# OGRGeomType should initialize on all these inputs.
|
||||
try:
|
||||
g = OGRGeomType(0)
|
||||
g = OGRGeomType(1)
|
||||
g = OGRGeomType(7)
|
||||
g = OGRGeomType('point')
|
||||
g = OGRGeomType('GeometrycollectioN')
|
||||
except:
|
||||
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)
|
||||
|
||||
# Equivalence can take strings, ints, and other OGRGeomTypes
|
||||
self.assertEqual(True, OGRGeomType(1) == OGRGeomType(1))
|
||||
self.assertEqual(True, OGRGeomType(7) == 'GeometryCollection')
|
||||
self.assertEqual(True, OGRGeomType('point') == 'POINT')
|
||||
self.assertEqual(False, OGRGeomType('point') == 2)
|
||||
self.assertEqual(True, OGRGeomType(6) == 'MULtiPolyGON')
|
||||
|
||||
def test01_wkt(self):
|
||||
"Testing WKT output."
|
||||
for g in wkt_out:
|
||||
geom = OGRGeometry(g.wkt)
|
||||
|
||||
def test02_points(self):
|
||||
"Testing Point objects."
|
||||
|
||||
prev = OGRGeometry('POINT(0 0)')
|
||||
for p in points:
|
||||
if not hasattr(p, 'z'): # No 3D
|
||||
pnt = OGRGeometry(p.wkt)
|
||||
self.assertEqual(pnt.geom_type, 1)
|
||||
self.assertEqual(p.x, pnt.x)
|
||||
self.assertEqual(p.y, pnt.y)
|
||||
self.assertEqual((p.x, p.y), pnt.tuple)
|
||||
|
||||
def test03_polygons(self):
|
||||
"Testing Polygon objects."
|
||||
for p in polygons:
|
||||
poly = OGRGeometry(p.wkt)
|
||||
first = True
|
||||
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)
|
||||
|
||||
def test04_multipoints(self):
|
||||
"Testing MultiPoint objects."
|
||||
|
||||
for mp in multipoints:
|
||||
mgeom1 = OGRGeometry(mp.wkt) # First one from WKT
|
||||
mgeom2 = OGRGeometry('MULTIPOINT') # Creating empty multipoint
|
||||
for g in mgeom1:
|
||||
mgeom2.add(g) # adding each point from the multipoint
|
||||
self.assertEqual(mgeom1, mgeom2) # they should equal
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(OGRGeomTest))
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
unittest.TextTestRunner(verbosity=verbosity).run(suite())
|
144
django/contrib/gis/tests/test_gdal_srs.py
Normal file
144
django/contrib/gis/tests/test_gdal_srs.py
Normal file
@ -0,0 +1,144 @@
|
||||
import unittest
|
||||
from django.contrib.gis.gdal import SpatialReference, CoordTransform, OGRException, SRSException
|
||||
|
||||
class TestSRS:
|
||||
def __init__(self, wkt, **kwargs):
|
||||
self.wkt = wkt
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
# Some Spatial Reference examples
|
||||
srlist = (TestSRS('GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
|
||||
proj='+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
|
||||
epsg=4326, projected=False, geographic=True, local=False,
|
||||
lin_name='unknown', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199,
|
||||
auth={'GEOGCS' : ('EPSG', '4326'), 'spheroid' : ('EPSG', '7030')},
|
||||
attr=(('DATUM', 'WGS_1984'), (('SPHEROID', 1), '6378137'),('primem|authority', 'EPSG'),),
|
||||
),
|
||||
TestSRS('PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
|
||||
proj='+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
|
||||
epsg=32140, projected=True, geographic=False, local=False,
|
||||
lin_name='metre', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199,
|
||||
auth={'PROJCS' : ('EPSG', '32140'), 'spheroid' : ('EPSG', '7019'), 'unit' : ('EPSG', '9001'),},
|
||||
attr=(('DATUM', 'North_American_Datum_1983'),(('SPHEROID', 2), '298.257222101'),('PROJECTION','Lambert_Conformal_Conic_2SP'),),
|
||||
),
|
||||
TestSRS('PROJCS["NAD_1983_StatePlane_Texas_South_Central_FIPS_4204_Feet",GEOGCS["GCS_North_American_1983",DATUM["North_American_Datum_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["False_Easting",1968500.0],PARAMETER["False_Northing",13123333.33333333],PARAMETER["Central_Meridian",-99.0],PARAMETER["Standard_Parallel_1",28.38333333333333],PARAMETER["Standard_Parallel_2",30.28333333333334],PARAMETER["Latitude_Of_Origin",27.83333333333333],UNIT["Foot_US",0.3048006096012192]]',
|
||||
proj='+proj=lcc +lat_1=28.38333333333333 +lat_2=30.28333333333334 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=3999999.999999999 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs ',
|
||||
epsg=None, projected=True, geographic=False, local=False,
|
||||
lin_name='Foot_US', ang_name='Degree', lin_units=0.3048006096012192, ang_units=0.0174532925199,
|
||||
auth={'PROJCS' : (None, None),},
|
||||
attr=(('PROJCS|GeOgCs|spheroid', 'GRS_1980'),(('projcs', 9), 'UNIT'), (('projcs', 11), None),),
|
||||
),
|
||||
# This is really ESRI format, not WKT -- but the import should work the same
|
||||
TestSRS('LOCAL_CS["Non-Earth (Meter)",LOCAL_DATUM["Local Datum",0],UNIT["Meter",1.0],AXIS["X",EAST],AXIS["Y",NORTH]]',
|
||||
esri=True, proj=None, epsg=None, projected=False, geographic=False, local=True,
|
||||
lin_name='Meter', ang_name='degree', lin_units=1.0, ang_units=0.0174532925199,
|
||||
attr=(('LOCAL_DATUM', 'Local Datum'), ('unit', 'Meter')),
|
||||
),
|
||||
)
|
||||
|
||||
# Well-Known Names
|
||||
well_known = (TestSRS('GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]', wk='WGS84'),
|
||||
TestSRS('GEOGCS["WGS 72",DATUM["WGS_1972",SPHEROID["WGS 72",6378135,298.26,AUTHORITY["EPSG","7043"]],AUTHORITY["EPSG","6322"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4322"]]', wk='WGS72'),
|
||||
TestSRS('GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982138982,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4267"]]', wk='NAD27'),
|
||||
TestSRS('GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]]', wk='NAD83'),
|
||||
TestSRS('PROJCS["NZGD49 / Karamea Circuit",GEOGCS["NZGD49",DATUM["New_Zealand_Geodetic_Datum_1949",SPHEROID["International 1924",6378388,297,AUTHORITY["EPSG","7022"]],TOWGS84[59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993],AUTHORITY["EPSG","6272"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4272"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",-41.28991152777778],PARAMETER["central_meridian",172.1090281944444],PARAMETER["scale_factor",1],PARAMETER["false_easting",300000],PARAMETER["false_northing",700000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","27216"]]', wk='EPSG:27216'),
|
||||
)
|
||||
|
||||
bad_srlist = ('Foobar', 'OOJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',)
|
||||
|
||||
class SpatialRefTest(unittest.TestCase):
|
||||
|
||||
def test01_wkt(self):
|
||||
"Testing initialization on valid OGC WKT."
|
||||
for s in srlist:
|
||||
srs = SpatialReference(s.wkt)
|
||||
|
||||
def test02_bad_wkt(self):
|
||||
"Testing initialization on invalid WKT."
|
||||
for bad in bad_srlist:
|
||||
try:
|
||||
srs = SpatialReference(bad)
|
||||
srs.validate()
|
||||
except (SRSException, OGRException):
|
||||
pass
|
||||
else:
|
||||
self.fail('Should not have initialized on bad WKT "%s"!')
|
||||
|
||||
def test03_get_wkt(self):
|
||||
"Testing getting the WKT."
|
||||
for s in srlist:
|
||||
srs = SpatialReference(s.wkt)
|
||||
self.assertEqual(s.wkt, srs.wkt)
|
||||
|
||||
def test04_proj(self):
|
||||
"Test PROJ.4 import and export."
|
||||
|
||||
for s in srlist:
|
||||
if s.proj:
|
||||
srs1 = SpatialReference(s.wkt)
|
||||
srs2 = SpatialReference(s.proj, 'proj')
|
||||
self.assertEqual(srs1.proj, srs2.proj)
|
||||
|
||||
def test05_epsg(self):
|
||||
"Test EPSG import."
|
||||
for s in srlist:
|
||||
if s.epsg:
|
||||
srs1 = SpatialReference(s.wkt)
|
||||
srs2 = SpatialReference(s.epsg, 'epsg')
|
||||
self.assertEqual(srs1.wkt, srs2.wkt)
|
||||
|
||||
def test07_boolean_props(self):
|
||||
"Testing the boolean properties."
|
||||
for s in srlist:
|
||||
srs = SpatialReference(s.wkt)
|
||||
self.assertEqual(s.projected, srs.projected)
|
||||
self.assertEqual(s.geographic, srs.geographic)
|
||||
|
||||
def test08_angular_linear(self):
|
||||
"Testing the linear and angular units routines."
|
||||
for s in srlist:
|
||||
srs = SpatialReference(s.wkt)
|
||||
self.assertEqual(s.ang_name, srs.angular_name)
|
||||
self.assertEqual(s.lin_name, srs.linear_name)
|
||||
self.assertAlmostEqual(s.ang_units, srs.angular_units, 9)
|
||||
self.assertAlmostEqual(s.lin_units, srs.linear_units, 9)
|
||||
|
||||
def test09_authority(self):
|
||||
"Testing the authority name & code routines."
|
||||
for s in srlist:
|
||||
if hasattr(s, 'auth'):
|
||||
srs = SpatialReference(s.wkt)
|
||||
for target, tup in s.auth.items():
|
||||
self.assertEqual(tup[0], srs.auth_name(target))
|
||||
self.assertEqual(tup[1], srs.auth_code(target))
|
||||
|
||||
def test10_attributes(self):
|
||||
"Testing the attribute retrieval routines."
|
||||
for s in srlist:
|
||||
srs = SpatialReference(s.wkt)
|
||||
for tup in s.attr:
|
||||
att = tup[0] # Attribute to test
|
||||
exp = tup[1] # Expected result
|
||||
self.assertEqual(exp, srs[att])
|
||||
|
||||
def test11_wellknown(self):
|
||||
"Testing Well Known Names of Spatial References."
|
||||
for s in well_known:
|
||||
srs = SpatialReference(s.wk)
|
||||
self.assertEqual(s.wkt, srs.wkt)
|
||||
|
||||
def test12_coordtransform(self):
|
||||
"Testing initialization of a CoordTransform."
|
||||
target = SpatialReference('WGS84')
|
||||
for s in srlist:
|
||||
if s.proj:
|
||||
ct = CoordTransform(SpatialReference(s.wkt), target)
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(SpatialRefTest))
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
unittest.TextTestRunner(verbosity=verbosity).run(suite())
|
BIN
django/contrib/gis/tests/test_point/test_point.dbf
Normal file
BIN
django/contrib/gis/tests/test_point/test_point.dbf
Normal file
Binary file not shown.
1
django/contrib/gis/tests/test_point/test_point.prj
Normal file
1
django/contrib/gis/tests/test_point/test_point.prj
Normal file
@ -0,0 +1 @@
|
||||
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
|
BIN
django/contrib/gis/tests/test_point/test_point.shp
Normal file
BIN
django/contrib/gis/tests/test_point/test_point.shp
Normal file
Binary file not shown.
BIN
django/contrib/gis/tests/test_point/test_point.shx
Normal file
BIN
django/contrib/gis/tests/test_point/test_point.shx
Normal file
Binary file not shown.
BIN
django/contrib/gis/tests/test_poly/test_poly.dbf
Normal file
BIN
django/contrib/gis/tests/test_poly/test_poly.dbf
Normal file
Binary file not shown.
1
django/contrib/gis/tests/test_poly/test_poly.prj
Normal file
1
django/contrib/gis/tests/test_poly/test_poly.prj
Normal file
@ -0,0 +1 @@
|
||||
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
|
BIN
django/contrib/gis/tests/test_poly/test_poly.shp
Normal file
BIN
django/contrib/gis/tests/test_poly/test_poly.shp
Normal file
Binary file not shown.
BIN
django/contrib/gis/tests/test_poly/test_poly.shx
Normal file
BIN
django/contrib/gis/tests/test_poly/test_poly.shx
Normal file
Binary file not shown.
74
django/contrib/gis/tests/test_spatialrefsys.py
Normal file
74
django/contrib/gis/tests/test_spatialrefsys.py
Normal file
@ -0,0 +1,74 @@
|
||||
import unittest
|
||||
from django.contrib.gis.models import SpatialRefSys
|
||||
|
||||
test_srs = ({'srid' : 4326,
|
||||
'auth_name' : 'EPSG',
|
||||
'auth_srid' : 4326,
|
||||
'srtext' : 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]',
|
||||
'proj4' : '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ',
|
||||
'spheroid' : 'WGS 84', 'name' : 'WGS 84',
|
||||
'geographic' : True, 'projected' : False,
|
||||
'ellipsoid' : (6378137.0, 6356752.3, 298.257223563), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
|
||||
'eprec' : (1, 1, 9),
|
||||
},
|
||||
{'srid' : 32140,
|
||||
'auth_name' : 'EPSG',
|
||||
'auth_srid' : 32140,
|
||||
'srtext' : 'PROJCS["NAD83 / Texas South Central",GEOGCS["NAD83",DATUM["North_American_Datum_1983",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]],PROJECTION["Lambert_Conformal_Conic_2SP"],PARAMETER["standard_parallel_1",30.28333333333333],PARAMETER["standard_parallel_2",28.38333333333333],PARAMETER["latitude_of_origin",27.83333333333333],PARAMETER["central_meridian",-99],PARAMETER["false_easting",600000],PARAMETER["false_northing",4000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","32140"]]',
|
||||
'proj4' : '+proj=lcc +lat_1=30.28333333333333 +lat_2=28.38333333333333 +lat_0=27.83333333333333 +lon_0=-99 +x_0=600000 +y_0=4000000 +ellps=GRS80 +datum=NAD83 +units=m +no_defs ',
|
||||
'spheroid' : 'GRS 1980', 'name' : 'NAD83 / Texas South Central',
|
||||
'geographic' : False, 'projected' : True,
|
||||
'ellipsoid' : (6378137.0, 6356752.31414, 298.257222101), # From proj's "cs2cs -le" and Wikipedia (semi-minor only)
|
||||
'eprec' : (1, 5, 10),
|
||||
},
|
||||
)
|
||||
|
||||
class SpatialRefSysTest(unittest.TestCase):
|
||||
|
||||
def test01_retrieve(self):
|
||||
"Testing retrieval of SpatialRefSys model objects."
|
||||
for sd in test_srs:
|
||||
srs = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||
self.assertEqual(srs.srid, sd['srid'])
|
||||
self.assertEqual(srs.auth_name, sd['auth_name'])
|
||||
self.assertEqual(srs.auth_srid, sd['auth_srid'])
|
||||
self.assertEqual(srs.srtext, sd['srtext'])
|
||||
self.assertEqual(srs.proj4, sd['proj4'])
|
||||
|
||||
def test02_osr(self):
|
||||
"Testing getting OSR objects from SpatialRefSys model objects."
|
||||
for sd in test_srs:
|
||||
sr = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||
self.assertEqual(sd['spheroid'], sr.spheroid)
|
||||
self.assertEqual(sd['geographic'], sr.geographic)
|
||||
self.assertEqual(sd['projected'], sr.projected)
|
||||
self.assertEqual(sd['name'], sr.name)
|
||||
|
||||
# Testing the SpatialReference object directly.
|
||||
srs = sr.srs
|
||||
self.assertEqual(sd['proj4'], srs.proj)
|
||||
self.assertEqual(sd['srtext'], srs.wkt)
|
||||
|
||||
def test03_ellipsoid(self):
|
||||
"Testing the ellipsoid property."
|
||||
for sd in test_srs:
|
||||
# Getting the ellipsoid and precision parameters.
|
||||
ellps1 = sd['ellipsoid']
|
||||
prec = sd['eprec']
|
||||
|
||||
# Getting our spatial reference and its ellipsoid
|
||||
srs = SpatialRefSys.objects.get(srid=sd['srid'])
|
||||
ellps2 = srs.ellipsoid
|
||||
|
||||
for i in range(3):
|
||||
param1 = ellps1[i]
|
||||
param2 = ellps2[i]
|
||||
self.assertAlmostEqual(ellps1[i], ellps2[i], prec[i])
|
||||
|
||||
def suite():
|
||||
s = unittest.TestSuite()
|
||||
s.addTest(unittest.makeSuite(SpatialRefSysTest))
|
||||
return s
|
||||
|
||||
def run(verbosity=2):
|
||||
unittest.TextTestRunner(verbosity=verbosity).run(suite())
|
Loading…
x
Reference in New Issue
Block a user