osgEarth 2.1.1
|
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