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