osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.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 "MapService.h"
00021 #include "ArcGISOptions"
00022 
00023 #include <osgEarth/TileSource>
00024 #include <osgEarth/Registry>
00025 #include <osg/Notify>
00026 #include <osgDB/FileNameUtils>
00027 #include <osgDB/FileUtils>
00028 #include <osgDB/Registry>
00029 #include <osgDB/ReadFile>
00030 #include <osgDB/WriteFile>
00031 
00032 #include <sstream>
00033 #include <iomanip>
00034 #include <algorithm>
00035 
00036 using namespace osgEarth;
00037 using namespace osgEarth::Drivers;
00038 
00039 //#define PROPERTY_URL        "url"
00040 //#define PROPERTY_PROFILE    "profile"
00041 
00042 class ArcGISSource : public TileSource
00043 {
00044 public:
00045     ArcGISSource( const TileSourceOptions& options ) :
00046       TileSource( options ),
00047       _options( options ),
00048       _profileConf( ProfileOptions() )
00049     {
00050         //if ( options )
00051         //{
00052         //    const Config& conf = options->config();
00053 
00054         //    // this is the ArcGIS REST services URL for the map service,
00055         //    // e.g. http://server/ArcGIS/rest/services/Layer/MapServer
00056         //    _url = conf.value( PROPERTY_URL );
00057 
00058         //    // force a profile type
00059         //    // TODO? do we need this anymore? doesn't this happen with overrideprofile now?
00060         //    if ( conf.hasChild( PROPERTY_PROFILE ) )
00061         //        _profileConf = ProfileOptions( conf.child( PROPERTY_PROFILE ) );
00062         //}
00063 
00064         //TODO: allow single layers vs. "fused view"
00065         if ( _layer.empty() )
00066             _layer = "_alllayers"; // default to the AGS "fused view"
00067 
00068         //TODO: detect the format
00069         if ( _format.empty() )
00070             _format = "png";
00071 
00072         URI url = _options.url().value();
00073         //Add the token if necessary
00074         if (_options.token().isSet())
00075         {
00076             std::string token = _options.token().value();
00077             if (!token.empty())
00078             {
00079                 std::string sep = url.full().find( "?" ) == std::string::npos ? "?" : "&";
00080                 url = url.append( sep + "token=" + token );
00081             }
00082         }
00083 
00084         // read metadata from the server
00085         if ( !_map_service.init( url.full() ) ) //, getOptions()) )
00086         {
00087             OE_WARN << "[osgearth] [ArcGIS] map service initialization failed: "
00088                 << _map_service.getError() << std::endl;
00089         }
00090     }
00091 
00092     // override
00093     void initialize( const std::string& referenceURI, const Profile* overrideProfile)
00094     {
00095         const Profile* profile = NULL;
00096 
00097         if ( _profileConf.isSet() )
00098         {
00099             profile = Profile::create( _profileConf.get() );
00100         }
00101         else if (overrideProfile)
00102         {
00103             profile = overrideProfile;
00104         }
00105         //if ( !_profile_str.empty() )
00106         //{
00107         //    profile = Profile::create( _profile_str );
00108         //}
00109         else if ( _map_service.getProfile() )
00110         {
00111             profile = _map_service.getProfile();
00112 
00113             /*
00114             if ( !_map_service.isTiled() )
00115             {
00116                 // expand the profile's extents so they form a square.
00117                 // AGS will return an image of a different extent than requested if the pixel aspect
00118                 // ratio is not the same at the geoextent aspect ratio. By forcing a square full extent,
00119                 // we can always request square tiles.
00120 
00121                 const GeoExtent& oldEx = profile->getExtent();
00122                 if ( oldEx.width() > oldEx.height() )
00123                 {
00124                     double d = oldEx.width() - oldEx.height();
00125                     unsigned int tilesX, tilesY;
00126                     profile->getNumTiles( 0, tilesX, tilesY );
00127                     profile = Profile::create( profile->getSRS(), oldEx.xMin(), oldEx.yMin()-d/2, oldEx.xMax(), oldEx.yMax()+d/2, 0L, tilesX, tilesY );
00128                 }
00129                 else if ( oldEx.width() < oldEx.height() )
00130                 {
00131                     double d = oldEx.height() - oldEx.width();
00132                     unsigned int tilesX, tilesY;
00133                     profile->getNumTiles( 0, tilesX, tilesY );
00134                     profile = Profile::create( profile->getSRS(), oldEx.xMin()-d/2, oldEx.yMin(), oldEx.xMax()+d/2, oldEx.yMax(), 0L, tilesX, tilesY );    
00135                 }
00136             }
00137             */
00138         }        
00139         else
00140         {
00141             profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile();
00142         }
00143 
00144                 //Set the profile
00145                 setProfile( profile );
00146     }
00147 
00148     // override
00149     int getPixelsPerTile() const
00150     {
00151       return _map_service.getTileInfo().getTileSize();
00152     }
00153 
00154     // override
00155     osg::Image* createImage( const TileKey& key,
00156                              ProgressCallback* progress)
00157     {
00158         std::stringstream buf;
00159 
00160         int level = key.getLevelOfDetail();
00161 
00162         unsigned int tile_x, tile_y;
00163         key.getTileXY( tile_x, tile_y );
00164 
00165         std::string f = _map_service.getTileInfo().getFormat();
00166         std::transform( f.begin(), f.end(), f.begin(), tolower );
00167         if ( f.length() > 3 && f.substr( 0, 3 ) == "png" )
00168             f = "png";
00169 
00170         if ( _map_service.isTiled() )
00171         {
00172             buf << _options.url()->full() << "/tile"
00173                 << "/" << level
00174                 << "/" << tile_y
00175                 << "/" << tile_x << "." << f;
00176         }
00177         else
00178         {
00179             const GeoExtent& ex = key.getExtent();
00180 
00181             buf << std::setprecision(16)
00182                 << _options.url()->full() << "/export"
00183                 << "?bbox=" << ex.xMin() << "," << ex.yMin() << "," << ex.xMax() << "," << ex.yMax()
00184                 << "&format=" << f 
00185                 << "&size=256,256"
00186                 << "&transparent=true"
00187                 << "&f=image"
00188                 << "&" << "." << f;
00189         }
00190 
00191         //Add the token if necessary
00192         if (_options.token().isSet())
00193         {
00194             std::string token = _options.token().value();
00195             if (!token.empty())
00196             {
00197                 std::string sep = buf.str().find( "?" ) == std::string::npos ? "?" : "&";
00198                 buf << sep << "token=" << token;
00199             }
00200         }
00201 
00202         //OE_NOTICE << "Key = " << key->str() << ", URL = " << buf.str() << std::endl;
00203         //return osgDB::readImageFile( buf.str(), getOptions() );
00204         //return HTTPClient::readImageFile( buf.str(), getOptions(), progress );
00205         
00206         osg::ref_ptr<osg::Image> image;
00207                 std::string bufStr;
00208                 bufStr = buf.str();
00209         HTTPClient::readImageFile( bufStr, image, 0L, progress ); //getOptions(), progress );
00210         return image.release();
00211     }
00212 
00213     // override
00214     osg::HeightField* createHeightField( const TileKey& key,
00215                                          ProgressCallback* progress)
00216     {
00217         //TODO
00218         return NULL;
00219     }
00220 
00221     // override
00222     virtual std::string getExtension() const 
00223     {
00224         return _format;
00225     }
00226 
00227 private:
00228     const ArcGISOptions _options;
00229     optional<ProfileOptions> _profileConf;
00230     std::string _map;
00231     std::string _layer;
00232     std::string _format;
00233     MapService _map_service;
00234 };
00235 
00236 
00237 class ArcGISTileSourceFactory : public TileSourceDriver
00238 {
00239 public:
00240     ArcGISTileSourceFactory()
00241     {
00242         supportsExtension( "osgearth_arcgis", "ArcGIS Server" );
00243     }
00244 
00245     virtual const char* className()
00246     {
00247         return "ArcGIS Server REST ReaderWriter";
00248     }
00249 
00250     virtual ReadResult readObject(const std::string& file_name, const Options* options) const
00251     {
00252         if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name )))
00253             return ReadResult::FILE_NOT_HANDLED;
00254 
00255         return new ArcGISSource( getTileSourceOptions(options) );
00256     }
00257 };
00258 
00259 REGISTER_OSGPLUGIN(osgearth_arcgis, ArcGISTileSourceFactory)
00260 
00261 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines