änderung1
This commit is contained in:
1 parent
cc1d096bc3
commit
cbdda613b7
2098 files changed
+274524
No files matched your search
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2007-2009, 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.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
This module houses ctypes interfaces for GDAL objects. The following GDAL
|
||||
objects are supported:
|
||||
|
||||
CoordTransform: Used for coordinate transformations from one spatial
|
||||
reference system to another.
|
||||
|
||||
Driver: Wraps an OGR data source driver.
|
||||
|
||||
DataSource: Wrapper for the OGR data source object, supports
|
||||
OGR-supported data sources.
|
||||
|
||||
Envelope: A ctypes structure for bounding boxes (GDAL library
|
||||
not required).
|
||||
|
||||
OGRGeometry: Object for accessing OGR Geometry functionality.
|
||||
|
||||
OGRGeomType: A class for representing the different OGR Geometry
|
||||
types (GDAL library not required).
|
||||
|
||||
SpatialReference: Represents OSR Spatial Reference objects.
|
||||
|
||||
The GDAL library will be imported from the system path using the default
|
||||
library name for the current OS. The default library path may be overridden
|
||||
by setting `GDAL_LIBRARY_PATH` in your settings with the path to the GDAL C
|
||||
library on your system.
|
||||
"""
|
||||
from django.contrib.gis.gdal.datasource import DataSource
|
||||
from django.contrib.gis.gdal.driver import Driver
|
||||
from django.contrib.gis.gdal.envelope import Envelope
|
||||
from django.contrib.gis.gdal.error import (
|
||||
GDALException, SRSException, check_err,
|
||||
)
|
||||
from django.contrib.gis.gdal.geometries import OGRGeometry
|
||||
from django.contrib.gis.gdal.geomtype import OGRGeomType
|
||||
from django.contrib.gis.gdal.libgdal import (
|
||||
GDAL_VERSION, gdal_full_version, gdal_version,
|
||||
)
|
||||
from django.contrib.gis.gdal.raster.source import GDALRaster
|
||||
from django.contrib.gis.gdal.srs import CoordTransform, SpatialReference
|
||||
|
||||
__all__ = (
|
||||
'Driver', 'DataSource', 'CoordTransform', 'Envelope', 'GDALException',
|
||||
'GDALRaster', 'GDAL_VERSION', 'OGRGeometry', 'OGRGeomType',
|
||||
'SpatialReference', 'SRSException', 'check_err', 'gdal_version',
|
||||
'gdal_full_version',
|
||||
)
|
||||
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
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).
|
||||
|
||||
When instantiating a DataSource object, use the filename of a
|
||||
GDAL-supported data source. For example, a SHP file or a
|
||||
TIGER/Line file from the government.
|
||||
|
||||
The ds_driver keyword is used internally when a ctypes pointer
|
||||
is passed in directly.
|
||||
|
||||
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']
|
||||
|
||||
# We can also increment through all of the fields
|
||||
# attached to this feature.
|
||||
for field in feature:
|
||||
# Get the name of the field (e.g. 'description')
|
||||
nm = field.name
|
||||
|
||||
# Get the type (integer) of the field, e.g. 0 => OFTInteger
|
||||
t = field.type
|
||||
|
||||
# Returns the value the field; OFTIntegers return ints,
|
||||
# OFTReal returns floats, all else returns string.
|
||||
val = field.value
|
||||
"""
|
||||
from ctypes import byref
|
||||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.driver import Driver
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.layer import Layer
|
||||
from django.contrib.gis.gdal.prototypes import ds as capi
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
|
||||
# For more information, see the OGR C API source code:
|
||||
# https://www.gdal.org/ogr__api_8h.html
|
||||
#
|
||||
# The OGR_DS_* routines are relevant here.
|
||||
class DataSource(GDALBase):
|
||||
"Wraps an OGR Data Source object."
|
||||
destructor = capi.destroy_ds
|
||||
|
||||
def __init__(self, ds_input, ds_driver=False, write=False, encoding='utf-8'):
|
||||
# The write flag.
|
||||
if write:
|
||||
self._write = 1
|
||||
else:
|
||||
self._write = 0
|
||||
# See also https://trac.osgeo.org/gdal/wiki/rfc23_ogr_unicode
|
||||
self.encoding = encoding
|
||||
|
||||
Driver.ensure_registered()
|
||||
|
||||
if isinstance(ds_input, str):
|
||||
# The data source driver is a void pointer.
|
||||
ds_driver = Driver.ptr_type()
|
||||
try:
|
||||
# OGROpen will auto-detect the data source type.
|
||||
ds = capi.open_ds(force_bytes(ds_input), self._write, byref(ds_driver))
|
||||
except GDALException:
|
||||
# Making the error message more clear rather than something
|
||||
# like "Invalid pointer returned from OGROpen".
|
||||
raise GDALException('Could not open the datasource at "%s"' % ds_input)
|
||||
elif isinstance(ds_input, self.ptr_type) and isinstance(ds_driver, Driver.ptr_type):
|
||||
ds = ds_input
|
||||
else:
|
||||
raise GDALException('Invalid data source input type: %s' % type(ds_input))
|
||||
|
||||
if ds:
|
||||
self.ptr = ds
|
||||
self.driver = Driver(ds_driver)
|
||||
else:
|
||||
# Raise an exception if the returned pointer is NULL
|
||||
raise GDALException('Invalid data source file "%s"' % ds_input)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Allows use of the index [] operator to get a layer at the index."
|
||||
if isinstance(index, str):
|
||||
try:
|
||||
layer = capi.get_layer_by_name(self.ptr, force_bytes(index))
|
||||
except GDALException:
|
||||
raise IndexError('Invalid OGR layer name given: %s.' % index)
|
||||
elif isinstance(index, int):
|
||||
if 0 <= index < self.layer_count:
|
||||
layer = capi.get_layer(self._ptr, index)
|
||||
else:
|
||||
raise IndexError('Index out of range when accessing layers in a datasource: %s.' % index)
|
||||
else:
|
||||
raise TypeError('Invalid index type: %s' % type(index))
|
||||
return Layer(layer, self)
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of layers within the data source."
|
||||
return self.layer_count
|
||||
|
||||
def __str__(self):
|
||||
"Return OGR GetName and Driver for the Data Source."
|
||||
return '%s (%s)' % (self.name, self.driver)
|
||||
|
||||
@property
|
||||
def layer_count(self):
|
||||
"Return the number of layers in the data source."
|
||||
return capi.get_layer_count(self._ptr)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"Return the name of the data source."
|
||||
name = capi.get_ds_name(self._ptr)
|
||||
return force_text(name, self.encoding, strings_only=True)
|
||||
@@ -0,0 +1,97 @@
|
||||
from ctypes import c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.prototypes import ds as vcapi, raster as rcapi
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
|
||||
class Driver(GDALBase):
|
||||
"""
|
||||
Wrap a GDAL/OGR Data Source Driver.
|
||||
For more information, see the C API source code:
|
||||
https://www.gdal.org/gdal_8h.html - https://www.gdal.org/ogr__api_8h.html
|
||||
"""
|
||||
|
||||
# Case-insensitive aliases for some GDAL/OGR Drivers.
|
||||
# For a complete list of original driver names see
|
||||
# https://www.gdal.org/ogr_formats.html (vector)
|
||||
# https://www.gdal.org/formats_list.html (raster)
|
||||
_alias = {
|
||||
# vector
|
||||
'esri': 'ESRI Shapefile',
|
||||
'shp': 'ESRI Shapefile',
|
||||
'shape': 'ESRI Shapefile',
|
||||
'tiger': 'TIGER',
|
||||
'tiger/line': 'TIGER',
|
||||
# raster
|
||||
'tiff': 'GTiff',
|
||||
'tif': 'GTiff',
|
||||
'jpeg': 'JPEG',
|
||||
'jpg': 'JPEG',
|
||||
}
|
||||
|
||||
def __init__(self, dr_input):
|
||||
"""
|
||||
Initialize an GDAL/OGR driver on either a string or integer input.
|
||||
"""
|
||||
if isinstance(dr_input, str):
|
||||
# If a string name of the driver was passed in
|
||||
self.ensure_registered()
|
||||
|
||||
# Checking the alias dictionary (case-insensitive) to see if an
|
||||
# alias exists for the given driver.
|
||||
if dr_input.lower() in self._alias:
|
||||
name = self._alias[dr_input.lower()]
|
||||
else:
|
||||
name = dr_input
|
||||
|
||||
# Attempting to get the GDAL/OGR driver by the string name.
|
||||
for iface in (vcapi, rcapi):
|
||||
driver = c_void_p(iface.get_driver_by_name(force_bytes(name)))
|
||||
if driver:
|
||||
break
|
||||
elif isinstance(dr_input, int):
|
||||
self.ensure_registered()
|
||||
for iface in (vcapi, rcapi):
|
||||
driver = iface.get_driver(dr_input)
|
||||
if driver:
|
||||
break
|
||||
elif isinstance(dr_input, c_void_p):
|
||||
driver = dr_input
|
||||
else:
|
||||
raise GDALException('Unrecognized input type for GDAL/OGR Driver: %s' % type(dr_input))
|
||||
|
||||
# Making sure we get a valid pointer to the OGR Driver
|
||||
if not driver:
|
||||
raise GDALException('Could not initialize GDAL/OGR Driver on input: %s' % dr_input)
|
||||
self.ptr = driver
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@classmethod
|
||||
def ensure_registered(cls):
|
||||
"""
|
||||
Attempt to register all the data source drivers.
|
||||
"""
|
||||
# Only register all if the driver counts are 0 (or else all drivers
|
||||
# will be registered over and over again)
|
||||
if not vcapi.get_driver_count():
|
||||
vcapi.register_all()
|
||||
if not rcapi.get_driver_count():
|
||||
rcapi.register_all()
|
||||
|
||||
@classmethod
|
||||
def driver_count(cls):
|
||||
"""
|
||||
Return the number of GDAL/OGR data source drivers registered.
|
||||
"""
|
||||
return vcapi.get_driver_count() + rcapi.get_driver_count()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Return description/name string for this driver.
|
||||
"""
|
||||
return force_text(rcapi.get_driver_description(self.ptr))
|
||||
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
The GDAL/OGR library uses an Envelope structure to hold the bounding
|
||||
box information for a geometry. The envelope (bounding box) contains
|
||||
two pairs of coordinates, one for the lower left coordinate and one
|
||||
for the upper right coordinate:
|
||||
|
||||
+----------o Upper right; (max_x, max_y)
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
Lower left (min_x, min_y) o----------+
|
||||
"""
|
||||
from ctypes import Structure, c_double
|
||||
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
|
||||
|
||||
# The OGR definition of an Envelope is a C structure containing four doubles.
|
||||
# See the 'ogr_core.h' source file for more information:
|
||||
# https://www.gdal.org/ogr__core_8h_source.html
|
||||
class OGREnvelope(Structure):
|
||||
"Represent the OGREnvelope C Structure."
|
||||
_fields_ = [("MinX", c_double),
|
||||
("MaxX", c_double),
|
||||
("MinY", c_double),
|
||||
("MaxY", c_double),
|
||||
]
|
||||
|
||||
|
||||
class Envelope:
|
||||
"""
|
||||
The Envelope object is a C structure that contains the minimum and
|
||||
maximum X, Y coordinates for a rectangle bounding box. The naming
|
||||
of the variables is compatible with the OGR Envelope structure.
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""
|
||||
The initialization function may take an OGREnvelope structure, 4-element
|
||||
tuple or list, or 4 individual arguments.
|
||||
"""
|
||||
|
||||
if len(args) == 1:
|
||||
if isinstance(args[0], OGREnvelope):
|
||||
# OGREnvelope (a ctypes Structure) was passed in.
|
||||
self._envelope = args[0]
|
||||
elif isinstance(args[0], (tuple, list)):
|
||||
# A tuple was passed in.
|
||||
if len(args[0]) != 4:
|
||||
raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
|
||||
else:
|
||||
self._from_sequence(args[0])
|
||||
else:
|
||||
raise TypeError('Incorrect type of argument: %s' % type(args[0]))
|
||||
elif len(args) == 4:
|
||||
# Individual parameters passed in.
|
||||
# Thanks to ww for the help
|
||||
self._from_sequence([float(a) for a in args])
|
||||
else:
|
||||
raise GDALException('Incorrect number (%d) of arguments.' % len(args))
|
||||
|
||||
# Checking the x,y coordinates
|
||||
if self.min_x > self.max_x:
|
||||
raise GDALException('Envelope minimum X > maximum X.')
|
||||
if self.min_y > self.max_y:
|
||||
raise GDALException('Envelope minimum Y > maximum Y.')
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Return True if the envelopes are equivalent; can compare against
|
||||
other Envelopes and 4-tuples.
|
||||
"""
|
||||
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, 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:
|
||||
raise GDALException('Equivalence testing only works with other Envelopes.')
|
||||
|
||||
def __str__(self):
|
||||
"Return a string representation of the tuple."
|
||||
return str(self.tuple)
|
||||
|
||||
def _from_sequence(self, seq):
|
||||
"Initialize the C OGR Envelope structure from the given sequence."
|
||||
self._envelope = OGREnvelope()
|
||||
self._envelope.MinX = seq[0]
|
||||
self._envelope.MinY = seq[1]
|
||||
self._envelope.MaxX = seq[2]
|
||||
self._envelope.MaxY = seq[3]
|
||||
|
||||
def expand_to_include(self, *args):
|
||||
"""
|
||||
Modify 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 GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
|
||||
else:
|
||||
raise TypeError('Incorrect type of argument: %s' % 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:
|
||||
# Individual parameters passed in.
|
||||
return self.expand_to_include(args)
|
||||
else:
|
||||
raise GDALException('Incorrect number (%d) of arguments.' % len(args[0]))
|
||||
|
||||
@property
|
||||
def min_x(self):
|
||||
"Return the value of the minimum X coordinate."
|
||||
return self._envelope.MinX
|
||||
|
||||
@property
|
||||
def min_y(self):
|
||||
"Return the value of the minimum Y coordinate."
|
||||
return self._envelope.MinY
|
||||
|
||||
@property
|
||||
def max_x(self):
|
||||
"Return the value of the maximum X coordinate."
|
||||
return self._envelope.MaxX
|
||||
|
||||
@property
|
||||
def max_y(self):
|
||||
"Return the value of the maximum Y coordinate."
|
||||
return self._envelope.MaxY
|
||||
|
||||
@property
|
||||
def ur(self):
|
||||
"Return the upper-right coordinate."
|
||||
return (self.max_x, self.max_y)
|
||||
|
||||
@property
|
||||
def ll(self):
|
||||
"Return the lower-left coordinate."
|
||||
return (self.min_x, self.min_y)
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple representing the envelope."
|
||||
return (self.min_x, self.min_y, self.max_x, self.max_y)
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return WKT representing a Polygon for this envelope."
|
||||
# TODO: Fix significant figures.
|
||||
return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
|
||||
(self.min_x, self.min_y, self.min_x, self.max_y,
|
||||
self.max_x, self.max_y, self.max_x, self.min_y,
|
||||
self.min_x, self.min_y)
|
||||
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
This module houses the GDAL & SRS Exception objects, and the
|
||||
check_err() routine which checks the status code returned by
|
||||
GDAL/OGR methods.
|
||||
"""
|
||||
|
||||
|
||||
# #### GDAL & SRS Exceptions ####
|
||||
class GDALException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SRSException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# #### GDAL/OGR error checking codes and routine ####
|
||||
|
||||
# OGR Error Codes
|
||||
OGRERR_DICT = {
|
||||
1: (GDALException, 'Not enough data.'),
|
||||
2: (GDALException, 'Not enough memory.'),
|
||||
3: (GDALException, 'Unsupported geometry type.'),
|
||||
4: (GDALException, 'Unsupported operation.'),
|
||||
5: (GDALException, 'Corrupt data.'),
|
||||
6: (GDALException, 'OGR failure.'),
|
||||
7: (SRSException, 'Unsupported SRS.'),
|
||||
8: (GDALException, 'Invalid handle.'),
|
||||
}
|
||||
|
||||
# CPL Error Codes
|
||||
# https://www.gdal.org/cpl__error_8h.html
|
||||
CPLERR_DICT = {
|
||||
1: (GDALException, 'AppDefined'),
|
||||
2: (GDALException, 'OutOfMemory'),
|
||||
3: (GDALException, 'FileIO'),
|
||||
4: (GDALException, 'OpenFailed'),
|
||||
5: (GDALException, 'IllegalArg'),
|
||||
6: (GDALException, 'NotSupported'),
|
||||
7: (GDALException, 'AssertionFailed'),
|
||||
8: (GDALException, 'NoWriteAccess'),
|
||||
9: (GDALException, 'UserInterrupt'),
|
||||
10: (GDALException, 'ObjectNull'),
|
||||
}
|
||||
|
||||
ERR_NONE = 0
|
||||
|
||||
|
||||
def check_err(code, cpl=False):
|
||||
"""
|
||||
Check the given CPL/OGRERR and raise an exception where appropriate.
|
||||
"""
|
||||
err_dict = CPLERR_DICT if cpl else OGRERR_DICT
|
||||
|
||||
if code == ERR_NONE:
|
||||
return
|
||||
elif code in err_dict:
|
||||
e, msg = err_dict[code]
|
||||
raise e(msg)
|
||||
else:
|
||||
raise GDALException('Unknown error code: "%s"' % code)
|
||||
@@ -0,0 +1,95 @@
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
|
||||
|
||||
class OGRGeomType:
|
||||
"Encapsulate OGR Geometry Types."
|
||||
|
||||
wkb25bit = -2147483648
|
||||
|
||||
# Dictionary of acceptable OGRwkbGeometryType s and their string names.
|
||||
_types = {
|
||||
0: 'Unknown',
|
||||
1: 'Point',
|
||||
2: 'LineString',
|
||||
3: 'Polygon',
|
||||
4: 'MultiPoint',
|
||||
5: 'MultiLineString',
|
||||
6: 'MultiPolygon',
|
||||
7: 'GeometryCollection',
|
||||
100: 'None',
|
||||
101: 'LinearRing',
|
||||
102: 'PointZ',
|
||||
1 + wkb25bit: 'Point25D',
|
||||
2 + wkb25bit: 'LineString25D',
|
||||
3 + wkb25bit: 'Polygon25D',
|
||||
4 + wkb25bit: 'MultiPoint25D',
|
||||
5 + wkb25bit: 'MultiLineString25D',
|
||||
6 + wkb25bit: 'MultiPolygon25D',
|
||||
7 + wkb25bit: 'GeometryCollection25D',
|
||||
}
|
||||
# Reverse type dictionary, keyed by lowercase of the name.
|
||||
_str_types = {v.lower(): k for k, v in _types.items()}
|
||||
|
||||
def __init__(self, type_input):
|
||||
"Figure out the correct OGR Type based upon the input."
|
||||
if isinstance(type_input, OGRGeomType):
|
||||
num = type_input.num
|
||||
elif isinstance(type_input, str):
|
||||
type_input = type_input.lower()
|
||||
if type_input == 'geometry':
|
||||
type_input = 'unknown'
|
||||
num = self._str_types.get(type_input)
|
||||
if num is None:
|
||||
raise GDALException('Invalid OGR String Type "%s"' % type_input)
|
||||
elif isinstance(type_input, int):
|
||||
if type_input not in self._types:
|
||||
raise GDALException('Invalid OGR Integer Type: %d' % type_input)
|
||||
num = type_input
|
||||
else:
|
||||
raise TypeError('Invalid OGR input type given.')
|
||||
|
||||
# Setting the OGR geometry type number.
|
||||
self.num = num
|
||||
|
||||
def __str__(self):
|
||||
"Return the value of the name property."
|
||||
return self.name
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Do 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.num == other.num
|
||||
elif isinstance(other, str):
|
||||
return self.name.lower() == other.lower()
|
||||
elif isinstance(other, int):
|
||||
return self.num == other
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"Return a short-hand string form of the OGR Geometry type."
|
||||
return self._types[self.num]
|
||||
|
||||
@property
|
||||
def django(self):
|
||||
"Return the Django GeometryField for this OGR Type."
|
||||
s = self.name.replace('25D', '')
|
||||
if s in ('LinearRing', 'None'):
|
||||
return None
|
||||
elif s == 'Unknown':
|
||||
s = 'Geometry'
|
||||
elif s == 'PointZ':
|
||||
s = 'Point'
|
||||
return s + 'Field'
|
||||
|
||||
def to_multi(self):
|
||||
"""
|
||||
Transform Point, LineString, Polygon, and their 25D equivalents
|
||||
to their Multi... counterpart.
|
||||
"""
|
||||
if self.name.startswith(('Point', 'LineString', 'Polygon')):
|
||||
self.num += 3
|
||||
@@ -0,0 +1,124 @@
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from ctypes import CDLL, CFUNCTYPE, c_char_p, c_int
|
||||
from ctypes.util import find_library
|
||||
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
logger = logging.getLogger('django.contrib.gis')
|
||||
|
||||
# Custom library path set?
|
||||
try:
|
||||
from django.conf import settings
|
||||
lib_path = settings.GDAL_LIBRARY_PATH
|
||||
except (AttributeError, EnvironmentError,
|
||||
ImportError, ImproperlyConfigured):
|
||||
lib_path = None
|
||||
|
||||
if lib_path:
|
||||
lib_names = None
|
||||
elif os.name == 'nt':
|
||||
# Windows NT shared libraries
|
||||
lib_names = ['gdal203', 'gdal202', 'gdal201', 'gdal20', 'gdal111']
|
||||
elif os.name == 'posix':
|
||||
# *NIX library names.
|
||||
lib_names = ['gdal', 'GDAL', 'gdal2.3.0', 'gdal2.2.0', 'gdal2.1.0', 'gdal2.0.0', 'gdal1.11.0']
|
||||
else:
|
||||
raise ImproperlyConfigured('GDAL is unsupported on OS "%s".' % os.name)
|
||||
|
||||
# Using the ctypes `find_library` utility to find the
|
||||
# path to the GDAL library from the list of library names.
|
||||
if lib_names:
|
||||
for lib_name in lib_names:
|
||||
lib_path = find_library(lib_name)
|
||||
if lib_path is not None:
|
||||
break
|
||||
|
||||
if lib_path is None:
|
||||
raise ImproperlyConfigured(
|
||||
'Could not find the GDAL library (tried "%s"). Is GDAL installed? '
|
||||
'If it is, try setting GDAL_LIBRARY_PATH in your settings.'
|
||||
% '", "'.join(lib_names)
|
||||
)
|
||||
|
||||
# This loads the GDAL/OGR C library
|
||||
lgdal = CDLL(lib_path)
|
||||
|
||||
# On Windows, the GDAL binaries have some OSR routines exported with
|
||||
# STDCALL, while others are not. Thus, the library will also need to
|
||||
# be loaded up as WinDLL for said OSR functions that require the
|
||||
# different calling convention.
|
||||
if os.name == 'nt':
|
||||
from ctypes import WinDLL
|
||||
lwingdal = WinDLL(lib_path)
|
||||
|
||||
|
||||
def std_call(func):
|
||||
"""
|
||||
Return the correct STDCALL function for certain OSR routines on Win32
|
||||
platforms.
|
||||
"""
|
||||
if os.name == 'nt':
|
||||
return lwingdal[func]
|
||||
else:
|
||||
return lgdal[func]
|
||||
|
||||
|
||||
# #### Version-information functions. ####
|
||||
|
||||
# Return GDAL library version information with the given key.
|
||||
_version_info = std_call('GDALVersionInfo')
|
||||
_version_info.argtypes = [c_char_p]
|
||||
_version_info.restype = c_char_p
|
||||
|
||||
|
||||
def gdal_version():
|
||||
"Return only the GDAL version number information."
|
||||
return _version_info(b'RELEASE_NAME')
|
||||
|
||||
|
||||
def gdal_full_version():
|
||||
"Return the full GDAL version information."
|
||||
return _version_info('')
|
||||
|
||||
|
||||
version_regex = re.compile(r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<subminor>\d+))?')
|
||||
|
||||
|
||||
def gdal_version_info():
|
||||
ver = gdal_version().decode()
|
||||
m = version_regex.match(ver)
|
||||
if not m:
|
||||
raise GDALException('Could not parse GDAL version string "%s"' % ver)
|
||||
return {key: m.group(key) for key in ('major', 'minor', 'subminor')}
|
||||
|
||||
|
||||
_verinfo = gdal_version_info()
|
||||
GDAL_MAJOR_VERSION = int(_verinfo['major'])
|
||||
GDAL_MINOR_VERSION = int(_verinfo['minor'])
|
||||
GDAL_SUBMINOR_VERSION = _verinfo['subminor'] and int(_verinfo['subminor'])
|
||||
GDAL_VERSION = (GDAL_MAJOR_VERSION, GDAL_MINOR_VERSION, GDAL_SUBMINOR_VERSION)
|
||||
del _verinfo
|
||||
|
||||
# Set library error handling so as errors are logged
|
||||
CPLErrorHandler = CFUNCTYPE(None, c_int, c_int, c_char_p)
|
||||
|
||||
|
||||
def err_handler(error_class, error_number, message):
|
||||
logger.error('GDAL_ERROR %d: %s', error_number, message)
|
||||
|
||||
|
||||
err_handler = CPLErrorHandler(err_handler)
|
||||
|
||||
|
||||
def function(name, args, restype):
|
||||
func = std_call(name)
|
||||
func.argtypes = args
|
||||
func.restype = restype
|
||||
return func
|
||||
|
||||
|
||||
set_error_handler = function('CPLSetErrorHandler', [CPLErrorHandler], CPLErrorHandler)
|
||||
set_error_handler(err_handler)
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
This module houses the ctypes function prototypes for OGR DataSource
|
||||
related data structures. OGR_Dr_*, OGR_DS_*, OGR_L_*, OGR_F_*,
|
||||
OGR_Fld_* routines are relevant here.
|
||||
"""
|
||||
from ctypes import POINTER, c_char_p, c_double, c_int, c_long, c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.envelope import OGREnvelope
|
||||
from django.contrib.gis.gdal.libgdal import GDAL_VERSION, lgdal
|
||||
from django.contrib.gis.gdal.prototypes.generation import (
|
||||
const_string_output, double_output, geom_output, int64_output, int_output,
|
||||
srs_output, void_output, voidptr_output,
|
||||
)
|
||||
|
||||
c_int_p = POINTER(c_int) # shortcut type
|
||||
|
||||
# Driver Routines
|
||||
register_all = void_output(lgdal.OGRRegisterAll, [], errcheck=False)
|
||||
cleanup_all = void_output(lgdal.OGRCleanupAll, [], errcheck=False)
|
||||
get_driver = voidptr_output(lgdal.OGRGetDriver, [c_int])
|
||||
get_driver_by_name = voidptr_output(lgdal.OGRGetDriverByName, [c_char_p], errcheck=False)
|
||||
get_driver_count = int_output(lgdal.OGRGetDriverCount, [])
|
||||
get_driver_name = const_string_output(lgdal.OGR_Dr_GetName, [c_void_p], decoding='ascii')
|
||||
|
||||
# DataSource
|
||||
open_ds = voidptr_output(lgdal.OGROpen, [c_char_p, c_int, POINTER(c_void_p)])
|
||||
destroy_ds = void_output(lgdal.OGR_DS_Destroy, [c_void_p], errcheck=False)
|
||||
release_ds = void_output(lgdal.OGRReleaseDataSource, [c_void_p])
|
||||
get_ds_name = const_string_output(lgdal.OGR_DS_GetName, [c_void_p])
|
||||
get_layer = voidptr_output(lgdal.OGR_DS_GetLayer, [c_void_p, c_int])
|
||||
get_layer_by_name = voidptr_output(lgdal.OGR_DS_GetLayerByName, [c_void_p, c_char_p])
|
||||
get_layer_count = int_output(lgdal.OGR_DS_GetLayerCount, [c_void_p])
|
||||
|
||||
# Layer Routines
|
||||
get_extent = void_output(lgdal.OGR_L_GetExtent, [c_void_p, POINTER(OGREnvelope), c_int])
|
||||
get_feature = voidptr_output(lgdal.OGR_L_GetFeature, [c_void_p, c_long])
|
||||
get_feature_count = int_output(lgdal.OGR_L_GetFeatureCount, [c_void_p, c_int])
|
||||
get_layer_defn = voidptr_output(lgdal.OGR_L_GetLayerDefn, [c_void_p])
|
||||
get_layer_srs = srs_output(lgdal.OGR_L_GetSpatialRef, [c_void_p])
|
||||
get_next_feature = voidptr_output(lgdal.OGR_L_GetNextFeature, [c_void_p])
|
||||
reset_reading = void_output(lgdal.OGR_L_ResetReading, [c_void_p], errcheck=False)
|
||||
test_capability = int_output(lgdal.OGR_L_TestCapability, [c_void_p, c_char_p])
|
||||
get_spatial_filter = geom_output(lgdal.OGR_L_GetSpatialFilter, [c_void_p])
|
||||
set_spatial_filter = void_output(lgdal.OGR_L_SetSpatialFilter, [c_void_p, c_void_p], errcheck=False)
|
||||
set_spatial_filter_rect = void_output(
|
||||
lgdal.OGR_L_SetSpatialFilterRect,
|
||||
[c_void_p, c_double, c_double, c_double, c_double], errcheck=False
|
||||
)
|
||||
|
||||
# Feature Definition Routines
|
||||
get_fd_geom_type = int_output(lgdal.OGR_FD_GetGeomType, [c_void_p])
|
||||
get_fd_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
|
||||
get_feat_name = const_string_output(lgdal.OGR_FD_GetName, [c_void_p])
|
||||
get_field_count = int_output(lgdal.OGR_FD_GetFieldCount, [c_void_p])
|
||||
get_field_defn = voidptr_output(lgdal.OGR_FD_GetFieldDefn, [c_void_p, c_int])
|
||||
|
||||
# Feature Routines
|
||||
clone_feature = voidptr_output(lgdal.OGR_F_Clone, [c_void_p])
|
||||
destroy_feature = void_output(lgdal.OGR_F_Destroy, [c_void_p], errcheck=False)
|
||||
feature_equal = int_output(lgdal.OGR_F_Equal, [c_void_p, c_void_p])
|
||||
get_feat_geom_ref = geom_output(lgdal.OGR_F_GetGeometryRef, [c_void_p])
|
||||
get_feat_field_count = int_output(lgdal.OGR_F_GetFieldCount, [c_void_p])
|
||||
get_feat_field_defn = voidptr_output(lgdal.OGR_F_GetFieldDefnRef, [c_void_p, c_int])
|
||||
get_fid = int_output(lgdal.OGR_F_GetFID, [c_void_p])
|
||||
get_field_as_datetime = int_output(
|
||||
lgdal.OGR_F_GetFieldAsDateTime,
|
||||
[c_void_p, c_int, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p, c_int_p]
|
||||
)
|
||||
get_field_as_double = double_output(lgdal.OGR_F_GetFieldAsDouble, [c_void_p, c_int])
|
||||
get_field_as_integer = int_output(lgdal.OGR_F_GetFieldAsInteger, [c_void_p, c_int])
|
||||
if GDAL_VERSION >= (2, 0):
|
||||
get_field_as_integer64 = int64_output(lgdal.OGR_F_GetFieldAsInteger64, [c_void_p, c_int])
|
||||
get_field_as_string = const_string_output(lgdal.OGR_F_GetFieldAsString, [c_void_p, c_int])
|
||||
get_field_index = int_output(lgdal.OGR_F_GetFieldIndex, [c_void_p, c_char_p])
|
||||
|
||||
# Field Routines
|
||||
get_field_name = const_string_output(lgdal.OGR_Fld_GetNameRef, [c_void_p])
|
||||
get_field_precision = int_output(lgdal.OGR_Fld_GetPrecision, [c_void_p])
|
||||
get_field_type = int_output(lgdal.OGR_Fld_GetType, [c_void_p])
|
||||
get_field_type_name = const_string_output(lgdal.OGR_GetFieldTypeName, [c_int])
|
||||
get_field_width = int_output(lgdal.OGR_Fld_GetWidth, [c_void_p])
|
||||
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
This module contains functions that generate ctypes prototypes for the
|
||||
GDAL routines.
|
||||
"""
|
||||
from ctypes import POINTER, c_char_p, c_double, c_int, c_int64, c_void_p
|
||||
from functools import partial
|
||||
|
||||
from django.contrib.gis.gdal.prototypes.errcheck import (
|
||||
check_arg_errcode, check_const_string, check_errcode, check_geom,
|
||||
check_geom_offset, check_pointer, check_srs, check_str_arg, check_string,
|
||||
)
|
||||
|
||||
|
||||
class gdal_char_p(c_char_p):
|
||||
pass
|
||||
|
||||
|
||||
def double_output(func, argtypes, errcheck=False, strarg=False, cpl=False):
|
||||
"Generate a ctypes function that returns a double value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_double
|
||||
if errcheck:
|
||||
func.errcheck = partial(check_arg_errcode, cpl=cpl)
|
||||
if strarg:
|
||||
func.errcheck = check_str_arg
|
||||
return func
|
||||
|
||||
|
||||
def geom_output(func, argtypes, offset=None):
|
||||
"""
|
||||
Generate a function that returns a Geometry either by reference
|
||||
or directly (if the return_geom keyword is set to True).
|
||||
"""
|
||||
# Setting the argument types
|
||||
func.argtypes = argtypes
|
||||
|
||||
if not offset:
|
||||
# When a geometry pointer is directly returned.
|
||||
func.restype = c_void_p
|
||||
func.errcheck = check_geom
|
||||
else:
|
||||
# Error code returned, geometry is returned by-reference.
|
||||
func.restype = c_int
|
||||
|
||||
def geomerrcheck(result, func, cargs):
|
||||
return check_geom_offset(result, func, cargs, offset)
|
||||
func.errcheck = geomerrcheck
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def int_output(func, argtypes, errcheck=None):
|
||||
"Generate a ctypes function that returns an integer value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_int
|
||||
if errcheck:
|
||||
func.errcheck = errcheck
|
||||
return func
|
||||
|
||||
|
||||
def int64_output(func, argtypes):
|
||||
"Generate a ctypes function that returns a 64-bit integer value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_int64
|
||||
return func
|
||||
|
||||
|
||||
def srs_output(func, argtypes):
|
||||
"""
|
||||
Generate a ctypes prototype for the given function with
|
||||
the given C arguments that returns a pointer to an OGR
|
||||
Spatial Reference System.
|
||||
"""
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_void_p
|
||||
func.errcheck = check_srs
|
||||
return func
|
||||
|
||||
|
||||
def const_string_output(func, argtypes, offset=None, decoding=None, cpl=False):
|
||||
func.argtypes = argtypes
|
||||
if offset:
|
||||
func.restype = c_int
|
||||
else:
|
||||
func.restype = c_char_p
|
||||
|
||||
def _check_const(result, func, cargs):
|
||||
res = check_const_string(result, func, cargs, offset=offset, cpl=cpl)
|
||||
if res and decoding:
|
||||
res = res.decode(decoding)
|
||||
return res
|
||||
func.errcheck = _check_const
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def string_output(func, argtypes, offset=-1, str_result=False, decoding=None):
|
||||
"""
|
||||
Generate a ctypes prototype for the given function with the
|
||||
given argument types that returns a string from a GDAL pointer.
|
||||
The `const` flag indicates whether the allocated pointer should
|
||||
be freed via the GDAL library routine VSIFree -- but only applies
|
||||
only when `str_result` is True.
|
||||
"""
|
||||
func.argtypes = argtypes
|
||||
if str_result:
|
||||
# Use subclass of c_char_p so the error checking routine
|
||||
# can free the memory at the pointer's address.
|
||||
func.restype = gdal_char_p
|
||||
else:
|
||||
# Error code is returned
|
||||
func.restype = c_int
|
||||
|
||||
# Dynamically defining our error-checking function with the
|
||||
# given offset.
|
||||
def _check_str(result, func, cargs):
|
||||
res = check_string(result, func, cargs, offset=offset, str_result=str_result)
|
||||
if res and decoding:
|
||||
res = res.decode(decoding)
|
||||
return res
|
||||
func.errcheck = _check_str
|
||||
return func
|
||||
|
||||
|
||||
def void_output(func, argtypes, errcheck=True, cpl=False):
|
||||
"""
|
||||
For functions that don't only return an error code that needs to
|
||||
be examined.
|
||||
"""
|
||||
if argtypes:
|
||||
func.argtypes = argtypes
|
||||
if errcheck:
|
||||
# `errcheck` keyword may be set to False for routines that
|
||||
# return void, rather than a status code.
|
||||
func.restype = c_int
|
||||
func.errcheck = partial(check_errcode, cpl=cpl)
|
||||
else:
|
||||
func.restype = None
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def voidptr_output(func, argtypes, errcheck=True):
|
||||
"For functions that return c_void_p."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_void_p
|
||||
if errcheck:
|
||||
func.errcheck = check_pointer
|
||||
return func
|
||||
|
||||
|
||||
def chararray_output(func, argtypes, errcheck=True):
|
||||
"""For functions that return a c_char_p array."""
|
||||
func.argtypes = argtypes
|
||||
func.restype = POINTER(c_char_p)
|
||||
if errcheck:
|
||||
func.errcheck = check_pointer
|
||||
return func
|
||||
@@ -0,0 +1,80 @@
|
||||
from ctypes import POINTER, c_char_p, c_int, c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.libgdal import lgdal, std_call
|
||||
from django.contrib.gis.gdal.prototypes.generation import (
|
||||
const_string_output, double_output, int_output, srs_output, string_output,
|
||||
void_output,
|
||||
)
|
||||
|
||||
|
||||
# Shortcut generation for routines with known parameters.
|
||||
def srs_double(f):
|
||||
"""
|
||||
Create a function prototype for the OSR routines that take
|
||||
the OSRSpatialReference object and return a double value.
|
||||
"""
|
||||
return double_output(f, [c_void_p, POINTER(c_int)], errcheck=True)
|
||||
|
||||
|
||||
def units_func(f):
|
||||
"""
|
||||
Create a ctypes function prototype for OSR units functions, e.g.,
|
||||
OSRGetAngularUnits, OSRGetLinearUnits.
|
||||
"""
|
||||
return double_output(f, [c_void_p, POINTER(c_char_p)], strarg=True)
|
||||
|
||||
|
||||
# Creation & destruction.
|
||||
clone_srs = srs_output(std_call('OSRClone'), [c_void_p])
|
||||
new_srs = srs_output(std_call('OSRNewSpatialReference'), [c_char_p])
|
||||
release_srs = void_output(lgdal.OSRRelease, [c_void_p], errcheck=False)
|
||||
destroy_srs = void_output(std_call('OSRDestroySpatialReference'), [c_void_p], errcheck=False)
|
||||
srs_validate = void_output(lgdal.OSRValidate, [c_void_p])
|
||||
|
||||
# Getting the semi_major, semi_minor, and flattening functions.
|
||||
semi_major = srs_double(lgdal.OSRGetSemiMajor)
|
||||
semi_minor = srs_double(lgdal.OSRGetSemiMinor)
|
||||
invflattening = srs_double(lgdal.OSRGetInvFlattening)
|
||||
|
||||
# WKT, PROJ, EPSG, XML importation routines.
|
||||
from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)])
|
||||
from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p])
|
||||
from_epsg = void_output(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])
|
||||
morph_from_esri = void_output(lgdal.OSRMorphFromESRI, [c_void_p])
|
||||
|
||||
# Identifying the EPSG
|
||||
identify_epsg = void_output(lgdal.OSRAutoIdentifyEPSG, [c_void_p])
|
||||
|
||||
# Getting the angular_units, linear_units functions
|
||||
linear_units = units_func(lgdal.OSRGetLinearUnits)
|
||||
angular_units = units_func(lgdal.OSRGetAngularUnits)
|
||||
|
||||
# For exporting to WKT, PROJ.4, "Pretty" WKT, and XML.
|
||||
to_wkt = string_output(std_call('OSRExportToWkt'), [c_void_p, POINTER(c_char_p)], decoding='utf-8')
|
||||
to_proj = string_output(std_call('OSRExportToProj4'), [c_void_p, POINTER(c_char_p)], decoding='ascii')
|
||||
to_pretty_wkt = string_output(
|
||||
std_call('OSRExportToPrettyWkt'),
|
||||
[c_void_p, POINTER(c_char_p), c_int], offset=-2, decoding='utf-8'
|
||||
)
|
||||
|
||||
# Memory leak fixed in GDAL 1.5; still exists in 1.4.
|
||||
to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2, decoding='utf-8')
|
||||
|
||||
# String attribute retrival routines.
|
||||
get_attr_value = const_string_output(std_call('OSRGetAttrValue'), [c_void_p, c_char_p, c_int], decoding='utf-8')
|
||||
get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p], decoding='ascii')
|
||||
get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p], decoding='ascii')
|
||||
|
||||
# SRS Properties
|
||||
isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p])
|
||||
islocal = int_output(lgdal.OSRIsLocal, [c_void_p])
|
||||
isprojected = int_output(lgdal.OSRIsProjected, [c_void_p])
|
||||
|
||||
# Coordinate transformation
|
||||
new_ct = srs_output(std_call('OCTNewCoordinateTransformation'), [c_void_p, c_void_p])
|
||||
destroy_ct = void_output(std_call('OCTDestroyCoordinateTransformation'), [c_void_p], errcheck=False)
|
||||
@@ -0,0 +1,252 @@
|
||||
from ctypes import byref, c_double, c_int, c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.prototypes import raster as capi
|
||||
from django.contrib.gis.gdal.raster.base import GDALRasterBase
|
||||
from django.contrib.gis.shortcuts import numpy
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from .const import (
|
||||
GDAL_COLOR_TYPES, GDAL_INTEGER_TYPES, GDAL_PIXEL_TYPES, GDAL_TO_CTYPES,
|
||||
)
|
||||
|
||||
|
||||
class GDALBand(GDALRasterBase):
|
||||
"""
|
||||
Wrap a GDAL raster band, needs to be obtained from a GDALRaster object.
|
||||
"""
|
||||
def __init__(self, source, index):
|
||||
self.source = source
|
||||
self._ptr = capi.get_ds_raster_band(source._ptr, index)
|
||||
|
||||
def _flush(self):
|
||||
"""
|
||||
Call the flush method on the Band's parent raster and force a refresh
|
||||
of the statistics attribute when requested the next time.
|
||||
"""
|
||||
self.source._flush()
|
||||
self._stats_refresh = True
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
"""
|
||||
Return the description string of the band.
|
||||
"""
|
||||
return force_text(capi.get_band_description(self._ptr))
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""
|
||||
Width (X axis) in pixels of the band.
|
||||
"""
|
||||
return capi.get_band_xsize(self._ptr)
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""
|
||||
Height (Y axis) in pixels of the band.
|
||||
"""
|
||||
return capi.get_band_ysize(self._ptr)
|
||||
|
||||
@property
|
||||
def pixel_count(self):
|
||||
"""
|
||||
Return the total number of pixels in this band.
|
||||
"""
|
||||
return self.width * self.height
|
||||
|
||||
_stats_refresh = False
|
||||
|
||||
def statistics(self, refresh=False, approximate=False):
|
||||
"""
|
||||
Compute statistics on the pixel values of this band.
|
||||
|
||||
The return value is a tuple with the following structure:
|
||||
(minimum, maximum, mean, standard deviation).
|
||||
|
||||
If approximate=True, the statistics may be computed based on overviews
|
||||
or a subset of image tiles.
|
||||
|
||||
If refresh=True, the statistics will be computed from the data directly,
|
||||
and the cache will be updated where applicable.
|
||||
|
||||
For empty bands (where all pixel values are nodata), all statistics
|
||||
values are returned as None.
|
||||
|
||||
For raster formats using Persistent Auxiliary Metadata (PAM) services,
|
||||
the statistics might be cached in an auxiliary file.
|
||||
"""
|
||||
# Prepare array with arguments for capi function
|
||||
smin, smax, smean, sstd = c_double(), c_double(), c_double(), c_double()
|
||||
stats_args = [
|
||||
self._ptr, c_int(approximate), byref(smin), byref(smax),
|
||||
byref(smean), byref(sstd), c_void_p(), c_void_p(),
|
||||
]
|
||||
|
||||
if refresh or self._stats_refresh:
|
||||
func = capi.compute_band_statistics
|
||||
else:
|
||||
# Add additional argument to force computation if there is no
|
||||
# existing PAM file to take the values from.
|
||||
force = True
|
||||
stats_args.insert(2, c_int(force))
|
||||
func = capi.get_band_statistics
|
||||
|
||||
# Computation of statistics fails for empty bands.
|
||||
try:
|
||||
func(*stats_args)
|
||||
result = smin.value, smax.value, smean.value, sstd.value
|
||||
except GDALException:
|
||||
result = (None, None, None, None)
|
||||
|
||||
self._stats_refresh = False
|
||||
|
||||
return result
|
||||
|
||||
@property
|
||||
def min(self):
|
||||
"""
|
||||
Return the minimum pixel value for this band.
|
||||
"""
|
||||
return self.statistics()[0]
|
||||
|
||||
@property
|
||||
def max(self):
|
||||
"""
|
||||
Return the maximum pixel value for this band.
|
||||
"""
|
||||
return self.statistics()[1]
|
||||
|
||||
@property
|
||||
def mean(self):
|
||||
"""
|
||||
Return the mean of all pixel values of this band.
|
||||
"""
|
||||
return self.statistics()[2]
|
||||
|
||||
@property
|
||||
def std(self):
|
||||
"""
|
||||
Return the standard deviation of all pixel values of this band.
|
||||
"""
|
||||
return self.statistics()[3]
|
||||
|
||||
@property
|
||||
def nodata_value(self):
|
||||
"""
|
||||
Return the nodata value for this band, or None if it isn't set.
|
||||
"""
|
||||
# Get value and nodata exists flag
|
||||
nodata_exists = c_int()
|
||||
value = capi.get_band_nodata_value(self._ptr, nodata_exists)
|
||||
if not nodata_exists:
|
||||
value = None
|
||||
# If the pixeltype is an integer, convert to int
|
||||
elif self.datatype() in GDAL_INTEGER_TYPES:
|
||||
value = int(value)
|
||||
return value
|
||||
|
||||
@nodata_value.setter
|
||||
def nodata_value(self, value):
|
||||
"""
|
||||
Set the nodata value for this band.
|
||||
"""
|
||||
if value is None:
|
||||
if not capi.delete_band_nodata_value:
|
||||
raise ValueError('GDAL >= 2.1 required to delete nodata values.')
|
||||
capi.delete_band_nodata_value(self._ptr)
|
||||
elif not isinstance(value, (int, float)):
|
||||
raise ValueError('Nodata value must be numeric or None.')
|
||||
else:
|
||||
capi.set_band_nodata_value(self._ptr, value)
|
||||
self._flush()
|
||||
|
||||
def datatype(self, as_string=False):
|
||||
"""
|
||||
Return the GDAL Pixel Datatype for this band.
|
||||
"""
|
||||
dtype = capi.get_band_datatype(self._ptr)
|
||||
if as_string:
|
||||
dtype = GDAL_PIXEL_TYPES[dtype]
|
||||
return dtype
|
||||
|
||||
def color_interp(self, as_string=False):
|
||||
"""Return the GDAL color interpretation for this band."""
|
||||
color = capi.get_band_color_interp(self._ptr)
|
||||
if as_string:
|
||||
color = GDAL_COLOR_TYPES[color]
|
||||
return color
|
||||
|
||||
def data(self, data=None, offset=None, size=None, shape=None, as_memoryview=False):
|
||||
"""
|
||||
Read or writes pixel values for this band. Blocks of data can
|
||||
be accessed by specifying the width, height and offset of the
|
||||
desired block. The same specification can be used to update
|
||||
parts of a raster by providing an array of values.
|
||||
|
||||
Allowed input data types are bytes, memoryview, list, tuple, and array.
|
||||
"""
|
||||
offset = offset or (0, 0)
|
||||
size = size or (self.width - offset[0], self.height - offset[1])
|
||||
shape = shape or size
|
||||
if any(x <= 0 for x in size):
|
||||
raise ValueError('Offset too big for this raster.')
|
||||
|
||||
if size[0] > self.width or size[1] > self.height:
|
||||
raise ValueError('Size is larger than raster.')
|
||||
|
||||
# Create ctypes type array generator
|
||||
ctypes_array = GDAL_TO_CTYPES[self.datatype()] * (shape[0] * shape[1])
|
||||
|
||||
if data is None:
|
||||
# Set read mode
|
||||
access_flag = 0
|
||||
# Prepare empty ctypes array
|
||||
data_array = ctypes_array()
|
||||
else:
|
||||
# Set write mode
|
||||
access_flag = 1
|
||||
|
||||
# Instantiate ctypes array holding the input data
|
||||
if isinstance(data, (bytes, memoryview)) or (numpy and isinstance(data, numpy.ndarray)):
|
||||
data_array = ctypes_array.from_buffer_copy(data)
|
||||
else:
|
||||
data_array = ctypes_array(*data)
|
||||
|
||||
# Access band
|
||||
capi.band_io(self._ptr, access_flag, offset[0], offset[1],
|
||||
size[0], size[1], byref(data_array), shape[0],
|
||||
shape[1], self.datatype(), 0, 0)
|
||||
|
||||
# Return data as numpy array if possible, otherwise as list
|
||||
if data is None:
|
||||
if as_memoryview:
|
||||
return memoryview(data_array)
|
||||
elif numpy:
|
||||
# reshape() needs a reshape parameter with the height first.
|
||||
return numpy.frombuffer(
|
||||
data_array, dtype=numpy.dtype(data_array)
|
||||
).reshape(tuple(reversed(size)))
|
||||
else:
|
||||
return list(data_array)
|
||||
else:
|
||||
self._flush()
|
||||
|
||||
|
||||
class BandList(list):
|
||||
def __init__(self, source):
|
||||
self.source = source
|
||||
super().__init__()
|
||||
|
||||
def __iter__(self):
|
||||
for idx in range(1, len(self) + 1):
|
||||
yield GDALBand(self.source, idx)
|
||||
|
||||
def __len__(self):
|
||||
return capi.get_ds_raster_count(self.source._ptr)
|
||||
|
||||
def __getitem__(self, index):
|
||||
try:
|
||||
return GDALBand(self.source, index + 1)
|
||||
except GDALException:
|
||||
raise GDALException('Unable to get band index %d' % index)
|
||||
@@ -0,0 +1,75 @@
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.prototypes import raster as capi
|
||||
|
||||
|
||||
class GDALRasterBase(GDALBase):
|
||||
"""
|
||||
Attributes that exist on both GDALRaster and GDALBand.
|
||||
"""
|
||||
@property
|
||||
def metadata(self):
|
||||
"""
|
||||
Return the metadata for this raster or band. The return value is a
|
||||
nested dictionary, where the first-level key is the metadata domain and
|
||||
the second-level is the metadata item names and values for that domain.
|
||||
"""
|
||||
# The initial metadata domain list contains the default domain.
|
||||
# The default is returned if domain name is None.
|
||||
domain_list = ['DEFAULT']
|
||||
|
||||
# Get additional metadata domains from the raster.
|
||||
meta_list = capi.get_ds_metadata_domain_list(self._ptr)
|
||||
if meta_list:
|
||||
# The number of domains is unknown, so retrieve data until there
|
||||
# are no more values in the ctypes array.
|
||||
counter = 0
|
||||
domain = meta_list[counter]
|
||||
while domain:
|
||||
domain_list.append(domain.decode())
|
||||
counter += 1
|
||||
domain = meta_list[counter]
|
||||
|
||||
# Free domain list array.
|
||||
capi.free_dsl(meta_list)
|
||||
|
||||
# Retrieve metadata values for each domain.
|
||||
result = {}
|
||||
for domain in domain_list:
|
||||
# Get metadata for this domain.
|
||||
data = capi.get_ds_metadata(
|
||||
self._ptr,
|
||||
(None if domain == 'DEFAULT' else domain.encode()),
|
||||
)
|
||||
if not data:
|
||||
continue
|
||||
# The number of metadata items is unknown, so retrieve data until
|
||||
# there are no more values in the ctypes array.
|
||||
domain_meta = {}
|
||||
counter = 0
|
||||
item = data[counter]
|
||||
while item:
|
||||
key, val = item.decode().split('=')
|
||||
domain_meta[key] = val
|
||||
counter += 1
|
||||
item = data[counter]
|
||||
# The default domain values are returned if domain is None.
|
||||
result[domain or 'DEFAULT'] = domain_meta
|
||||
return result
|
||||
|
||||
@metadata.setter
|
||||
def metadata(self, value):
|
||||
"""
|
||||
Set the metadata. Update only the domains that are contained in the
|
||||
value dictionary.
|
||||
"""
|
||||
# Loop through domains.
|
||||
for domain, metadata in value.items():
|
||||
# Set the domain to None for the default, otherwise encode.
|
||||
domain = None if domain == 'DEFAULT' else domain.encode()
|
||||
# Set each metadata entry separately.
|
||||
for meta_name, meta_value in metadata.items():
|
||||
capi.set_ds_metadata_item(
|
||||
self._ptr, meta_name.encode(),
|
||||
meta_value.encode() if meta_value else None,
|
||||
domain,
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
GDAL - Constant definitions
|
||||
"""
|
||||
from ctypes import (
|
||||
c_double, c_float, c_int16, c_int32, c_ubyte, c_uint16, c_uint32,
|
||||
)
|
||||
|
||||
# See https://www.gdal.org/gdal_8h.html#a22e22ce0a55036a96f652765793fb7a4
|
||||
GDAL_PIXEL_TYPES = {
|
||||
0: 'GDT_Unknown', # Unknown or unspecified type
|
||||
1: 'GDT_Byte', # Eight bit unsigned integer
|
||||
2: 'GDT_UInt16', # Sixteen bit unsigned integer
|
||||
3: 'GDT_Int16', # Sixteen bit signed integer
|
||||
4: 'GDT_UInt32', # Thirty-two bit unsigned integer
|
||||
5: 'GDT_Int32', # Thirty-two bit signed integer
|
||||
6: 'GDT_Float32', # Thirty-two bit floating point
|
||||
7: 'GDT_Float64', # Sixty-four bit floating point
|
||||
8: 'GDT_CInt16', # Complex Int16
|
||||
9: 'GDT_CInt32', # Complex Int32
|
||||
10: 'GDT_CFloat32', # Complex Float32
|
||||
11: 'GDT_CFloat64', # Complex Float64
|
||||
}
|
||||
|
||||
# A list of gdal datatypes that are integers.
|
||||
GDAL_INTEGER_TYPES = [1, 2, 3, 4, 5]
|
||||
|
||||
# Lookup values to convert GDAL pixel type indices into ctypes objects.
|
||||
# The GDAL band-io works with ctypes arrays to hold data to be written
|
||||
# or to hold the space for data to be read into. The lookup below helps
|
||||
# selecting the right ctypes object for a given gdal pixel type.
|
||||
GDAL_TO_CTYPES = [
|
||||
None, c_ubyte, c_uint16, c_int16, c_uint32, c_int32,
|
||||
c_float, c_double, None, None, None, None
|
||||
]
|
||||
|
||||
# List of resampling algorithms that can be used to warp a GDALRaster.
|
||||
GDAL_RESAMPLE_ALGORITHMS = {
|
||||
'NearestNeighbour': 0,
|
||||
'Bilinear': 1,
|
||||
'Cubic': 2,
|
||||
'CubicSpline': 3,
|
||||
'Lanczos': 4,
|
||||
'Average': 5,
|
||||
'Mode': 6,
|
||||
}
|
||||
|
||||
# See https://www.gdal.org/gdal_8h.html#ace76452d94514561fffa8ea1d2a5968c
|
||||
GDAL_COLOR_TYPES = {
|
||||
0: 'GCI_Undefined', # Undefined, default value, i.e. not known
|
||||
1: 'GCI_GrayIndex', # Greyscale
|
||||
2: 'GCI_PaletteIndex', # Paletted
|
||||
3: 'GCI_RedBand', # Red band of RGBA image
|
||||
4: 'GCI_GreenBand', # Green band of RGBA image
|
||||
5: 'GCI_BlueBand', # Blue band of RGBA image
|
||||
6: 'GCI_AlphaBand', # Alpha (0=transparent, 255=opaque)
|
||||
7: 'GCI_HueBand', # Hue band of HLS image
|
||||
8: 'GCI_SaturationBand', # Saturation band of HLS image
|
||||
9: 'GCI_LightnessBand', # Lightness band of HLS image
|
||||
10: 'GCI_CyanBand', # Cyan band of CMYK image
|
||||
11: 'GCI_MagentaBand', # Magenta band of CMYK image
|
||||
12: 'GCI_YellowBand', # Yellow band of CMYK image
|
||||
13: 'GCI_BlackBand', # Black band of CMLY image
|
||||
14: 'GCI_YCbCr_YBand', # Y Luminance
|
||||
15: 'GCI_YCbCr_CbBand', # Cb Chroma
|
||||
16: 'GCI_YCbCr_CrBand', # Cr Chroma, also GCI_Max
|
||||
}
|
||||
|
||||
# Fixed base path for buffer-based GDAL in-memory files.
|
||||
VSI_FILESYSTEM_BASE_PATH = '/vsimem/'
|
||||
|
||||
# Should the memory file system take ownership of the buffer, freeing it when
|
||||
# the file is deleted? (No, GDALRaster.__del__() will delete the buffer.)
|
||||
VSI_TAKE_BUFFER_OWNERSHIP = False
|
||||
|
||||
# Should a VSI file be removed when retrieving its buffer?
|
||||
VSI_DELETE_BUFFER_ON_READ = False
|
||||
@@ -0,0 +1,335 @@
|
||||
"""
|
||||
The Spatial Reference class, represents OGR Spatial Reference objects.
|
||||
|
||||
Example:
|
||||
>>> from django.contrib.gis.gdal import SpatialReference
|
||||
>>> srs = SpatialReference('WGS84')
|
||||
>>> print(srs)
|
||||
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"]]
|
||||
>>> print(srs.proj)
|
||||
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
|
||||
>>> print(srs.ellipsoid)
|
||||
(6378137.0, 6356752.3142451793, 298.25722356300003)
|
||||
>>> print(srs.projected, srs.geographic)
|
||||
False True
|
||||
>>> srs.import_epsg(32140)
|
||||
>>> print(srs.name)
|
||||
NAD83 / Texas South Central
|
||||
"""
|
||||
from ctypes import byref, c_char_p, c_int
|
||||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.error import SRSException
|
||||
from django.contrib.gis.gdal.prototypes import srs as capi
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
|
||||
class SpatialReference(GDALBase):
|
||||
"""
|
||||
A wrapper for the OGRSpatialReference object. According to the GDAL Web site,
|
||||
the SpatialReference object "provide[s] services to represent coordinate
|
||||
systems (projections and datums) and to transform between them."
|
||||
"""
|
||||
destructor = capi.release_srs
|
||||
|
||||
def __init__(self, srs_input='', srs_type='user'):
|
||||
"""
|
||||
Create 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').
|
||||
"""
|
||||
|
||||
if srs_type == 'wkt':
|
||||
self.ptr = capi.new_srs(c_char_p(b''))
|
||||
self.import_wkt(srs_input)
|
||||
return
|
||||
elif isinstance(srs_input, str):
|
||||
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
|
||||
elif isinstance(srs_input, int):
|
||||
# EPSG integer code was input.
|
||||
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':
|
||||
# Input is already an SRS pointer.
|
||||
srs = srs_input
|
||||
else:
|
||||
# Creating a new SRS pointer, using the string buffer.
|
||||
buf = c_char_p(b'')
|
||||
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
|
||||
|
||||
# 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 __getitem__(self, target):
|
||||
"""
|
||||
Return 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 index of the attribute in the WKT. For example:
|
||||
|
||||
>>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]'
|
||||
>>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326
|
||||
>>> print(srs['GEOGCS'])
|
||||
WGS 84
|
||||
>>> print(srs['DATUM'])
|
||||
WGS_1984
|
||||
>>> print(srs['AUTHORITY'])
|
||||
EPSG
|
||||
>>> print(srs['AUTHORITY', 1]) # The authority value
|
||||
4326
|
||||
>>> print(srs['TOWGS84', 4]) # the fourth value in this wkt
|
||||
0
|
||||
>>> print(srs['UNIT|AUTHORITY']) # For the units authority, have to use the pipe symbole.
|
||||
EPSG
|
||||
>>> print(srs['UNIT|AUTHORITY', 1]) # The authority value for the units
|
||||
9122
|
||||
"""
|
||||
if isinstance(target, tuple):
|
||||
return self.attr_value(*target)
|
||||
else:
|
||||
return self.attr_value(target)
|
||||
|
||||
def __str__(self):
|
||||
"Use 'pretty' WKT."
|
||||
return self.pretty_wkt
|
||||
|
||||
# #### SpatialReference Methods ####
|
||||
def attr_value(self, target, index=0):
|
||||
"""
|
||||
The attribute value for the given target node (e.g. 'PROJCS'). The index
|
||||
keyword specifies an index of the child node to return.
|
||||
"""
|
||||
if not isinstance(target, str) or not isinstance(index, int):
|
||||
raise TypeError
|
||||
return capi.get_attr_value(self.ptr, force_bytes(target), index)
|
||||
|
||||
def auth_name(self, target):
|
||||
"Return the authority name for the given string target node."
|
||||
return capi.get_auth_name(self.ptr, force_bytes(target))
|
||||
|
||||
def auth_code(self, target):
|
||||
"Return the authority code for the given string target node."
|
||||
return capi.get_auth_code(self.ptr, force_bytes(target))
|
||||
|
||||
def clone(self):
|
||||
"Return a clone of this SpatialReference object."
|
||||
return SpatialReference(capi.clone_srs(self.ptr))
|
||||
|
||||
def from_esri(self):
|
||||
"Morph this SpatialReference from ESRI's format to EPSG."
|
||||
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.
|
||||
"""
|
||||
capi.identify_epsg(self.ptr)
|
||||
|
||||
def to_esri(self):
|
||||
"Morph this SpatialReference to ESRI's format."
|
||||
capi.morph_to_esri(self.ptr)
|
||||
|
||||
def validate(self):
|
||||
"Check to see if the given spatial reference is valid."
|
||||
capi.srs_validate(self.ptr)
|
||||
|
||||
# #### Name & SRID properties ####
|
||||
@property
|
||||
def name(self):
|
||||
"Return 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
|
||||
|
||||
@property
|
||||
def srid(self):
|
||||
"Return the SRID of top-level authority, or None if undefined."
|
||||
try:
|
||||
return int(self.attr_value('AUTHORITY', 1))
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
# #### Unit Properties ####
|
||||
@property
|
||||
def linear_name(self):
|
||||
"Return the name of the linear units."
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
return name
|
||||
|
||||
@property
|
||||
def linear_units(self):
|
||||
"Return the value of the linear units."
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
return units
|
||||
|
||||
@property
|
||||
def angular_name(self):
|
||||
"Return the name of the angular units."
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
return name
|
||||
|
||||
@property
|
||||
def angular_units(self):
|
||||
"Return the value of the angular units."
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
return units
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
"""
|
||||
Return a 2-tuple of the units value and the units name. Automatically
|
||||
determine whether to return the linear or angular units.
|
||||
"""
|
||||
units, name = None, None
|
||||
if self.projected or self.local:
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
elif self.geographic:
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
if name is not None:
|
||||
name = force_text(name)
|
||||
return (units, name)
|
||||
|
||||
# #### Spheroid/Ellipsoid Properties ####
|
||||
@property
|
||||
def ellipsoid(self):
|
||||
"""
|
||||
Return a tuple of the ellipsoid parameters:
|
||||
(semimajor axis, semiminor axis, and inverse flattening)
|
||||
"""
|
||||
return (self.semi_major, self.semi_minor, self.inverse_flattening)
|
||||
|
||||
@property
|
||||
def semi_major(self):
|
||||
"Return the Semi Major Axis for this Spatial Reference."
|
||||
return capi.semi_major(self.ptr, byref(c_int()))
|
||||
|
||||
@property
|
||||
def semi_minor(self):
|
||||
"Return the Semi Minor Axis for this Spatial Reference."
|
||||
return capi.semi_minor(self.ptr, byref(c_int()))
|
||||
|
||||
@property
|
||||
def inverse_flattening(self):
|
||||
"Return the Inverse Flattening for this Spatial Reference."
|
||||
return capi.invflattening(self.ptr, byref(c_int()))
|
||||
|
||||
# #### Boolean Properties ####
|
||||
@property
|
||||
def geographic(self):
|
||||
"""
|
||||
Return True if this SpatialReference is geographic
|
||||
(root node is GEOGCS).
|
||||
"""
|
||||
return bool(capi.isgeographic(self.ptr))
|
||||
|
||||
@property
|
||||
def local(self):
|
||||
"Return True if this SpatialReference is local (root node is LOCAL_CS)."
|
||||
return bool(capi.islocal(self.ptr))
|
||||
|
||||
@property
|
||||
def projected(self):
|
||||
"""
|
||||
Return True if this SpatialReference is a projected coordinate system
|
||||
(root node is PROJCS).
|
||||
"""
|
||||
return bool(capi.isprojected(self.ptr))
|
||||
|
||||
# #### Import Routines #####
|
||||
def import_epsg(self, epsg):
|
||||
"Import the Spatial Reference from the EPSG code (an integer)."
|
||||
capi.from_epsg(self.ptr, epsg)
|
||||
|
||||
def import_proj(self, proj):
|
||||
"Import the Spatial Reference from a PROJ.4 string."
|
||||
capi.from_proj(self.ptr, proj)
|
||||
|
||||
def import_user_input(self, user_input):
|
||||
"Import the Spatial Reference from the given user input string."
|
||||
capi.from_user_input(self.ptr, force_bytes(user_input))
|
||||
|
||||
def import_wkt(self, wkt):
|
||||
"Import the Spatial Reference from OGC WKT (string)"
|
||||
capi.from_wkt(self.ptr, byref(c_char_p(force_bytes(wkt))))
|
||||
|
||||
def import_xml(self, xml):
|
||||
"Import the Spatial Reference from an XML string."
|
||||
capi.from_xml(self.ptr, xml)
|
||||
|
||||
# #### Export Properties ####
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return the WKT representation of this Spatial Reference."
|
||||
return capi.to_wkt(self.ptr, byref(c_char_p()))
|
||||
|
||||
@property
|
||||
def pretty_wkt(self, simplify=0):
|
||||
"Return the 'pretty' representation of the WKT."
|
||||
return capi.to_pretty_wkt(self.ptr, byref(c_char_p()), simplify)
|
||||
|
||||
@property
|
||||
def proj(self):
|
||||
"Return the PROJ.4 representation for this Spatial Reference."
|
||||
return capi.to_proj(self.ptr, byref(c_char_p()))
|
||||
|
||||
@property
|
||||
def proj4(self):
|
||||
"Alias for proj()."
|
||||
return self.proj
|
||||
|
||||
@property
|
||||
def xml(self, dialect=''):
|
||||
"Return the XML representation of this Spatial Reference."
|
||||
return capi.to_xml(self.ptr, byref(c_char_p()), force_bytes(dialect))
|
||||
|
||||
|
||||
class CoordTransform(GDALBase):
|
||||
"The coordinate system transformation object."
|
||||
destructor = capi.destroy_ct
|
||||
|
||||
def __init__(self, source, target):
|
||||
"Initialize on a source and target SpatialReference objects."
|
||||
if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference):
|
||||
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 __str__(self):
|
||||
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)
|
||||
Reference in new issue
Block a user