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 "TileService" 00021 00022 #include <osgEarth/XmlUtils> 00023 #include <osgEarth/HTTPClient> 00024 00025 #include <osg/io_utils> 00026 #include <osgDB/FileNameUtils> 00027 #include <osgDB/FileUtils> 00028 00029 using namespace osgEarth; 00030 using namespace std; 00031 00032 00033 00034 std::string extractBetween(const std::string& str, const string &lhs, const string &rhs) 00035 { 00036 std::string result; 00037 string::size_type start = str.find(lhs); 00038 if (start != string::npos) 00039 { 00040 start += lhs.length(); 00041 string::size_type count = str.size() - start; 00042 string::size_type end = str.find(rhs, start); 00043 if (end != string::npos) count = end-start; 00044 result = str.substr(start, count); 00045 } 00046 return result; 00047 } 00048 00049 TilePattern::TilePattern(const std::string& pattern) 00050 { 00051 _pattern = pattern; 00052 init(); 00053 } 00054 00055 00056 00057 void TilePattern::init() 00058 { 00059 _dataMin.x() = -180; 00060 _dataMin.y() = -90; 00061 _dataMax.x() = 180; 00062 _dataMax.y() = 90; 00063 00064 //request=GetMap&layers=global_mosaic&srs=EPSG:4326&format=image/jpeg&styles=visual&width=512&height=512&bbox=-180,-166,76,90 00065 //Convert the filename to lower case 00066 std::string lower = osgDB::convertToLowerCase( _pattern ); 00067 00068 _layers = extractBetween(lower, "layers=", "&"); 00069 _styles = extractBetween(lower, "styles=", "&"); 00070 _srs = extractBetween(lower, "srs=", "&"); 00071 _format = extractBetween(lower, "format=image/", "&"); 00072 _imageWidth = as<int>(extractBetween(lower, "width=", "&"), 0); 00073 _imageHeight = as<int>(extractBetween(lower, "height=", "&"), 0); 00074 00075 //Read the coordinates of the top left tile 00076 std::string bbox = extractBetween(lower, "bbox=", "&"); 00077 sscanf(bbox.c_str(), "%lf,%lf,%lf,%lf", &_topLeftMin.x(), &_topLeftMin.y(), &_topLeftMax.x(), &_topLeftMax.y()); 00078 00079 //Compute the tile dimensions 00080 _tileWidth = _topLeftMax.x() - _topLeftMin.x(); 00081 _tileHeight = _topLeftMax.y() - _topLeftMin.y(); 00082 00083 //Create the prototype 00084 string::size_type len = lower.find( bbox ); 00085 if (len != string::npos) 00086 { 00087 string beforeBB = _pattern.substr(0, len); 00088 00089 string::size_type after = len + bbox.length(); 00090 string afterBB = ""; 00091 if (after < _pattern.length()-1) 00092 { 00093 afterBB = _pattern.substr(after, _pattern.length() - after); 00094 } 00095 _prototype = beforeBB + std::string("%lf,%lf,%lf,%lf") + afterBB; 00096 } 00097 } 00098 00099 void TilePattern::getTileBounds(const int &x, const int &y, double &minX, double &minY, double &maxX, double &maxY) 00100 { 00101 minX = _topLeftMin.x() + (double)x * _tileWidth; 00102 maxX = minX + _tileWidth; 00103 00104 maxY = _topLeftMax.y() - (double)y * _tileHeight; 00105 minY = maxY - _tileHeight; 00106 } 00107 00108 std::string TilePattern::getRequestString(const int &x, const int &y) 00109 { 00110 double minX, minY, maxX, maxY; 00111 getTileBounds(x, y, minX, minY, maxX, maxY); 00112 00113 char buf[2048]; 00114 sprintf(buf, _prototype.c_str(), minX, minY, maxX, maxY); 00115 return buf; 00116 } 00117 00118 00119 TileService::TileService(): 00120 _dataMin(-180, -90), 00121 _dataMax(180, 90) 00122 { 00123 } 00124 00125 void TileService::getMatchingPatterns(const std::string &layers, const std::string &format, 00126 const std::string &styles, const std::string &srs, 00127 unsigned int imageWidth, unsigned int imageHeight, 00128 TilePatternList& out_patterns) 00129 { 00130 out_patterns.clear(); 00131 for (TilePatternList::iterator i = _patterns.begin(); i < _patterns.end(); ++i) 00132 { 00133 if (osgDB::equalCaseInsensitive(i->getLayers(), layers) && 00134 osgDB::equalCaseInsensitive(i->getFormat(),format) && 00135 osgDB::equalCaseInsensitive(i->getStyles(), styles) && 00136 osgDB::equalCaseInsensitive(i->getSRS(), srs) && 00137 (i->getImageWidth() == (int)imageWidth) && 00138 (i->getImageHeight() == (int)imageHeight)) 00139 { 00140 out_patterns.push_back(*i); 00141 } 00142 } 00143 } 00144 00145 const Profile* 00146 TileService::createProfile(TilePatternList &patterns) 00147 { 00148 //Assume that all the values in the patterns are equal except for the bounding boxes 00149 const Profile* profile = NULL; 00150 00151 if (patterns.size() > 0) 00152 { 00153 double maxWidth = -1; 00154 double maxHeight = -1; 00155 00156 osg::Vec2d topLeftMin; 00157 osg::Vec2d topLeftMax; 00158 00159 //Find the lowest resolution pattern. 00160 for (unsigned int i = 0; i < patterns.size(); ++i) 00161 { 00162 if (maxWidth < patterns[i].getTileWidth() && 00163 maxHeight < patterns[i].getTileHeight()) 00164 { 00165 maxWidth = patterns[i].getTileWidth(); 00166 maxHeight = patterns[i].getTileHeight(); 00167 00168 topLeftMin = patterns[i].getTopLeftMin(); 00169 topLeftMax = patterns[i].getTopLeftMax(); 00170 } 00171 } 00172 00173 double dataWidth = _dataMax.x() - _dataMin.x(); 00174 double dataHeight = _dataMax.y() - _dataMin.y(); 00175 00176 double tileWidth = topLeftMax.x() - topLeftMin.x(); 00177 double tileHeight = topLeftMax.y() - topLeftMin.y(); 00178 00179 unsigned int w = (unsigned int) ceil(dataWidth / tileWidth ); 00180 unsigned int h = (unsigned int) ceil(dataHeight / tileHeight); 00181 00182 double xmin = topLeftMin.x(); 00183 double xmax = xmin + (double)w * tileWidth; 00184 double ymax = topLeftMax.y(); 00185 double ymin = ymax - (double)h * tileHeight; 00186 00187 profile = Profile::create( patterns[0].getSRS(), xmin, ymin, xmax, ymax, "", w, h); 00188 } 00189 00190 return profile; 00191 } 00192 00193 #define ELEM_WMS_TILE_SERVICE "wms_tile_service" 00194 #define ELEM_SERVICE "service" 00195 #define ATTR_VERSION "version" 00196 #define ELEM_NAME "name" 00197 #define ELEM_TITLE "title" 00198 #define ELEM_ABSTRACT "abstract" 00199 #define ELEM_ACCESSCONSTRAINTS "accessconstraints" 00200 00201 #define ELEM_TILEDPATTERNS "tiledpatterns" 00202 #define ELEM_TILEPATTERN "tilepattern" 00203 #define ELEM_TILEDGROUP "tiledgroup" 00204 #define ELEM_LATLONBOUNDINGBOX "latlonboundingbox" 00205 #define ATTR_MINX "minx" 00206 #define ATTR_MINY "miny" 00207 #define ATTR_MAXX "maxx" 00208 #define ATTR_MAXY "maxy" 00209 00210 TileService* 00211 TileServiceReader::read( const std::string &location, const osgDB::ReaderWriter::Options* options ) 00212 { 00213 TileService *tileService = NULL; 00214 if ( osgDB::containsServerAddress( location ) ) 00215 { 00216 HTTPResponse response = HTTPClient::get( location, options); 00217 if (response.isOK() && response.getNumParts() > 0 ) 00218 { 00219 tileService = read( response.getPartStream( 0 ) ); 00220 } 00221 } 00222 else 00223 { 00224 if ((osgDB::fileExists(location)) && (osgDB::fileType(location) == osgDB::REGULAR_FILE)) 00225 { 00226 std::ifstream in( location.c_str() ); 00227 tileService = read( in ); 00228 } 00229 } 00230 return tileService; 00231 } 00232 00233 void readBoundingBox(XmlElement* e_bb, double &minX, double &minY, double &maxX, double &maxY) 00234 { 00235 if (e_bb) 00236 { 00237 minX = as<double>(e_bb->getAttr( ATTR_MINX ), minX); 00238 minY = as<double>(e_bb->getAttr( ATTR_MINY ), minY); 00239 maxX = as<double>(e_bb->getAttr( ATTR_MAXX ), maxX); 00240 maxY = as<double>(e_bb->getAttr( ATTR_MAXY ), maxY); 00241 } 00242 } 00243 00244 void addTilePatterns(XmlElement* e_root, TileService* tileService) 00245 { 00246 //Read all the TilePatterns 00247 XmlNodeList tile_patterns = e_root->getSubElements( ELEM_TILEPATTERN ); 00248 for( XmlNodeList::const_iterator i = tile_patterns.begin(); i != tile_patterns.end(); i++ ) 00249 { 00250 //We only really care about a single access pattern, so extract it 00251 string txt = static_cast<XmlElement*>( i->get() )->getText(); 00252 //Access patterns are separated by whitespace 00253 std::string whitespace (" \t\f\v\n\r"); 00254 string::size_type len = txt.find_first_of(whitespace); 00255 if (len != string::npos) 00256 { 00257 txt = trim(txt.substr(0, len)); 00258 } 00259 TilePattern pattern(txt); 00260 tileService->getPatterns().push_back(pattern); 00261 } 00262 00263 //Read all TilePatterns in the TiledGroups 00264 XmlNodeList tiled_groups = e_root->getSubElements(ELEM_TILEDGROUP); 00265 for( XmlNodeList::const_iterator i = tiled_groups.begin(); i != tiled_groups.end(); i++ ) 00266 { 00267 addTilePatterns(static_cast<XmlElement*>(i->get()), tileService); 00268 } 00269 } 00270 00271 TileService* 00272 TileServiceReader::read(std::istream &in) 00273 { 00274 osg::ref_ptr<TileService> tileService = new TileService; 00275 00276 osg::ref_ptr<XmlDocument> doc = XmlDocument::load( in ); 00277 if (!doc.valid()) 00278 { 00279 OE_INFO << "Failed to load TileService " << std::endl; 00280 return 0; 00281 } 00282 00283 //Get the root TileMap element 00284 osg::ref_ptr<XmlElement> e_root = doc->getSubElement( ELEM_WMS_TILE_SERVICE ); 00285 if (!e_root.valid()) 00286 { 00287 OE_INFO << "Could not find root TileService element " << std::endl; 00288 return 0; 00289 } 00290 00291 tileService->setVersion( e_root->getAttr( ATTR_VERSION ) ); 00292 00293 //Get properties from the Service element. 00294 osg::ref_ptr<XmlElement> e_service = e_root->getSubElement( ELEM_SERVICE ); 00295 if (!e_service.valid()) 00296 { 00297 OE_INFO << "Could not find Service element " << std::endl; 00298 return 0; 00299 } 00300 00301 tileService->setName( e_service->getSubElementText( ELEM_NAME ) ); 00302 tileService->setTitle( e_service->getSubElementText( ELEM_TITLE ) ); 00303 tileService->setAbstract( e_service->getSubElementText( ELEM_ABSTRACT ) ); 00304 tileService->setAccessConstraints( e_service->getSubElementText( ELEM_ACCESSCONSTRAINTS ) ); 00305 00306 //Get the TiledPattern element which contains the TiledGroups 00307 osg::ref_ptr<XmlElement> e_tiledPatterns = e_root->getSubElement( ELEM_TILEDPATTERNS ); 00308 if (!e_tiledPatterns.valid()) 00309 { 00310 OE_INFO << "Could not find TiledPatterns element" << std::endl; 00311 return 0; 00312 } 00313 00314 //Get the bounding box from the TiledPatterns 00315 osg::ref_ptr<XmlElement> e_bb = e_tiledPatterns->getSubElement( ELEM_LATLONBOUNDINGBOX ); 00316 if (e_bb.valid()) 00317 { 00318 double minX, minY, maxX, maxY; 00319 readBoundingBox(e_bb.get(), minX, minY, maxX, maxY); 00320 tileService->setDataMin(osg::Vec2d(minX, minY)); 00321 tileService->setDataMax(osg::Vec2d(maxX, maxY)); 00322 } 00323 00324 addTilePatterns(e_tiledPatterns.get(), tileService.get()); 00325 00326 OE_INFO << "Returning TileService with " << tileService->getPatterns().size() << " patterns " << std::endl; 00327 return tileService.release(); 00328 }