osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/SpatialReference.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00020 #include <osgEarth/SpatialReference>
00021 #include <osgEarth/Registry>
00022 #include <osgEarth/Cube>
00023 #include <osgEarth/LocalTangentPlane>
00024 #include <OpenThreads/ScopedLock>
00025 #include <osg/Notify>
00026 #include <ogr_api.h>
00027 #include <ogr_spatialref.h>
00028 #include <algorithm>
00029 
00030 #define LC "[SpatialReference] "
00031 
00032 using namespace osgEarth;
00033 
00034 #define USE_CUSTOM_MERCATOR_TRANSFORM 1
00035 //#undef USE_CUSTOM_MERCATOR_TRANSFORM
00036 
00037 //------------------------------------------------------------------------
00038 
00039 namespace
00040 {
00041     std::string
00042     getOGRAttrValue( void* _handle, const std::string& name, int child_num, bool lowercase =false)
00043     {
00044         GDAL_SCOPED_LOCK;
00045             const char* val = OSRGetAttrValue( _handle, name.c_str(), child_num );
00046         if ( val )
00047         {
00048             std::string t = val;
00049             if ( lowercase )
00050             {
00051                 std::transform( t.begin(), t.end(), t.begin(), ::tolower );
00052             }
00053             return t;
00054         }
00055         return "";
00056     }
00057 
00058     std::string&
00059     replaceIn( std::string& s, const std::string& sub, const std::string& other)
00060     {
00061         if ( sub.empty() ) return s;
00062         size_t b=0;
00063         for( ; ; )
00064         {
00065             b = s.find( sub, b );
00066             if ( b == s.npos ) break;
00067             s.replace( b, sub.size(), other );
00068             b += other.size();
00069         }
00070         return s;
00071     }
00072 }
00073 
00074 //------------------------------------------------------------------------
00075 
00076 SpatialReference::SpatialReferenceCache& SpatialReference::getSpatialReferenceCache()
00077 {
00078     //Make sure the registry is created before the cache
00079     osgEarth::Registry::instance();
00080     static SpatialReferenceCache s_cache;
00081     return s_cache;
00082 }
00083 
00084 
00085 SpatialReference*
00086 SpatialReference::createFromPROJ4( const std::string& init, const std::string& init_alias, const std::string& name )
00087 {
00088     SpatialReference* result = NULL;
00089     GDAL_SCOPED_LOCK;
00090         void* handle = OSRNewSpatialReference( NULL );
00091     if ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE )
00092         {
00093         result = new SpatialReference( handle, "PROJ4", init_alias, name );
00094         }
00095         else 
00096         {
00097         OE_WARN << LC << "Unable to create spatial reference from PROJ4: " << init << std::endl;
00098                 OSRDestroySpatialReference( handle );
00099         }
00100     return result;
00101 }
00102 
00103 SpatialReference*
00104 SpatialReference::createCube()
00105 {
00106     // root the cube srs with a WGS84 intermediate ellipsoid.
00107     std::string init = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
00108 
00109     SpatialReference* result = NULL;
00110     GDAL_SCOPED_LOCK;
00111         void* handle = OSRNewSpatialReference( NULL );
00112     if ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE )
00113         {
00114         result = new CubeSpatialReference( handle );
00115         }
00116         else 
00117         {
00118         OE_WARN << LC << "Unable to create SRS: " << init << std::endl;
00119                 OSRDestroySpatialReference( handle );
00120         }
00121     return result;
00122 }
00123 
00124 #if 0
00125 SpatialReference*
00126 SpatialReference::createLTP( const osg::Vec3d& refPointLLA, const SpatialReference* geoSRS )
00127 {
00128     GDAL_SCOPED_LOCK;
00129 
00130     void* handle = OSRNewSpatialReference( NULL );
00131     bool ok = false;
00132     SpatialReference* result = 0L;
00133 
00134     if ( geoSRS )
00135     {
00136         std::string init = geoSRS->getGeographicSRS()->getWKT();
00137         char buf[4096];
00138         char* buf_ptr = &buf[0];
00139             strcpy( buf, init.c_str() );
00140         ok = ( OSRImportFromWkt( handle, &buf_ptr ) == OGRERR_NONE );
00141     }
00142     else 
00143     {
00144         std::string init = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
00145         ok = ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE );
00146     }
00147 
00148     if ( ok )
00149     {
00150         result = new LTPSpatialReference( handle, refPointLLA );
00151     }
00152     else
00153     {
00154         OE_WARN << LC << "Unable to create LTP SRS" << std::endl;
00155         if ( handle )
00156                     OSRDestroySpatialReference( handle );
00157     }
00158 
00159     return result;
00160 }
00161 #endif
00162 
00163 SpatialReference*
00164 SpatialReference::createFromWKT( const std::string& init, const std::string& init_alias, const std::string& name )
00165 {
00166     osg::ref_ptr<SpatialReference> result;
00167     GDAL_SCOPED_LOCK;
00168         void* handle = OSRNewSpatialReference( NULL );
00169     char buf[4096];
00170     char* buf_ptr = &buf[0];
00171         strcpy( buf, init.c_str() );
00172         if ( OSRImportFromWkt( handle, &buf_ptr ) == OGRERR_NONE )
00173         {
00174         result = new SpatialReference( handle, "WKT", init_alias, name );
00175         result = result->validate();
00176         }
00177         else 
00178         {
00179                 OE_WARN << LC << "Unable to create spatial reference from WKT: " << init << std::endl;
00180                 OSRDestroySpatialReference( handle );
00181         }
00182     return result.release();
00183 }
00184 
00185 SpatialReference*
00186 SpatialReference::create( const std::string& init )
00187 {
00188     static OpenThreads::Mutex s_mutex;
00189     OpenThreads::ScopedLock<OpenThreads::Mutex> exclusiveLock(s_mutex);
00190 
00191     std::string low = init;
00192     std::transform( low.begin(), low.end(), low.begin(), ::tolower );
00193 
00194     SpatialReferenceCache::iterator itr = getSpatialReferenceCache().find(init);
00195     if (itr != getSpatialReferenceCache().end())
00196     {
00197         //OE_NOTICE << "Returning cached SRS" << std::endl;
00198         return itr->second.get();
00199     }
00200 
00201     osg::ref_ptr<SpatialReference> srs;
00202 
00203     // shortcut for spherical-mercator:
00204     if (low == "spherical-mercator" || low == "epsg:900913" || low == "epsg:3785" ||
00205         low == "epsg:41001" || low == "epsg:102113" || low == "epsg:102100")
00206     {
00207         // note the use of nadgrids=@null (see http://proj.maptools.org/faq.html)
00208         // adjusted +a by ONE to work around osg manipulator error until we can figure out why.. GW
00209         srs = createFromPROJ4(
00210             "+proj=merc +a=6378137 +b=6378137 +lon_0=0 +k=1 +x_0=0 +y_0=0 +nadgrids=@null +units=m +no_defs",
00211             init,
00212             "Spherical Mercator" );
00213     }
00214 
00215     // ellipsoidal ("world") mercator:
00216     else if (low == "epsg:54004" || low == "epsg:9804" || low == "epsg:3832")
00217     {
00218         srs = createFromPROJ4(
00219             "+proj=merc +lon_0=0 +k=1 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs",
00220             init,
00221             "World Mercator" );
00222     }
00223 
00224     // common WGS84:
00225     else if (low == "epsg:4326" || low == "wgs84")
00226     {
00227         srs = createFromPROJ4(
00228             "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs",
00229             init,
00230             "WGS84" );
00231     }
00232 
00233     // custom srs for the unified cube
00234     else if ( low == "unified-cube" )
00235     {
00236         srs = createCube();
00237     }
00238 
00239     else if ( low.find( "+" ) == 0 )
00240     {
00241         srs = createFromPROJ4( low, init );
00242     }
00243     else if ( low.find( "epsg:" ) == 0 || low.find( "osgeo:" ) == 0 )
00244     {
00245         srs = createFromPROJ4( std::string("+init=") + low, init );
00246     }
00247     else if ( low.find( "projcs" ) == 0 || low.find( "geogcs" ) == 0 )
00248     {
00249         srs = createFromWKT( init, init );
00250     }
00251     else
00252     {
00253         return NULL;
00254     }
00255 
00256     getSpatialReferenceCache()[init] = srs;
00257     return srs.get();
00258 }
00259 
00260 
00261 SpatialReference*
00262 SpatialReference::create( osg::CoordinateSystemNode* csn )
00263 {
00264     SpatialReference* result = NULL;
00265     if ( csn && !csn->getCoordinateSystem().empty() )
00266     {
00267         result = create( csn->getCoordinateSystem() );
00268     }
00269     return result;
00270 }
00271 
00272 
00273 SpatialReference*
00274 SpatialReference::createFromHandle( void* ogrHandle, bool xferOwnership )
00275 {
00276     SpatialReference* srs = new SpatialReference( ogrHandle, xferOwnership );
00277     return srs;
00278 }
00279 
00280 SpatialReference*
00281 SpatialReference::validate()
00282 {
00283     std::string proj = getOGRAttrValue( _handle, "PROJECTION", 0 );
00284 
00285     // fix invalid ESRI LCC projections:
00286     if ( proj == "Lambert_Conformal_Conic" )
00287     {
00288         bool has_2_sps =
00289             !getOGRAttrValue( _handle, "Standard_Parallel_2", 0 ).empty() ||
00290             !getOGRAttrValue( _handle, "standard_parallel_2", 0 ).empty();
00291 
00292         std::string new_wkt = getWKT();
00293         if ( has_2_sps )
00294             replaceIn( new_wkt, "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_2SP" );
00295         else
00296             replaceIn( new_wkt, "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_1SP" );
00297 
00298         OE_INFO << LC << "Morphing Lambert_Conformal_Conic to 1SP/2SP" << std::endl;
00299         
00300         return createFromWKT( new_wkt, _init_str, _name );
00301     }
00302 
00303     // fixes for ESRI Plate_Carree and Equidistant_Cylindrical projections:
00304     else if ( proj == "Plate_Carree" )
00305     {
00306         std::string new_wkt = getWKT();
00307         replaceIn( new_wkt, "Plate_Carree", "Equirectangular" );
00308         OE_INFO << LC << "Morphing Plate_Carree to Equirectangular" << std::endl;
00309         return createFromWKT( new_wkt, _init_str, _name ); //, input->getReferenceFrame() );
00310     }
00311     else if ( proj == "Equidistant_Cylindrical" )
00312     {
00313         std::string new_wkt = getWKT();
00314         OE_INFO << LC << "Morphing Equidistant_Cylindrical to Equirectangular" << std::endl;
00315         replaceIn( new_wkt, "Equidistant_Cylindrical", "Equirectangular" );
00316         return createFromWKT( new_wkt, _init_str, _name );
00317     }
00318 
00319     // no changes.
00320     return this;
00321 }
00322 
00323 
00324 /****************************************************************************/
00325 
00326 
00327 SpatialReference::SpatialReference(void* handle, 
00328                                    const std::string& init_type,
00329                                    const std::string& init_str,
00330                                    const std::string& name ) :
00331 osg::Referenced( true ),
00332 _initialized( false ),
00333 _handle( handle ),
00334 _owns_handle( true ),
00335 _name( name ),
00336 _init_type( init_type ),
00337 _init_str( init_str ),
00338 _is_geographic( false ),
00339 _is_mercator( false ),
00340 _is_north_polar( false ), 
00341 _is_south_polar( false ),
00342 _is_cube( false ),
00343 _is_contiguous( false ),
00344 _is_user_defined( false ),
00345 _is_ltp( false )
00346 {
00347     _init_str_lc = init_str;
00348     std::transform( _init_str_lc.begin(), _init_str_lc.end(), _init_str_lc.begin(), ::tolower );
00349 }
00350 
00351 SpatialReference::SpatialReference(void* handle, bool ownsHandle) :
00352 osg::Referenced( true ),
00353 _initialized( false ),
00354 _handle( handle ),
00355 _owns_handle( ownsHandle )
00356 {
00357     //nop
00358 }
00359 
00360 SpatialReference::~SpatialReference()
00361 {
00362     if ( _handle )
00363     {
00364         GDAL_SCOPED_LOCK;
00365 
00366         for (TransformHandleCache::iterator itr = _transformHandleCache.begin(); itr != _transformHandleCache.end(); ++itr)
00367         {
00368             OCTDestroyCoordinateTransformation(itr->second);
00369         }
00370 
00371         if ( _owns_handle )
00372         {
00373             OSRDestroySpatialReference( _handle );
00374         }
00375 
00376         _handle = NULL;
00377     }
00378 }
00379 
00380 bool
00381 SpatialReference::isGeographic() const 
00382 {
00383     if ( !_initialized )
00384         const_cast<SpatialReference*>(this)->init();
00385     return _is_geographic;
00386 }
00387 
00388 bool
00389 SpatialReference::isProjected() const
00390 {
00391     if ( !_initialized )
00392         const_cast<SpatialReference*>(this)->init();
00393     return !_is_geographic;
00394 }
00395 
00396 const std::string&
00397 SpatialReference::getName() const
00398 {
00399     if ( !_initialized )
00400         const_cast<SpatialReference*>(this)->init();
00401     return _name;
00402 }
00403 
00404 const osg::EllipsoidModel*
00405 SpatialReference::getEllipsoid() const
00406 {
00407     if ( !_initialized )
00408         const_cast<SpatialReference*>(this)->init();
00409     return _ellipsoid.get();
00410 }
00411 
00412 const std::string&
00413 SpatialReference::getDatumName() const
00414 {
00415     if ( !_initialized )
00416         const_cast<SpatialReference*>(this)->init();
00417     return _datum;
00418 }
00419 
00420 const std::string&
00421 SpatialReference::getWKT() const 
00422 {
00423     if ( !_initialized )
00424         const_cast<SpatialReference*>(this)->init();
00425     return _wkt;
00426 }
00427 
00428 const std::string&
00429 SpatialReference::getInitString() const
00430 {
00431     if ( !_initialized )
00432         const_cast<SpatialReference*>(this)->init();
00433     return _init_str;
00434 }
00435 
00436 const std::string&
00437 SpatialReference::getInitType() const
00438 {
00439     if ( !_initialized )
00440         const_cast<SpatialReference*>(this)->init();
00441     return _init_type;
00442 }
00443 
00444 bool
00445 SpatialReference::isEquivalentTo( const SpatialReference* rhs ) const
00446 {
00447     if ( !_initialized )
00448         const_cast<SpatialReference*>(this)->init();
00449 
00450     return _isEquivalentTo( rhs );
00451 }
00452 
00453 bool
00454 SpatialReference::_isEquivalentTo( const SpatialReference* rhs ) const
00455 {
00456     if ( !rhs )
00457         return false;
00458 
00459     if ( this == rhs )
00460         return true;
00461 
00462     if (isGeographic()  != rhs->isGeographic()  ||
00463         isMercator()    != rhs->isMercator()    ||
00464         isNorthPolar()  != rhs->isNorthPolar()  ||
00465         isSouthPolar()  != rhs->isSouthPolar()  ||
00466         isContiguous()  != rhs->isContiguous()  ||
00467         isUserDefined() != rhs->isUserDefined() ||
00468         isCube()        != rhs->isCube()        ||
00469         isLTP()         != rhs->isLTP() )
00470     {
00471         return false;
00472     }
00473 
00474     if ( _init_str_lc == rhs->_init_str_lc )
00475         return true;
00476 
00477     if ( this->getWKT() == rhs->getWKT() )
00478         return true;
00479 
00480     if (this->isGeographic() && rhs->isGeographic() &&
00481         this->getEllipsoid()->getRadiusEquator() == rhs->getEllipsoid()->getRadiusEquator() &&
00482         this->getEllipsoid()->getRadiusPolar() == rhs->getEllipsoid()->getRadiusPolar())
00483     {
00484         return true;
00485     }
00486 
00487     // last resort, since it requires the lock
00488     GDAL_SCOPED_LOCK;
00489     return TRUE == ::OSRIsSame( _handle, rhs->_handle );
00490 }
00491 
00492 const SpatialReference*
00493 SpatialReference::getGeographicSRS() const
00494 {
00495     if ( !_initialized )
00496         const_cast<SpatialReference*>(this)->init();
00497 
00498     if ( _is_geographic )
00499         return this;
00500 
00501     if ( !_geo_srs.valid() )
00502     {
00503         GDAL_SCOPED_LOCK;
00504 
00505         if ( !_geo_srs.valid() ) // double-check pattern
00506         {
00507             void* new_handle = OSRNewSpatialReference( NULL );
00508             int err = OSRCopyGeogCSFrom( new_handle, _handle );
00509             if ( err == OGRERR_NONE )
00510             {
00511                 const_cast<SpatialReference*>(this)->_geo_srs = new SpatialReference( new_handle );
00512             }
00513             else
00514             {
00515                 OSRDestroySpatialReference( new_handle );
00516             }
00517         }
00518     }
00519 
00520     return _geo_srs.get();
00521 }
00522 
00523 SpatialReference*
00524 SpatialReference::createTangentPlaneSRS( const osg::Vec3d& pos ) const
00525 {
00526     SpatialReference* result = 0L;
00527     osg::Vec3d lla;
00528     if ( this->transform(pos, this->getGeographicSRS(), lla) )
00529     {
00530         result = new LTPSpatialReference( this->getGeographicSRS()->_handle, lla );
00531     }
00532     else
00533     {
00534         OE_WARN << LC << "Unable to create LTP SRS" << std::endl;
00535     }
00536     return result;
00537 }
00538 
00539 SpatialReference*
00540 SpatialReference::createTransMercFromLongitude( const Angular& lon ) const
00541 {
00542     // note. using tmerc with +lat_0 <> 0 is sloooooow.
00543     std::string datum = getDatumName();
00544     std::stringstream buf;
00545     buf << "+proj=tmerc +lat_0=0"
00546         << " +lon_0=" << lon.as(Units::DEGREES)
00547         << " +datum=" << (!datum.empty() ? "wgs84" : datum);
00548     std::string projstr;
00549     projstr = buf.str();
00550     return create( projstr );
00551 }
00552 
00553 SpatialReference*
00554 SpatialReference::createUTMFromLongitude( const Angular& lon ) const
00555 {
00556     // note. UTM is up to 10% faster than TMERC for the same meridian.
00557     unsigned zone = 1 + (unsigned)floor((lon.as(Units::DEGREES)+180.0)/6.0);
00558     std::string datum = getDatumName();
00559     std::stringstream buf;
00560     buf << "+proj=utm +zone=" << zone
00561         << " +datum=" << (!datum.empty() ? "wgs84" : datum);
00562     std::string projstr;
00563     projstr = buf.str();
00564     return create( projstr );
00565 }
00566 
00567 bool
00568 SpatialReference::isMercator() const
00569 {
00570     if ( !_initialized )
00571         const_cast<SpatialReference*>(this)->init();
00572     return _is_mercator;
00573 }
00574 
00575 bool 
00576 SpatialReference::isNorthPolar() const
00577 {
00578     if ( !_initialized )
00579         const_cast<SpatialReference*>(this)->init();
00580     return _is_north_polar;
00581 }
00582 
00583 bool 
00584 SpatialReference::isSouthPolar() const
00585 {
00586     if ( !_initialized )
00587         const_cast<SpatialReference*>(this)->init();
00588     return _is_south_polar;
00589 }
00590 
00591 bool
00592 SpatialReference::isContiguous() const
00593 {
00594     if ( !_initialized )
00595         const_cast<SpatialReference*>(this)->init();
00596     return _is_contiguous;
00597 }
00598 
00599 bool
00600 SpatialReference::isUserDefined() const
00601 {
00602     if ( !_initialized )
00603         const_cast<SpatialReference*>(this)->init();
00604     return _is_user_defined;
00605 }
00606 
00607 bool
00608 SpatialReference::isCube() const
00609 {
00610     if ( !_initialized )
00611         const_cast<SpatialReference*>(this)->init();
00612     return _is_cube;
00613 }
00614 
00615 osg::CoordinateSystemNode*
00616 SpatialReference::createCoordinateSystemNode() const
00617 {
00618     if ( !_initialized )
00619         const_cast<SpatialReference*>(this)->init();
00620 
00621     osg::CoordinateSystemNode* csn = new osg::CoordinateSystemNode();
00622     populateCoordinateSystemNode( csn );
00623     return csn;
00624 }
00625 
00626 bool
00627 SpatialReference::populateCoordinateSystemNode( osg::CoordinateSystemNode* csn ) const
00628 {
00629     if ( !csn )
00630         return false;
00631 
00632     if ( !_initialized )
00633         const_cast<SpatialReference*>(this)->init();
00634 
00635     if ( !_wkt.empty() )
00636     {
00637         csn->setFormat( "WKT" );
00638         csn->setCoordinateSystem( _wkt );
00639     }
00640     else if ( !_proj4.empty() )
00641     {
00642         csn->setFormat( "PROJ4" );
00643         csn->setCoordinateSystem( _proj4 );
00644     }
00645     else
00646     {
00647         csn->setFormat( _init_type );
00648         csn->setCoordinateSystem( _init_str );
00649     }
00650     
00651     csn->setEllipsoidModel( _ellipsoid.get() );
00652     
00653     return true;
00654 }
00655 
00656 // Make a MatrixTransform suitable for use with a Locator object based on the given extents.
00657 // Calling Locator::setTransformAsExtents doesn't work with OSG 2.6 due to the fact that the
00658 // _inverse member isn't updated properly.  Calling Locator::setTransform works correctly.
00659 static osg::Matrixd
00660 getTransformFromExtents(double minX, double minY, double maxX, double maxY)
00661 {
00662     osg::Matrixd transform;
00663     transform.set(
00664         maxX-minX, 0.0,       0.0, 0.0,
00665         0.0,       maxY-minY, 0.0, 0.0,
00666         0.0,       0.0,       1.0, 0.0,
00667         minX,      minY,      0.0, 1.0); 
00668     return transform;
00669 }
00670 
00671 GeoLocator*
00672 SpatialReference::createLocator(double xmin, double ymin, double xmax, double ymax,
00673                                 bool plate_carre ) const
00674 {
00675     if ( !_initialized )
00676         const_cast<SpatialReference*>(this)->init();
00677 
00678     GeoLocator* locator = new GeoLocator( GeoExtent(this, xmin, ymin, xmax, ymax) );
00679     locator->setEllipsoidModel( (osg::EllipsoidModel*)getEllipsoid() );
00680     locator->setCoordinateSystemType( isGeographic()? osgTerrain::Locator::GEOGRAPHIC : osgTerrain::Locator::PROJECTED );
00681     // note: not setting the format/cs on purpose.
00682 
00683     if ( isGeographic() && !plate_carre )
00684     {
00685         locator->setTransform( getTransformFromExtents(
00686             osg::DegreesToRadians( xmin ),
00687             osg::DegreesToRadians( ymin ),
00688             osg::DegreesToRadians( xmax ),
00689             osg::DegreesToRadians( ymax ) ) );
00690     }
00691     else
00692     {
00693         locator->setTransform( getTransformFromExtents( xmin, ymin, xmax, ymax ) );
00694     }
00695     return locator;
00696 }
00697 
00698 bool
00699 SpatialReference::transform(double x, double y, double z,
00700                             const SpatialReference* out_srs, 
00701                             double& out_x, double& out_y, double& out_z,
00702                             void* context ) const
00703 {        
00704     if ( !_initialized )
00705         const_cast<SpatialReference*>(this)->init();
00706 
00707     //Check for equivalence and return if the coordinate systems are the same.
00708     if (isEquivalentTo(out_srs))
00709     {
00710         out_x = x;
00711         out_y = y;
00712         out_z = z;
00713         return true;
00714     }
00715 
00716     out_x = x;
00717     out_y = y;
00718     out_z = z;
00719     bool result = transformPoints(out_srs, &out_x, &out_y, &out_z, 1, context);
00720 
00721     return result;
00722 }
00723 
00724 // http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection
00725 static bool
00726 mercatorToGeographic( double* x, double* y, double* z, int numPoints )
00727 {
00728     for( int i=0; i<numPoints; i++ )
00729     {
00730         double xr = -osg::PI + ((x[i]-MERC_MINX)/MERC_WIDTH)*2.0*osg::PI;
00731         double yr = -osg::PI + ((y[i]-MERC_MINY)/MERC_HEIGHT)*2.0*osg::PI;
00732         x[i] = osg::RadiansToDegrees( xr );
00733         y[i] = osg::RadiansToDegrees( 2.0 * atan( exp(yr) ) - osg::PI_2 );
00734         // z doesn't change
00735     }
00736     return true;
00737 }
00738 
00739 // http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection
00740 static bool
00741 geographicToMercator( double* x, double* y, double* z, int numPoints )
00742 {
00743     for( int i=0; i<numPoints; i++ )
00744     {
00745         double xr = (osg::DegreesToRadians(x[i]) - (-osg::PI)) / (2.0*osg::PI);
00746         double sinLat = sin(osg::DegreesToRadians(y[i]));
00747         double oneMinusSinLat = 1-sinLat;
00748         if ( oneMinusSinLat != 0.0 )
00749         {
00750             double yr = ((0.5 * log( (1+sinLat)/oneMinusSinLat )) - (-osg::PI)) / (2.0*osg::PI);
00751             x[i] = MERC_MINX + (xr * MERC_WIDTH);
00752             y[i] = MERC_MINY + (yr * MERC_HEIGHT);
00753             // z doesn't change
00754         }
00755     }
00756     return true;
00757 }
00758 
00759 bool
00760 SpatialReference::transformPoints(const SpatialReference* out_srs,
00761                                   double* x, double* y, double* z,
00762                                   unsigned int numPoints,
00763                                   void* context,
00764                                   bool ignore_errors ) const
00765 {
00766     if ( !_initialized )
00767         const_cast<SpatialReference*>(this)->init();
00768 
00769     //Check for equivalence and return if the coordinate systems are the same.
00770     if (isEquivalentTo(out_srs))
00771         return true;
00772 
00773     if ( z )
00774     {
00775         for (unsigned int i = 0; i < numPoints; ++i)
00776             preTransform(x[i], y[i], z[i], context);
00777     }
00778     else
00779     {
00780         double dummyZ = 0.0;
00781         for (unsigned int i = 0; i < numPoints; ++i)
00782             preTransform(x[i], y[i], dummyZ, context);
00783     }
00784     
00785     bool success = false;
00786 
00787 #ifdef USE_CUSTOM_MERCATOR_TRANSFORM
00788 
00789     if ( isGeographic() && out_srs->isMercator() )
00790     {
00791         success = geographicToMercator( x, y, z, numPoints );
00792     }
00793 
00794     else if ( isMercator() && out_srs->isGeographic() )
00795     {
00796         success = mercatorToGeographic( x, y, z, numPoints );
00797     }
00798 
00799     else
00800 #endif
00801 
00802     {    
00803         GDAL_SCOPED_LOCK;
00804 
00805         void* xform_handle = NULL;
00806         TransformHandleCache::const_iterator itr = _transformHandleCache.find(out_srs->getWKT());
00807         if (itr != _transformHandleCache.end())
00808         {
00809             //OE_DEBUG << "SpatialRefernece: using cached transform handle" << std::endl;
00810             xform_handle = itr->second;
00811         }
00812         else
00813         {
00814             xform_handle = OCTNewCoordinateTransformation( _handle, out_srs->_handle);
00815             const_cast<SpatialReference*>(this)->_transformHandleCache[out_srs->getWKT()] = xform_handle;
00816         }
00817 
00818         if ( !xform_handle )
00819         {
00820             OE_WARN << LC
00821                 << "SRS xform not possible" << std::endl
00822                 << "    From => " << getName() << std::endl
00823                 << "    To   => " << out_srs->getName() << std::endl;
00824             return false;
00825         }
00826 
00827         //double* temp_z = new double[numPoints];
00828         //success = OCTTransform( xform_handle, numPoints, x, y, temp_z ) > 0;
00829         //delete[] temp_z;
00830         
00831         success = OCTTransform( xform_handle, numPoints, x, y, z ) > 0;
00832 
00833         // END GDAL_SCOPE_LOCK
00834     }
00835 
00836     if ( success || ignore_errors )
00837     {
00838         if ( z )
00839         {
00840             for (unsigned int i = 0; i < numPoints; ++i)
00841                 out_srs->postTransform(x[i], y[i], z[i], context);
00842         }
00843         else
00844         {
00845             double dummyZ = 0.0;
00846             for (unsigned int i = 0; i < numPoints; ++i)
00847                 out_srs->postTransform(x[i], y[i], dummyZ, context);
00848         }
00849     }
00850     else
00851     {
00852         OE_WARN << LC << "Failed to xform a point from "
00853             << getName() << " to " << out_srs->getName()
00854             << std::endl;
00855     }
00856     return success;
00857 }
00858 
00859 bool
00860 SpatialReference::transformPoints(const SpatialReference* out_srs,
00861                                   std::vector<osg::Vec3d>& points,
00862                                   void* context,
00863                                   bool ignore_errors ) const
00864 {
00865     if ( !_initialized )
00866         const_cast<SpatialReference*>(this)->init();
00867 
00868     //Check for equivalence and return if the coordinate systems are the same.
00869     if (isEquivalentTo(out_srs)) 
00870         return true;
00871 
00872     int numPoints = points.size();
00873     double* x = new double[numPoints];
00874     double* y = new double[numPoints];
00875     double* z = new double[numPoints];
00876 
00877     for( int i=0; i<numPoints; i++ )
00878     {
00879         x[i] = points[i].x();
00880         y[i] = points[i].y();
00881         z[i] = points[i].z();
00882     }
00883 
00884     bool success = transformPoints( out_srs, x, y, z, numPoints, context, ignore_errors );
00885 
00886     if ( success )
00887     {
00888         for( int i=0; i<numPoints; i++ )
00889         {
00890             points[i].x() = x[i];
00891             points[i].y() = y[i];
00892             points[i].z() = z[i];
00893         }
00894     }
00895 
00896     delete[] x;
00897     delete[] y;
00898     delete[] z;
00899 
00900     return success;
00901 }
00902 
00903 bool 
00904 SpatialReference::transformToECEF(const osg::Vec3d& input,
00905                                   osg::Vec3d&       output ) const
00906 {
00907     double lat = input.y(), lon = input.x(), alt = input.z();
00908     
00909     // first convert to lat/long if necessary:
00910     if ( !isGeographic() )
00911         transform( input.x(), input.y(), input.z(), getGeographicSRS(), lon, lat, alt );
00912 
00913     // then convert to ECEF.
00914     //double z = input.z();
00915     getGeographicSRS()->getEllipsoid()->convertLatLongHeightToXYZ(
00916         osg::DegreesToRadians( lat ), osg::DegreesToRadians( lon ), alt,
00917         output.x(), output.y(), output.z() );
00918 
00919     return true;
00920 }
00921 
00922 bool 
00923 SpatialReference::transformToECEF(std::vector<osg::Vec3d>& points,
00924                                   bool                     ignoreErrors ) const
00925 {
00926     if ( points.size() == 0 )
00927         return false;
00928 
00929     const SpatialReference*    geoSRS    = getGeographicSRS();
00930     const osg::EllipsoidModel* ellipsoid = geoSRS->getEllipsoid();
00931 
00932     for( unsigned i=0; i<points.size(); ++i )
00933     {
00934         osg::Vec3d& p = points[i];
00935 
00936         if ( !isGeographic() )
00937             transform( p.x(), p.y(), p.z(), geoSRS, p.x(), p.y(), p.z() );
00938 
00939         ellipsoid->convertLatLongHeightToXYZ(
00940             osg::DegreesToRadians( p.y() ), osg::DegreesToRadians( p.x() ), p.z(),
00941             p.x(), p.y(), p.z() );
00942     }
00943 
00944     return true;
00945 }
00946 
00947 bool 
00948 SpatialReference::transformFromECEF(const osg::Vec3d& input,
00949                                     osg::Vec3d&       output ) const
00950 {
00951     // transform to lat/long:
00952     osg::Vec3d geo;
00953 
00954     getGeographicSRS()->getEllipsoid()->convertXYZToLatLongHeight(
00955         input.x(), input.y(), input.z(),
00956         geo.y(), geo.x(), geo.z() );
00957 
00958     // then convert to the local SRS.
00959     if ( isGeographic() )
00960     {
00961         output.set( osg::RadiansToDegrees(geo.x()), osg::RadiansToDegrees(geo.y()), geo.z() );
00962     }
00963     else
00964     {
00965         getGeographicSRS()->transform( 
00966             osg::RadiansToDegrees(geo.x()), osg::RadiansToDegrees(geo.y()), geo.z(),
00967             this,
00968             output.x(), output.y(), output.z() );
00969         //output.z() = geo.z();
00970     }
00971 
00972     return true;
00973 }
00974 
00975 bool 
00976 SpatialReference::transformFromECEF(std::vector<osg::Vec3d>& points,
00977                                     bool                     ignoreErrors ) const
00978 {
00979     bool ok = true;
00980 
00981     // first convert all the points to lat/long (in place):
00982     for( unsigned i=0; i<points.size(); ++i )
00983     {
00984         osg::Vec3d& p = points[i];
00985         osg::Vec3d geo;
00986         getGeographicSRS()->getEllipsoid()->convertXYZToLatLongHeight(
00987             p.x(), p.y(), p.z(),
00988             geo.y(), geo.x(), geo.z() );
00989         geo.x() = osg::RadiansToDegrees( geo.x() );
00990         geo.y() = osg::RadiansToDegrees( geo.y() );
00991         p = geo;
00992     }
00993 
00994     // then convert them all to the local SRS if necessary.
00995     if ( !isGeographic() )
00996     {
00997         ok = getGeographicSRS()->transformPoints( this, points, 0L, ignoreErrors );
00998     }
00999 
01000     return ok;
01001 }
01002 
01003 bool
01004 SpatialReference::transformExtent(const SpatialReference* to_srs,
01005                                   double&                 in_out_xmin,
01006                                   double&                 in_out_ymin,
01007                                   double&                 in_out_xmax,
01008                                   double&                 in_out_ymax,
01009                                   void*                   context ) const
01010 {
01011     if ( !_initialized )
01012         const_cast<SpatialReference*>(this)->init();
01013 
01014     int oks = 0;
01015 
01016     //Transform all points and take the maximum bounding rectangle the resulting points
01017     double llx, lly;
01018     double ulx, uly;
01019     double urx, ury;
01020     double lrx, lry;
01021     double dummyZ = 0;
01022 
01023     //Lower Left
01024     oks += transform( in_out_xmin, in_out_ymin, 0, to_srs, llx, lly, dummyZ, context ) == true;
01025 
01026     //Upper Left
01027     oks += transform( in_out_xmin, in_out_ymax, 0, to_srs, ulx, uly, dummyZ, context ) == true;
01028 
01029     //Upper Right
01030     oks += transform( in_out_xmax, in_out_ymax, 0, to_srs, urx, ury, dummyZ, context ) == true;
01031 
01032     //Lower Right
01033     oks += transform( in_out_xmax, in_out_ymin, 0, to_srs, lrx, lry, dummyZ, context ) == true;
01034 
01035 
01036     if (oks == 4)
01037     {
01038         in_out_xmin = osg::minimum(llx, ulx);
01039         in_out_xmax = osg::maximum(lrx, urx);
01040         in_out_ymin = osg::minimum(lly, lry);
01041         in_out_ymax = osg::maximum(uly, ury);
01042         return true;
01043     }
01044     return false;
01045 }
01046 
01047 bool SpatialReference::transformExtentPoints(const SpatialReference* to_srs,
01048                                              double in_xmin, double in_ymin,
01049                                              double in_xmax, double in_ymax,
01050                                              double* x, double* y,
01051                                              unsigned int numx, unsigned int numy,
01052                                              void* context, bool ignore_errors ) const
01053 {
01054     const double dx = (in_xmax - in_xmin) / (numx - 1);
01055     const double dy = (in_ymax - in_ymin) / (numy - 1);
01056 
01057     unsigned int pixel = 0;
01058     double fc = 0.0;
01059     for (unsigned int c = 0; c < numx; ++c, ++fc)
01060     {
01061         const double dest_x = in_xmin + fc * dx;
01062         double fr = 0.0;
01063         for (unsigned int r = 0; r < numy; ++r, ++fr)
01064         {
01065             const double dest_y = in_ymin + fr * dy;
01066 
01067             x[pixel] = dest_x;
01068             y[pixel] = dest_y;
01069             pixel++;     
01070         }
01071     }
01072     return transformPoints(to_srs, x, y, 0L, numx * numy, context, ignore_errors);
01073 }
01074 
01075 void
01076 SpatialReference::init()
01077 {
01078     GDAL_SCOPED_LOCK;
01079 
01080     // always double-check the _initialized flag after obtaining the lock.
01081     if ( !_initialized )
01082     {
01083         // calls the internal version, which can be overriden by the developer.
01084         // therefore do not call init() from the constructor!
01085         _init();
01086     }
01087 }
01088 
01089 void
01090 SpatialReference::_init()
01091 {
01092     // set defaults:
01093     _is_user_defined = false; 
01094     _is_contiguous = true;   
01095     _is_cube = false;
01096     _is_geographic = OSRIsGeographic( _handle ) != 0;
01097 
01098     // extract the ellipsoid parameters:
01099     int err;
01100     double semi_major_axis = OSRGetSemiMajor( _handle, &err );
01101     double semi_minor_axis = OSRGetSemiMinor( _handle, &err );
01102     _ellipsoid = new osg::EllipsoidModel( semi_major_axis, semi_minor_axis );
01103 
01104     // try to get an ellipsoid name:
01105     _ellipsoid->setName( getOGRAttrValue(_handle, "SPHEROID", 0, true) );
01106 
01107     // extract the projection:
01108     if ( _name.empty() || _name == "unnamed" )
01109     {
01110         _name = _is_geographic? 
01111             getOGRAttrValue( _handle, "GEOGCS", 0 ) : 
01112             getOGRAttrValue( _handle, "PROJCS", 0 );
01113     }
01114     std::string proj = getOGRAttrValue( _handle, "PROJECTION", 0, true );
01115 
01116     // check for the Mercator projection:
01117     _is_mercator = !proj.empty() && proj.find("mercator")==0;
01118 
01119     // check for the Polar projection:
01120     if ( !proj.empty() && proj.find("polar_stereographic") != std::string::npos )
01121     {
01122         double lat = as<double>( getOGRAttrValue( _handle, "latitude_of_origin", 0, true ), -90.0 );
01123         _is_north_polar = lat > 0.0;
01124         _is_south_polar = lat < 0.0;
01125     }
01126         else
01127         {
01128                 _is_north_polar = false;
01129                 _is_south_polar = false;
01130         }
01131 
01132     // Give the SRS a name if it doesn't have one:
01133     if ( _name == "unnamed" || _name.empty() )
01134     {
01135         _name =
01136             _is_geographic? "Geographic CS" :
01137             _is_mercator? "Mercator CS" :
01138             ( !proj.empty()? proj : "Projected CS" );
01139     }
01140 
01141     // Try to extract the OGC well-known-text (WKT) string:
01142     char* wktbuf;
01143     if ( OSRExportToWkt( _handle, &wktbuf ) == OGRERR_NONE )
01144     {
01145         _wkt = wktbuf;
01146         OGRFree( wktbuf );
01147     }
01148 
01149     // If the user did not specify and initialization string, use the WKT.
01150     if ( _init_str.empty() )
01151     {
01152         _init_str = _wkt;
01153         _init_type = "WKT";
01154     }
01155     
01156     // Try to extract the PROJ4 initialization string:
01157     char* proj4buf;
01158     if ( OSRExportToProj4( _handle, &proj4buf ) == OGRERR_NONE )
01159     {
01160         _proj4 = proj4buf;
01161         OGRFree( proj4buf );
01162     }
01163 
01164     // Try to extract the datum
01165     _datum = getOGRAttrValue( _handle, "DATUM", 0, true );
01166 
01167     _initialized = true;
01168 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines