osgEarth 2.1.1
|
00001 #include "MapService.h" 00002 #include <osgEarth/HTTPClient> 00003 #include <osgEarth/JsonUtils> 00004 #include <osgEarth/Registry> 00005 #include <osg/Notify> 00006 #include <sstream> 00007 #include <limits.h> 00008 00009 using namespace osgEarth; 00010 00011 00012 MapServiceLayer::MapServiceLayer(int in_id, 00013 const std::string& in_name) : 00014 id( in_id ), 00015 name( in_name ) 00016 { 00017 //NOP 00018 } 00019 00020 int 00021 MapServiceLayer::getId() const { 00022 return id; 00023 } 00024 00025 const std::string& 00026 MapServiceLayer::getName() const { 00027 return name; 00028 } 00029 00030 //=========================================================================== 00031 00032 TileInfo::TileInfo() 00033 : is_valid( false ) 00034 { 00035 //NOP 00036 } 00037 00038 TileInfo::TileInfo( int _tile_size, const std::string& _format, int _min_level, int _max_level, int _num_tiles_wide, int _num_tiles_high ) : 00039 format( _format ), 00040 tile_size( _tile_size ), 00041 min_level( _min_level ), 00042 max_level( _max_level ), 00043 is_valid( true ), 00044 num_tiles_wide(_num_tiles_wide), 00045 num_tiles_high(_num_tiles_high) 00046 { 00047 //NOP 00048 } 00049 00050 TileInfo::TileInfo( const TileInfo& rhs ) : 00051 format( rhs.format ), 00052 tile_size( rhs.tile_size ), 00053 min_level( rhs.min_level ), 00054 max_level( rhs.max_level ), 00055 is_valid( rhs.is_valid ), 00056 num_tiles_wide( rhs.num_tiles_wide ), 00057 num_tiles_high( rhs.num_tiles_high ) 00058 { 00059 //NOP 00060 } 00061 00062 bool 00063 TileInfo::isValid() const { 00064 return is_valid; 00065 } 00066 00067 int 00068 TileInfo::getTileSize() const { 00069 return tile_size; 00070 } 00071 00072 const std::string& 00073 TileInfo::getFormat() const { 00074 return format; 00075 } 00076 00077 int 00078 TileInfo::getMinLevel() const { 00079 return min_level; 00080 } 00081 00082 int 00083 TileInfo::getMaxLevel() const { 00084 return max_level; 00085 } 00086 00087 int 00088 TileInfo::getNumTilesWide() const { 00089 return num_tiles_wide; 00090 } 00091 00092 int 00093 TileInfo::getNumTilesHigh() const { 00094 return num_tiles_high; 00095 } 00096 00097 00098 //=========================================================================== 00099 00100 00101 MapService::MapService() : 00102 is_valid( false ), 00103 tiled( false ) 00104 { 00105 //NOP 00106 } 00107 00108 bool 00109 MapService::isValid() const { 00110 return is_valid; 00111 } 00112 00113 bool 00114 MapService::isTiled() const { 00115 return tiled; 00116 } 00117 00118 const Profile* 00119 MapService::getProfile() const { 00120 return profile.get(); 00121 } 00122 00123 const TileInfo& 00124 MapService::getTileInfo() const { 00125 return tile_info; 00126 } 00127 00128 bool 00129 MapService::init( const std::string& _url, const osgDB::ReaderWriter::Options* options ) 00130 { 00131 url = _url; 00132 std::string sep = url.find( "?" ) == std::string::npos ? "?" : "&"; 00133 std::string json_url = url + sep + std::string("f=pjson"); // request the data in JSON format 00134 00135 HTTPResponse response = HTTPClient::get( json_url, options ); 00136 if ( !response.isOK() ) 00137 return setError( "Unable to read metadata from ArcGIS service" ); 00138 00139 Json::Value doc; 00140 Json::Reader reader; 00141 if ( !reader.parse( response.getPartStream(0), doc ) ) 00142 return setError( "Unable to parse metadata; invalid JSON" ); 00143 00144 // Read the profile. We are using "fullExtent"; perhaps an option to use "initialExtent" instead? 00145 double xmin = doc["fullExtent"].get("xmin", 0).asDouble(); 00146 double ymin = doc["fullExtent"].get("ymin", 0).asDouble(); 00147 double xmax = doc["fullExtent"].get("xmax", 0).asDouble(); 00148 double ymax = doc["fullExtent"].get("ymax", 0).asDouble(); 00149 int srs = doc["fullExtent"].get("spatialReference", osgEarth::Json::Value::null).get("wkid", 0).asInt(); 00150 00151 //Assumes the SRS is going to be an EPSG code 00152 std::stringstream ss; 00153 ss << "epsg:" << srs; 00154 00155 if ( ! (xmax > xmin && ymax > ymin && srs != 0 ) ) 00156 { 00157 return setError( "Map service does not define a full extent" ); 00158 } 00159 00160 // Read the layers list 00161 Json::Value j_layers = doc["layers"]; 00162 if ( j_layers.empty() ) 00163 return setError( "Map service contains no layers" ); 00164 00165 for( unsigned int i=0; i<j_layers.size(); i++ ) 00166 { 00167 Json::Value layer = j_layers[i]; 00168 int id = i; // layer.get("id", -1).asInt(); 00169 std::string name = layer["name"].asString(); 00170 00171 if ( id >= 0 && !name.empty() ) 00172 { 00173 layers.push_back( MapServiceLayer( id, name ) ); 00174 } 00175 } 00176 00177 tiled = false; 00178 std::string format = "png"; 00179 int tile_rows = 256; 00180 int tile_cols = 256; 00181 int min_level = 25; 00182 int max_level = 0; 00183 int num_tiles_wide = 1; 00184 int num_tiles_high = 1; 00185 00186 // Read the tiling schema 00187 Json::Value j_tileinfo = doc["tileInfo"]; 00188 if ( !j_tileinfo.empty() ) 00189 { 00190 tiled = true; 00191 00192 // return setError( "Map service does not define a tiling schema" ); 00193 00194 // TODO: what do we do if the width <> height? 00195 tile_rows = j_tileinfo.get( "rows", 0 ).asInt(); 00196 tile_cols = j_tileinfo.get( "cols", 0 ).asInt(); 00197 if ( tile_rows <= 0 && tile_cols <= 0 ) 00198 return setError( "Map service tile size not specified" ); 00199 00200 format = j_tileinfo.get( "format", "" ).asString(); 00201 if ( format.empty() ) 00202 return setError( "Map service tile schema does not specify an image format" ); 00203 00204 Json::Value j_levels = j_tileinfo["lods"]; 00205 if ( j_levels.empty() ) 00206 return setError( "Map service tile schema contains no LODs" ); 00207 00208 min_level = INT_MAX; 00209 max_level = 0; 00210 for( unsigned int i=0; i<j_levels.size(); i++ ) 00211 { 00212 int level = j_levels[i].get( "level", -1 ).asInt(); 00213 if ( level >= 0 && level < min_level ) 00214 min_level = level; 00215 if ( level >= 0 && level > max_level ) 00216 max_level = level; 00217 } 00218 00219 if (j_levels.size() > 0) 00220 { 00221 int l = j_levels[0u].get("level", -1).asInt(); 00222 double res = j_levels[0u].get("resolution", 0.0).asDouble(); 00223 num_tiles_wide = (int)osg::round((xmax - xmin) / (res * tile_cols)); 00224 num_tiles_high = (int)osg::round((ymax - ymin) / (res * tile_cols)); 00225 00226 //In case the first level specified isn't level 0, compute the number of tiles at level 0 00227 for (int i = 0; i < l; i++) 00228 { 00229 num_tiles_wide /= 2; 00230 num_tiles_high /= 2; 00231 } 00232 00233 //profile.setNumTilesWideAtLod0(num_tiles_wide); 00234 //profile.setNumTilesHighAtLod0(num_tiles_high); 00235 } 00236 } 00237 00238 std::string ssStr; 00239 ssStr = ss.str(); 00240 00241 osg::ref_ptr< SpatialReference > spatialReference = SpatialReference::create( ssStr ); 00242 if (spatialReference->isGeographic()) 00243 { 00244 //If we have a geographic SRS, just use the geodetic profile 00245 profile = Registry::instance()->getGlobalGeodeticProfile(); 00246 } 00247 else if (spatialReference->isMercator()) 00248 { 00249 //If we have a mercator SRS, just use the mercator profile 00250 profile = Registry::instance()->getGlobalMercatorProfile(); 00251 } 00252 else 00253 { 00254 //It's not geodetic or mercator, so try to use the full extent 00255 profile = Profile::create( 00256 spatialReference.get(), 00257 xmin, ymin, xmax, ymax, 00258 NULL, 00259 num_tiles_wide, 00260 num_tiles_high); 00261 } 00262 00263 00264 00265 // now we're good. 00266 tile_info = TileInfo( tile_rows, format, min_level, max_level, num_tiles_wide, num_tiles_high); 00267 is_valid = true; 00268 return is_valid; 00269 } 00270 00271 bool 00272 MapService::setError( const std::string& msg ) { 00273 error_msg = msg; 00274 return false; 00275 } 00276 00277 const std::string& 00278 MapService::getError() const { 00279 return error_msg; 00280 }