app/django/contrib/gis/gdal/srs.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 """
       
     2   The Spatial Reference class, represensents OGR Spatial Reference objects.
       
     3 
       
     4   Example:
       
     5   >>> from django.contrib.gis.gdal import SpatialReference
       
     6   >>> srs = SpatialReference('WGS84')
       
     7   >>> print srs
       
     8   GEOGCS["WGS 84",
       
     9       DATUM["WGS_1984",
       
    10           SPHEROID["WGS 84",6378137,298.257223563,
       
    11               AUTHORITY["EPSG","7030"]],
       
    12           TOWGS84[0,0,0,0,0,0,0],
       
    13           AUTHORITY["EPSG","6326"]],
       
    14       PRIMEM["Greenwich",0,
       
    15           AUTHORITY["EPSG","8901"]],
       
    16       UNIT["degree",0.01745329251994328,
       
    17           AUTHORITY["EPSG","9122"]],
       
    18       AUTHORITY["EPSG","4326"]]
       
    19   >>> print srs.proj
       
    20   +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
       
    21   >>> print srs.ellipsoid
       
    22   (6378137.0, 6356752.3142451793, 298.25722356300003)
       
    23   >>> print srs.projected, srs.geographic
       
    24   False True
       
    25   >>> srs.import_epsg(32140)
       
    26   >>> print srs.name
       
    27   NAD83 / Texas South Central
       
    28 """
       
    29 import re
       
    30 from types import UnicodeType, TupleType
       
    31 from ctypes import byref, c_char_p, c_int, c_void_p
       
    32 
       
    33 # Getting the error checking routine and exceptions
       
    34 from django.contrib.gis.gdal.error import OGRException, SRSException
       
    35 from django.contrib.gis.gdal.prototypes.srs import *
       
    36 
       
    37 #### Spatial Reference class. ####
       
    38 class SpatialReference(object):
       
    39     """
       
    40     A wrapper for the OGRSpatialReference object.  According to the GDAL website,
       
    41     the SpatialReference object "provide[s] services to represent coordinate 
       
    42     systems (projections and datums) and to transform between them."
       
    43     """
       
    44 
       
    45     # Well-Known Geographical Coordinate System Name
       
    46     _well_known = {'WGS84':4326, 'WGS72':4322, 'NAD27':4267, 'NAD83':4269}
       
    47     _epsg_regex = re.compile('^(EPSG:)?(?P<epsg>\d+)$', re.I)
       
    48     _proj_regex = re.compile(r'^\+proj')
       
    49 
       
    50     #### Python 'magic' routines ####
       
    51     def __init__(self, srs_input='', srs_type='wkt'):
       
    52         """
       
    53         Creates a GDAL OSR Spatial Reference object from the given input.
       
    54         The input may be string of OGC Well Known Text (WKT), an integer 
       
    55         EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand 
       
    56         string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83').
       
    57         """
       
    58         # Intializing pointer and string buffer.
       
    59         self._ptr = None
       
    60         buf = c_char_p('')
       
    61 
       
    62         if isinstance(srs_input, basestring):
       
    63             # Encoding to ASCII if unicode passed in.
       
    64             if isinstance(srs_input, UnicodeType):
       
    65                 srs_input = srs_input.encode('ascii')
       
    66 
       
    67             epsg_m = self._epsg_regex.match(srs_input)
       
    68             proj_m = self._proj_regex.match(srs_input)
       
    69             if epsg_m:
       
    70                 # Is this an EPSG well known name?    
       
    71                 srs_type = 'epsg'
       
    72                 srs_input = int(epsg_m.group('epsg'))
       
    73             elif proj_m:
       
    74                 # Is the string a PROJ.4 string?
       
    75                 srs_type = 'proj'
       
    76             elif srs_input in self._well_known:
       
    77                 # Is this a short-hand well known name?  
       
    78                 srs_type = 'epsg'
       
    79                 srs_input = self._well_known[srs_input]
       
    80             elif srs_type == 'proj':
       
    81                 pass
       
    82             else:
       
    83                 # Setting the buffer with WKT, PROJ.4 string, etc.
       
    84                 buf = c_char_p(srs_input)
       
    85         elif isinstance(srs_input, int):
       
    86             # EPSG integer code was input.
       
    87             if srs_type != 'epsg': srs_type = 'epsg'
       
    88         elif isinstance(srs_input, c_void_p):
       
    89             srs_type = 'ogr'
       
    90         else:
       
    91             raise TypeError('Invalid SRS type "%s"' % srs_type)
       
    92 
       
    93         if srs_type == 'ogr':
       
    94             # SRS input is OGR pointer
       
    95             srs = srs_input
       
    96         else:
       
    97             # Creating a new pointer, using the string buffer.
       
    98             srs = new_srs(buf)
       
    99 
       
   100         # If the pointer is NULL, throw an exception.
       
   101         if not srs:
       
   102             raise SRSException('Could not create spatial reference from: %s' % srs_input)
       
   103         else:
       
   104             self._ptr = srs
       
   105 
       
   106         # Post-processing if in PROJ.4 or EPSG formats.
       
   107         if srs_type == 'proj': self.import_proj(srs_input)
       
   108         elif srs_type == 'epsg': self.import_epsg(srs_input)
       
   109 
       
   110     def __del__(self):
       
   111         "Destroys this spatial reference."
       
   112         if self._ptr: release_srs(self._ptr)
       
   113 
       
   114     def __getitem__(self, target):
       
   115         """
       
   116         Returns the value of the given string attribute node, None if the node 
       
   117         doesn't exist.  Can also take a tuple as a parameter, (target, child), 
       
   118         where child is the index of the attribute in the WKT.  For example:
       
   119 
       
   120         >>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]')
       
   121         >>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326
       
   122         >>> print srs['GEOGCS']
       
   123         WGS 84
       
   124         >>> print srs['DATUM']
       
   125         WGS_1984
       
   126         >>> print srs['AUTHORITY']
       
   127         EPSG
       
   128         >>> print srs['AUTHORITY', 1] # The authority value
       
   129         4326
       
   130         >>> print srs['TOWGS84', 4] # the fourth value in this wkt
       
   131         0
       
   132         >>> print srs['UNIT|AUTHORITY'] # For the units authority, have to use the pipe symbole.
       
   133         EPSG
       
   134         >>> print srs['UNIT|AUTHORITY', 1] # The authority value for the untis
       
   135         9122
       
   136         """
       
   137         if isinstance(target, TupleType):
       
   138             return self.attr_value(*target)
       
   139         else:
       
   140             return self.attr_value(target)
       
   141 
       
   142     def __str__(self):
       
   143         "The string representation uses 'pretty' WKT."
       
   144         return self.pretty_wkt
       
   145 
       
   146     #### SpatialReference Methods ####
       
   147     def attr_value(self, target, index=0):
       
   148         """
       
   149         The attribute value for the given target node (e.g. 'PROJCS'). The index
       
   150         keyword specifies an index of the child node to return.
       
   151         """
       
   152         if not isinstance(target, str) or not isinstance(index, int):
       
   153             raise TypeError
       
   154         return get_attr_value(self._ptr, target, index)
       
   155 
       
   156     def auth_name(self, target):
       
   157         "Returns the authority name for the given string target node."
       
   158         return get_auth_name(self._ptr, target)
       
   159     
       
   160     def auth_code(self, target):
       
   161         "Returns the authority code for the given string target node."
       
   162         return get_auth_code(self._ptr, target)
       
   163 
       
   164     def clone(self):
       
   165         "Returns a clone of this SpatialReference object."
       
   166         return SpatialReference(clone_srs(self._ptr))
       
   167 
       
   168     def from_esri(self):
       
   169         "Morphs this SpatialReference from ESRI's format to EPSG."
       
   170         morph_from_esri(self._ptr)
       
   171 
       
   172     def identify_epsg(self):
       
   173         """
       
   174         This method inspects the WKT of this SpatialReference, and will
       
   175         add EPSG authority nodes where an EPSG identifier is applicable.
       
   176         """
       
   177         identify_epsg(self._ptr)
       
   178 
       
   179     def to_esri(self):
       
   180         "Morphs this SpatialReference to ESRI's format."
       
   181         morph_to_esri(self._ptr)
       
   182 
       
   183     def validate(self):
       
   184         "Checks to see if the given spatial reference is valid."
       
   185         srs_validate(self._ptr)
       
   186     
       
   187     #### Name & SRID properties ####
       
   188     @property
       
   189     def name(self):
       
   190         "Returns the name of this Spatial Reference."
       
   191         if self.projected: return self.attr_value('PROJCS')
       
   192         elif self.geographic: return self.attr_value('GEOGCS')
       
   193         elif self.local: return self.attr_value('LOCAL_CS')
       
   194         else: return None
       
   195 
       
   196     @property
       
   197     def srid(self):
       
   198         "Returns the SRID of top-level authority, or None if undefined."
       
   199         try:
       
   200             return int(self.attr_value('AUTHORITY', 1))
       
   201         except (TypeError, ValueError):
       
   202             return None
       
   203         
       
   204     #### Unit Properties ####
       
   205     @property
       
   206     def linear_name(self):
       
   207         "Returns the name of the linear units."
       
   208         units, name = linear_units(self._ptr, byref(c_char_p()))
       
   209         return name
       
   210 
       
   211     @property
       
   212     def linear_units(self):
       
   213         "Returns the value of the linear units."
       
   214         units, name = linear_units(self._ptr, byref(c_char_p()))
       
   215         return units
       
   216 
       
   217     @property
       
   218     def angular_name(self):
       
   219         "Returns the name of the angular units."
       
   220         units, name = angular_units(self._ptr, byref(c_char_p()))
       
   221         return name
       
   222 
       
   223     @property
       
   224     def angular_units(self):
       
   225         "Returns the value of the angular units."
       
   226         units, name = angular_units(self._ptr, byref(c_char_p()))
       
   227         return units
       
   228 
       
   229     @property
       
   230     def units(self):
       
   231         """
       
   232         Returns a 2-tuple of the units value and the units name, 
       
   233         and will automatically determines whether to return the linear
       
   234         or angular units.
       
   235         """
       
   236         if self.projected or self.local:
       
   237             return linear_units(self._ptr, byref(c_char_p()))
       
   238         elif self.geographic:
       
   239             return angular_units(self._ptr, byref(c_char_p()))
       
   240         else:
       
   241             return (None, None)
       
   242 
       
   243     #### Spheroid/Ellipsoid Properties ####
       
   244     @property
       
   245     def ellipsoid(self):
       
   246         """
       
   247         Returns a tuple of the ellipsoid parameters:
       
   248          (semimajor axis, semiminor axis, and inverse flattening)
       
   249         """
       
   250         return (self.semi_major, self.semi_minor, self.inverse_flattening)
       
   251 
       
   252     @property
       
   253     def semi_major(self):
       
   254         "Returns the Semi Major Axis for this Spatial Reference."
       
   255         return semi_major(self._ptr, byref(c_int()))
       
   256 
       
   257     @property
       
   258     def semi_minor(self):
       
   259         "Returns the Semi Minor Axis for this Spatial Reference."
       
   260         return semi_minor(self._ptr, byref(c_int()))
       
   261 
       
   262     @property
       
   263     def inverse_flattening(self):
       
   264         "Returns the Inverse Flattening for this Spatial Reference."
       
   265         return invflattening(self._ptr, byref(c_int()))
       
   266 
       
   267     #### Boolean Properties ####
       
   268     @property
       
   269     def geographic(self):
       
   270         """
       
   271         Returns True if this SpatialReference is geographic 
       
   272          (root node is GEOGCS).
       
   273         """
       
   274         return bool(isgeographic(self._ptr))
       
   275 
       
   276     @property
       
   277     def local(self):
       
   278         "Returns True if this SpatialReference is local (root node is LOCAL_CS)."
       
   279         return bool(islocal(self._ptr))
       
   280 
       
   281     @property
       
   282     def projected(self):
       
   283         """
       
   284         Returns True if this SpatialReference is a projected coordinate system 
       
   285          (root node is PROJCS).
       
   286         """
       
   287         return bool(isprojected(self._ptr))
       
   288 
       
   289     #### Import Routines #####
       
   290     def import_wkt(self, wkt):
       
   291         "Imports the Spatial Reference from OGC WKT (string)"
       
   292         from_wkt(self._ptr, byref(c_char_p(wkt)))
       
   293 
       
   294     def import_proj(self, proj):
       
   295         "Imports the Spatial Reference from a PROJ.4 string."
       
   296         from_proj(self._ptr, proj)
       
   297 
       
   298     def import_epsg(self, epsg):
       
   299         "Imports the Spatial Reference from the EPSG code (an integer)."
       
   300         from_epsg(self._ptr, epsg)
       
   301 
       
   302     def import_xml(self, xml):
       
   303         "Imports the Spatial Reference from an XML string."
       
   304         from_xml(self._ptr, xml)
       
   305 
       
   306     #### Export Properties ####
       
   307     @property
       
   308     def wkt(self):
       
   309         "Returns the WKT representation of this Spatial Reference."
       
   310         return to_wkt(self._ptr, byref(c_char_p()))
       
   311 
       
   312     @property
       
   313     def pretty_wkt(self, simplify=0):
       
   314         "Returns the 'pretty' representation of the WKT."
       
   315         return to_pretty_wkt(self._ptr, byref(c_char_p()), simplify)
       
   316 
       
   317     @property
       
   318     def proj(self):
       
   319         "Returns the PROJ.4 representation for this Spatial Reference."
       
   320         return to_proj(self._ptr, byref(c_char_p()))
       
   321 
       
   322     @property
       
   323     def proj4(self):
       
   324         "Alias for proj()."
       
   325         return self.proj
       
   326 
       
   327     @property
       
   328     def xml(self, dialect=''):
       
   329         "Returns the XML representation of this Spatial Reference."
       
   330         # FIXME: This leaks memory, have to figure out why.
       
   331         return to_xml(self._ptr, byref(c_char_p()), dialect)
       
   332 
       
   333     def to_esri(self):
       
   334         "Morphs this SpatialReference to ESRI's format."
       
   335         morph_to_esri(self._ptr)
       
   336 
       
   337     def from_esri(self):
       
   338         "Morphs this SpatialReference from ESRI's format to EPSG."
       
   339         morph_from_esri(self._ptr)
       
   340 
       
   341 class CoordTransform(object):
       
   342     "The coordinate system transformation object."
       
   343 
       
   344     def __init__(self, source, target):
       
   345         "Initializes on a source and target SpatialReference objects."
       
   346         self._ptr = None # Initially NULL 
       
   347         if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference):
       
   348             raise SRSException('source and target must be of type SpatialReference')
       
   349         self._ptr = new_ct(source._ptr, target._ptr)
       
   350         if not self._ptr:
       
   351             raise SRSException('could not intialize CoordTransform object')
       
   352         self._srs1_name = source.name
       
   353         self._srs2_name = target.name
       
   354 
       
   355     def __del__(self):
       
   356         "Deletes this Coordinate Transformation object."
       
   357         if self._ptr: destroy_ct(self._ptr)
       
   358 
       
   359     def __str__(self):
       
   360         return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)