1 /* -*-c++-*- */ 2 /* osgEarth - Geospatial SDK for OpenSceneGraph 3 * Copyright 2019 Pelican Mapping 4 * http://osgearth.org 5 * 6 * osgEarth is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/> 18 */ 19 20 #include <osgEarth/Registry> 21 #include <osgEarth/TileSource> 22 #include <osgEarth/FileUtils> 23 #include <osgEarth/ThreadingUtils> 24 #include <osgEarth/URI> 25 #include <osgEarth/HTTPClient> 26 27 #include <osg/Notify> 28 #include <osg/io_utils> 29 #include <osg/Version> 30 #include <osgTerrain/Terrain> 31 32 #include <osgDB/FileNameUtils> 33 #include <osgDB/FileUtils> 34 #include <osgDB/Registry> 35 #include <osgDB/ReadFile> 36 #include <osgDB/WriteFile> 37 38 #include <sstream> 39 40 #include "VPBOptions" 41 42 #define LC "[VPB] " 43 44 using namespace osgEarth; 45 using namespace osgEarth::Drivers; 46 47 //#define PROPERTY_URL "url" 48 //#define PROPERTY_PRIMARY_SPLIT_LEVEL "primary_split_level" 49 //#define PROPERTY_SECONDARY_SPLIT_LEVEL "secondary_split_level" 50 //#define PROPERTY_DIRECTORY_STRUCTURE "directory_structure" 51 //#define PROPERTY_LAYER_NUM "layer" 52 //#define PROPERTY_NUM_TILES_WIDE_AT_LOD0 "num_tiles_wide_at_lod0" 53 //#define PROPERTY_NUM_TILES_HIGH_AT_LOD0 "num_tiles_high_at_lod0" 54 //#define PROPERTY_BASE_NAME "base_name" 55 56 57 class CollectTiles : public osg::NodeVisitor 58 { 59 public: 60 CollectTiles()61 CollectTiles(): 62 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) 63 { 64 } 65 reset()66 void reset() 67 { 68 _terrainTiles.clear(); 69 } 70 apply(osg::Group & group)71 void apply(osg::Group& group) 72 { 73 osgTerrain::TerrainTile* terrainTile = dynamic_cast<osgTerrain::TerrainTile*>(&group); 74 if (terrainTile) 75 { 76 //OE_DEBUG<<"VPB: Found terrain tile TileID("<< 77 // TileKey::getLOD(terrainTile->getTileID())<<", "<< 78 // terrainTile->getTileID().x<<", "<< 79 // terrainTile->getTileID().y<<")"<<std::endl; 80 81 _terrainTiles.push_back(terrainTile); 82 } 83 else 84 { 85 traverse(group); 86 } 87 } 88 getLocator()89 osgTerrain::Locator* getLocator() 90 { 91 for(unsigned int i=0; i<_terrainTiles.size(); ++i) 92 { 93 osgTerrain::TerrainTile* tile = _terrainTiles[i].get(); 94 osgTerrain::Locator* locator = tile->getLocator(); 95 if (locator) return locator; 96 } 97 return 0; 98 } 99 getRange(double & min_x,double & min_y,double & max_x,double & max_y) const100 bool getRange(double& min_x, double& min_y, double& max_x, double& max_y) const 101 { 102 min_x = DBL_MAX; 103 max_x = -DBL_MAX; 104 min_y = DBL_MAX; 105 max_y = -DBL_MAX; 106 107 typedef std::vector<osg::Vec3d> Corners; 108 Corners corners; 109 corners.push_back(osg::Vec3d(0.0f,0.0f,0.0f)); 110 corners.push_back(osg::Vec3d(1.0f,0.0f,0.0f)); 111 corners.push_back(osg::Vec3d(1.0f,1.0f,0.0f)); 112 corners.push_back(osg::Vec3d(1.0f,1.0f,0.0f)); 113 114 for(unsigned int i=0; i<_terrainTiles.size(); ++i) 115 { 116 osgTerrain::TerrainTile* tile = _terrainTiles[i].get(); 117 osgTerrain::Locator* locator = tile->getLocator(); 118 if (locator) 119 { 120 for(Corners::iterator itr = corners.begin(); 121 itr != corners.end(); 122 ++itr) 123 { 124 osg::Vec3d& local = *itr; 125 osg::Vec3d projected = local * locator->getTransform(); 126 127 if (projected.x()<min_x) min_x = projected.x(); 128 if (projected.x()>max_x) max_x = projected.x(); 129 130 if (projected.y()<min_y) min_y = projected.y(); 131 if (projected.y()>max_y) max_y = projected.y(); 132 } 133 } 134 } 135 136 return min_x <= max_x; 137 } 138 139 typedef std::vector< osg::ref_ptr<osgTerrain::TerrainTile> > TerrainTiles; 140 TerrainTiles _terrainTiles; 141 }; 142 143 144 class VPBDatabase : public osg::Referenced 145 { 146 public: VPBDatabase(const VPBOptions & in_options)147 VPBDatabase( const VPBOptions& in_options ) : 148 _options( in_options ), 149 //_directory_structure( FLAT_TASK_DIRECTORIES ), 150 _profile( osgEarth::Registry::instance()->getGlobalGeodeticProfile() ), 151 _maxNumTilesInCache( in_options.terrainTileCacheSize().value() ), 152 _initialized( false ) 153 { 154 } 155 initialize(const osgDB::Options * dbOptions)156 void initialize( const osgDB::Options* dbOptions ) 157 { 158 Threading::ScopedMutexLock lock( _initializeMutex ); 159 160 if ( _initialized ) 161 return; 162 163 _dbOptions = dbOptions; 164 165 unsigned int numTilesWideAtLod0, numTilesHighAtLod0; 166 _profile->getNumTiles(0, numTilesWideAtLod0, numTilesHighAtLod0); 167 168 // validate dataset 169 _url = _options.url().value(); 170 171 if ( !_url.empty() ) 172 { 173 osg::ref_ptr<osgDB::Options> localOptions = Registry::instance()->cloneOrCreateOptions(); 174 175 localOptions->setPluginData("osgearth_vpb Plugin",(void*)(1)); 176 ReadResult rc = _url.readNode( localOptions.get() ); 177 178 if ( rc.succeeded() ) 179 { 180 _rootNode = rc.getNode(); 181 _baseNameToUse = _options.baseName().value(); 182 183 _path = osgDB::getFilePath( *_url ); 184 if ( _baseNameToUse.empty() ) 185 _baseNameToUse = osgDB::getStrippedName( *_url ); 186 _extension = osgDB::getFileExtension( *_url ); 187 188 OE_INFO << LC << "Loaded root "<< _url.full() <<", path="<<_path<<" base_name="<<_baseNameToUse<<" extension="<<_extension<<std::endl; 189 190 std::string srs = _profile->getSRS()->getHorizInitString(); //->getInitString(); //.srs(); 191 192 osg::CoordinateSystemNode* csn = dynamic_cast<osg::CoordinateSystemNode*>(_rootNode.get()); 193 if (csn) 194 { 195 OE_INFO << LC << "CSN found: "<<csn->getCoordinateSystem()<<std::endl; 196 197 srs = csn->getCoordinateSystem(); 198 } 199 200 // We default to a global-geodetic profile, so only try to come up with another profile if the 201 // database is not geocentric. We do this to avoid small arounding errors where the VPB database 202 // isn't exactly -180,-90 180,90 and will result in unnecessary reprojection. 203 if (!csn->getEllipsoidModel()) 204 { 205 CollectTiles ct; 206 _rootNode->accept(ct); 207 208 209 osgTerrain::Locator* locator = ct.getLocator(); 210 if (locator) 211 { 212 double min_x, max_x, min_y, max_y; 213 ct.getRange(min_x, min_y, max_x, max_y); 214 215 OE_DEBUG << LC << "range("<<min_x<<", "<<min_y<<", "<<max_x<<", "<<max_y<< ")" <<std::endl; 216 OE_DEBUG << LC << "range("<<osg::RadiansToDegrees(min_x)<<", "<<osg::RadiansToDegrees(min_y)<<", " 217 <<osg::RadiansToDegrees(max_x)<<", "<<osg::RadiansToDegrees(max_y)<< ")" <<std::endl; 218 219 srs = locator->getCoordinateSystem(); 220 221 double aspectRatio = (max_x-min_x)/(max_y-min_y); 222 223 OE_DEBUG << LC << "aspectRatio = "<<aspectRatio<<std::endl; 224 225 if (aspectRatio>1.0) 226 { 227 numTilesWideAtLod0 = static_cast<unsigned int>(floor(aspectRatio+0.499999)); 228 numTilesHighAtLod0 = 1; 229 } 230 else 231 { 232 numTilesWideAtLod0 = 1; 233 numTilesHighAtLod0 = static_cast<unsigned int>(floor(1.0/aspectRatio+0.499999)); 234 } 235 236 OE_DEBUG << LC << "computed numTilesWideAtLod0 = "<<numTilesWideAtLod0<<std::endl; 237 OE_DEBUG << LC << "computed numTilesHightAtLod0 = "<<numTilesHighAtLod0<<std::endl; 238 239 //if ( _options.valid() ) 240 { 241 if ( _options.numTilesWideAtLod0().isSet() ) 242 numTilesWideAtLod0 = _options.numTilesWideAtLod0().value(); 243 244 if ( _options.numTilesHighAtLod0().isSet() ) 245 numTilesHighAtLod0 = _options.numTilesHighAtLod0().value(); 246 } 247 248 OE_DEBUG << LC << "final numTilesWideAtLod0 = "<<numTilesWideAtLod0<<std::endl; 249 OE_DEBUG << LC << "final numTilesHightAtLod0 = "<<numTilesHighAtLod0<<std::endl; 250 251 _profile = osgEarth::Profile::create( 252 srs, 253 osg::RadiansToDegrees(min_x), 254 osg::RadiansToDegrees(min_y), 255 osg::RadiansToDegrees(max_x), 256 osg::RadiansToDegrees(max_y), 257 "", 258 numTilesWideAtLod0, 259 numTilesHighAtLod0 ); 260 } 261 } 262 263 } 264 else 265 { 266 OE_WARN << LC << rc.getResultCodeString() << ": " << *_url << std::endl; 267 _url = URI(); 268 } 269 } 270 else 271 { 272 OE_WARN<<"VPB: No data referenced "<<std::endl; 273 } 274 275 _initialized = true; 276 } 277 createTileName(int level,int tile_x,int tile_y)278 std::string createTileName( int level, int tile_x, int tile_y ) 279 { 280 std::stringstream buf; 281 if ( _options.directoryStructure() == VPBOptions::DS_FLAT ) 282 { 283 buf<<_path<<"/"<<_baseNameToUse<<"_L"<<level<<"_X"<<tile_x/2<<"_Y"<<tile_y/2<<"_subtile."<<_extension; 284 } 285 else 286 { 287 int psl = _options.primarySplitLevel().value(); 288 int ssl = _options.secondarySplitLevel().value(); 289 290 if (level<psl) 291 { 292 buf<<_path<<"/"<<_baseNameToUse<<"_root_L0_X0_Y0/"<< 293 _baseNameToUse<<"_L"<<level<<"_X"<<tile_x/2<<"_Y"<<tile_y/2<<"_subtile."<<_extension; 294 295 } 296 else if (level<ssl) 297 { 298 tile_x /= 2; 299 tile_y /= 2; 300 301 int split_x = tile_x >> (level - psl); 302 int split_y = tile_y >> (level - psl); 303 304 buf<<_path<<"/"<<_baseNameToUse<<"_subtile_L"<<psl<<"_X"<<split_x<<"_Y"<<split_y<<"/"<< 305 _baseNameToUse<<"_L"<<level<<"_X"<<tile_x<<"_Y"<<tile_y<<"_subtile."<<_extension; 306 } 307 else if ( _options.directoryStructure() == VPBOptions::DS_TASK ) 308 { 309 tile_x /= 2; 310 tile_y /= 2; 311 312 int split_x = tile_x >> (level - psl); 313 int split_y = tile_y >> (level - psl); 314 315 int secondary_split_x = tile_x >> (level - ssl); 316 int secondary_split_y = tile_y >> (level - ssl); 317 318 buf<<_path<<"/"<<_baseNameToUse<<"_subtile_L"<<psl<<"_X"<<split_x<<"_Y"<<split_y<<"/"<< 319 _baseNameToUse<<"_subtile_L"<<ssl<<"_X"<<secondary_split_x<<"_Y"<<secondary_split_y<<"/"<< 320 _baseNameToUse<<"_L"<<level<<"_X"<<tile_x<<"_Y"<<tile_y<<"_subtile."<<_extension; 321 } 322 else 323 { 324 tile_x /= 2; 325 tile_y /= 2; 326 327 int split_x = tile_x >> (level - ssl); 328 int split_y = tile_y >> (level - ssl); 329 330 buf<<_path<<"/"<<_baseNameToUse<<"_subtile_L"<<ssl<<"_X"<<split_x<<"_Y"<<split_y<<"/"<< 331 _baseNameToUse<<"_L"<<level<<"_X"<<tile_x<<"_Y"<<tile_y<<"_subtile."<<_extension; 332 } 333 } 334 335 std::string bufStr; 336 bufStr = buf.str(); 337 OE_DEBUG<<"VPB: VPBDatabase::createTileName(), buf.str()=="<< bufStr <<std::endl; 338 339 return bufStr; 340 } 341 getTerrainTile(const TileKey & key,ProgressCallback * progress,osg::ref_ptr<osgTerrain::TerrainTile> & out_tile)342 void getTerrainTile( const TileKey& key, ProgressCallback* progress, osg::ref_ptr<osgTerrain::TerrainTile>& out_tile ) 343 { 344 int level = key.getLevelOfDetail(); 345 unsigned int tile_x, tile_y; 346 key.getTileXY( tile_x, tile_y ); 347 348 //int max_x = (2 << level) - 1; 349 int max_y = (1 << level) - 1; 350 351 tile_y = max_y - tile_y; 352 353 osgTerrain::TileID tileID(level, tile_x, tile_y); 354 355 findTile(tileID, false, out_tile); 356 if (out_tile.valid()) 357 return; 358 359 std::string filename = createTileName(level, tile_x, tile_y); 360 361 bool foundInBlacklist = false; 362 { 363 Threading::ScopedReadLock sharedLock( _blacklistMutex ); 364 foundInBlacklist = _blacklistedFilenames.count(filename) == 1; 365 } 366 if ( foundInBlacklist ) 367 { 368 OE_DEBUG << LC << "file has been found in black list : "<<filename<<std::endl; 369 insertTile(tileID, 0); 370 return; //return 0; 371 } 372 373 osg::ref_ptr<osgDB::Options> localOptions = Registry::instance()->cloneOrCreateOptions(); 374 localOptions->setPluginData("osgearth_vpb Plugin",(void*)(1)); 375 376 ReadResult r = URI(filename).readNode( localOptions.get(), progress ); 377 if ( r.succeeded() ) 378 { 379 osg::Node* node = r.getNode(); 380 381 //OE_INFO << LC << "Loaded model "<<filename<<std::endl; 382 CollectTiles ct; 383 node->accept(ct); 384 385 int base_x = (tile_x / 2) * 2; 386 int base_y = (tile_y / 2) * 2; 387 388 double min_x, max_x, min_y, max_y; 389 ct.getRange(min_x, min_y, max_x, max_y); 390 391 double center_x = (min_x + max_x)*0.5; 392 double center_y = (min_y + max_y)*0.5; 393 394 osg::Vec3d local(0.5,0.5,0.0); 395 for(unsigned int i=0; i<ct._terrainTiles.size(); ++i) 396 { 397 osgTerrain::TerrainTile* tile = ct._terrainTiles[i].get(); 398 osgTerrain::Locator* locator = tile->getLocator(); 399 if (locator) 400 { 401 osg::Vec3d projected = local * locator->getTransform(); 402 403 int local_x = base_x + ((projected.x() > center_x) ? 1 : 0); 404 int local_y = base_y + ((projected.y() > center_y) ? 1 : 0); 405 osgTerrain::TileID local_tileID(level, local_x, local_y); 406 407 tile->setTileID(local_tileID); 408 insertTile(local_tileID, tile); 409 410 if ( local_tileID == tileID ) 411 out_tile = tile; 412 } 413 414 } 415 416 } 417 else 418 { 419 // in the case of an "unrecoverable" error, black-list the URL for this tile. 420 if ( ! HTTPClient::isRecoverable( r.code() ) ) 421 { 422 Threading::ScopedWriteLock exclusiveLock( _blacklistMutex ); 423 _blacklistedFilenames.insert( filename ); 424 } 425 } 426 427 //TODO: just return it instead... 428 //findTile(tileID, false, out_tile); 429 } 430 insertTile(const osgTerrain::TileID & tileID,osgTerrain::TerrainTile * tile)431 void insertTile(const osgTerrain::TileID& tileID, osgTerrain::TerrainTile* tile) 432 { 433 Threading::ScopedWriteLock exclusiveLock( _tileMapMutex ); 434 435 if ( _tileMap.find(tileID) == _tileMap.end() ) 436 { 437 _tileMap[tileID] = tile; 438 439 _tileFIFO.push_back(tileID); 440 441 if (_tileFIFO.size() > _maxNumTilesInCache) 442 { 443 osgTerrain::TileID tileToRemove = _tileFIFO.front(); 444 _tileFIFO.pop_front(); 445 _tileMap.erase(tileToRemove); 446 447 // OE_DEBUG << LC << "Pruned tileID ("<<TileKey::getLOD(tileID)<<", "<<tileID.x<<", "<<tileID.y<<")"<<std::endl; 448 } 449 450 //OE_DEBUG << LC << "insertTile (" 451 // << TileKey::getLOD(tileID)<<", "<<tileID.x<<", "<<tileID.y<<") " 452 // << " tileFIFO.size()=="<<_tileFIFO.size()<<std::endl; 453 } 454 else 455 { 456 //OE_DEBUG << LC << "insertTile (" 457 // << TileKey::getLOD(tileID)<<", "<<tileID.x<<", "<<tileID.y<<") " 458 // << " ...already in cache!"<<std::endl; 459 } 460 } 461 findTile(const osgTerrain::TileID & tileID,bool insertBlankTileIfNotFound,osg::ref_ptr<osgTerrain::TerrainTile> & out_tile)462 void findTile(const osgTerrain::TileID& tileID, bool insertBlankTileIfNotFound, osg::ref_ptr<osgTerrain::TerrainTile>& out_tile) 463 { 464 // read with a shared lock 465 { 466 Threading::ScopedReadLock sharedLock( _tileMapMutex ); 467 TileMap::iterator itr = _tileMap.find(tileID); 468 if (itr != _tileMap.end()) 469 out_tile = itr->second.get(); 470 } 471 472 // upgrade lock and write: 473 if (insertBlankTileIfNotFound) 474 insertTile(tileID, 0); 475 476 //return 0; 477 } 478 479 const VPBOptions _options; 480 URI _url; 481 std::string _path; 482 std::string _extension; 483 484 std::string _baseNameToUse; 485 486 osg::ref_ptr<const Profile> _profile; 487 osg::ref_ptr<osg::Node> _rootNode; 488 489 unsigned int _maxNumTilesInCache; 490 491 typedef std::map<osgTerrain::TileID, osg::ref_ptr<osgTerrain::TerrainTile> > TileMap; 492 TileMap _tileMap; 493 Threading::ReadWriteMutex _tileMapMutex; 494 495 typedef std::list<osgTerrain::TileID> TileIDList; 496 TileIDList _tileFIFO; 497 498 typedef std::set<std::string> StringSet; 499 StringSet _blacklistedFilenames; 500 Threading::ReadWriteMutex _blacklistMutex; 501 502 bool _initialized; 503 Threading::Mutex _initializeMutex; 504 505 osg::ref_ptr<const osgDB::Options> _dbOptions; 506 507 }; 508 509 class VPBSource : public TileSource 510 { 511 public: VPBSource(VPBDatabase * vpbDatabase,const VPBOptions & in_options)512 VPBSource( VPBDatabase* vpbDatabase, const VPBOptions& in_options ) : 513 TileSource ( in_options ), 514 _vpbDatabase ( vpbDatabase ), 515 _options ( in_options ) 516 { 517 //nop 518 } 519 initialize(const osgDB::Options * dbOptions)520 Status initialize( const osgDB::Options* dbOptions ) 521 { 522 _dbOptions = Registry::instance()->cloneOrCreateOptions( dbOptions ); 523 524 _vpbDatabase->initialize( _dbOptions.get() ); 525 526 if ( !getProfile() ) 527 { 528 setProfile(_vpbDatabase->_profile.get()); 529 } 530 531 return STATUS_OK; 532 } 533 createImage(const TileKey & key,ProgressCallback * progress)534 osg::Image* createImage( const TileKey& key, ProgressCallback* progress) 535 { 536 osg::Image * ret = NULL; 537 //TODO: Make VPB driver use progress callback 538 osg::ref_ptr<osgTerrain::TerrainTile> tile; 539 _vpbDatabase->getTerrainTile(key, progress, tile); 540 if (tile.valid()) 541 { 542 int layerNum = _options.layer().value(); 543 const optional<std::string> & layerSetName = _options.layerSetName(); 544 545 int numColorLayers = (int)tile->getNumColorLayers(); 546 if(layerNum > numColorLayers) 547 layerNum = 0; 548 if (layerNum < numColorLayers) 549 { 550 osgTerrain::Layer* layer = tile->getColorLayer(layerNum); 551 552 osgTerrain::ImageLayer* imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(layer); 553 if (imageLayer) 554 { 555 OE_DEBUG << LC << "createImage(" << key.str() << " layerNum=" << layerNum << ") successful." <<std::endl; 556 ret = new osg::Image( *imageLayer->getImage() ); 557 } 558 else 559 { 560 osgTerrain::SwitchLayer* switchLayer = dynamic_cast<osgTerrain::SwitchLayer*>(layer); 561 if (switchLayer && layerSetName.isSet()) 562 { 563 for(unsigned int si=0; !imageLayer && si<switchLayer->getNumLayers(); ++si) 564 { 565 if(switchLayer->getSetName(si) == layerSetName.value()) 566 { 567 imageLayer = dynamic_cast<osgTerrain::ImageLayer*>(switchLayer->getLayer(si)); 568 } 569 } 570 } 571 if(imageLayer) 572 { 573 OE_DEBUG << LC << "createImage(" << key.str() << " layerSet=" << layerSetName.value() << ") successful." <<std::endl; 574 ret = new osg::Image( *imageLayer->getImage() ); 575 } 576 } 577 } 578 if(!ret) 579 { 580 OE_DEBUG << LC << "createImage(" << key.str() << " layerSet=" << layerSetName.value() << " layerNum=" << layerNum << "/" << numColorLayers << ") failed." <<std::endl; 581 } 582 } 583 else 584 { 585 OE_DEBUG << LC << "createImage(" << key.str() << ") database retrieval failed." <<std::endl; 586 } 587 return ret; 588 } 589 createHeightField(const TileKey & key,ProgressCallback * progress)590 osg::HeightField* createHeightField( const TileKey& key, 591 ProgressCallback* progress ) 592 { 593 osg::ref_ptr<osgTerrain::TerrainTile> tile; 594 _vpbDatabase->getTerrainTile(key, progress, tile); 595 if (tile.valid()) 596 { 597 osgTerrain::Layer* elevationLayer = tile->getElevationLayer(); 598 osgTerrain::HeightFieldLayer* hfLayer = dynamic_cast<osgTerrain::HeightFieldLayer*>(elevationLayer); 599 if (hfLayer) 600 { 601 //return hfLayer->getHeightField(); 602 return new osg::HeightField(*hfLayer->getHeightField()); 603 } 604 } 605 606 return 0; 607 } 608 getExtension() const609 virtual std::string getExtension() const 610 { 611 //All VPB tiles are in IVE format 612 return _vpbDatabase->_extension; 613 } 614 615 private: 616 osg::ref_ptr<VPBDatabase> _vpbDatabase; 617 const VPBOptions _options; 618 osg::ref_ptr<osgDB::Options> _dbOptions; 619 }; 620 621 622 class VPBSourceFactory : public TileSourceDriver 623 { 624 public: VPBSourceFactory()625 VPBSourceFactory() 626 { 627 supportsExtension( "osgearth_vpb", "VirtualPlanetBuilder data" ); 628 } 629 className() const630 virtual const char* className() const 631 { 632 return "VirtualPlanetBuilder ReaderWriter"; 633 } 634 readObject(const std::string & file_name,const Options * options) const635 virtual ReadResult readObject(const std::string& file_name, const Options* options) const 636 { 637 if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) 638 return ReadResult::FILE_NOT_HANDLED; 639 640 VPBOptions vpbOptions( getTileSourceOptions(options) ); 641 642 URI url = vpbOptions.url().value(); 643 if ( !url.empty() ) 644 { 645 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(vpbDatabaseMapMutex); 646 osg::observer_ptr<VPBDatabase>& db_ptr = vpbDatabaseMap[*url]; //get or create 647 648 if (!db_ptr) db_ptr = new VPBDatabase( vpbOptions ); 649 650 if (db_ptr.valid()) 651 return new VPBSource( db_ptr.get(), vpbOptions ); 652 else 653 return ReadResult::FILE_NOT_FOUND; 654 } 655 else 656 { 657 return ReadResult::FILE_NOT_HANDLED; 658 } 659 } 660 661 typedef std::map<std::string, osg::observer_ptr<VPBDatabase> > VPBDatabaseMap; 662 mutable OpenThreads::Mutex vpbDatabaseMapMutex; 663 mutable VPBDatabaseMap vpbDatabaseMap; 664 }; 665 666 REGISTER_OSGPLUGIN(osgearth_vpb, VPBSourceFactory) 667 668