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 #include <osgEarth/TerrainLayer>
20 #include <osgEarth/Registry>
21 #include <osgEarth/TimeControl>
22 #include <osgEarth/URI>
23 
24 using namespace osgEarth;
25 using namespace OpenThreads;
26 
27 #define LC "[TerrainLayer] Layer \"" << getName() << "\" "
28 
29 //------------------------------------------------------------------------
30 
TerrainLayerOptions()31 TerrainLayerOptions::TerrainLayerOptions() :
32 VisibleLayerOptions()
33 {
34     setDefaults();
35     fromConfig(_conf);
36 }
37 
TerrainLayerOptions(const ConfigOptions & co)38 TerrainLayerOptions::TerrainLayerOptions(const ConfigOptions& co) :
39 VisibleLayerOptions(co)
40 {
41     setDefaults();
42     fromConfig(_conf);
43 }
44 
TerrainLayerOptions(const std::string & layerName)45 TerrainLayerOptions::TerrainLayerOptions(const std::string& layerName) :
46 VisibleLayerOptions()
47 {
48     setDefaults();
49     fromConfig(_conf);
50     name() = layerName;
51 }
52 
TerrainLayerOptions(const std::string & layerName,const TileSourceOptions & driverOptions)53 TerrainLayerOptions::TerrainLayerOptions(const std::string& layerName, const TileSourceOptions& driverOptions) :
54 VisibleLayerOptions(driverOptions)
55 {
56     setDefaults();
57     fromConfig(_conf);
58     _driver = driverOptions;
59     name() = layerName;
60 }
61 
62 void
setDefaults()63 TerrainLayerOptions::setDefaults()
64 {
65     _exactCropping.init( false );
66     _reprojectedTileSize.init( 256 );
67     _minLevel.init( 0 );
68     _maxLevel.init( 23 );
69     _maxDataLevel.init( 99 );
70     _tileSize.init( 256 );
71 }
72 
73 Config
getConfig() const74 TerrainLayerOptions::getConfig() const
75 {
76     Config conf = VisibleLayerOptions::getConfig();
77 
78     conf.set( "min_level", _minLevel );
79     conf.set( "max_level", _maxLevel );
80     conf.set( "min_resolution", _minResolution );
81     conf.set( "max_resolution", _maxResolution );
82     conf.set( "max_data_level", _maxDataLevel );
83     conf.set( "edge_buffer_ratio", _edgeBufferRatio);
84     conf.set( "reprojected_tilesize", _reprojectedTileSize);
85     conf.set( "vdatum", _vertDatum );
86     conf.set( "proxy", _proxySettings );
87     conf.set("no_data_value", _noDataValue);
88     conf.set("min_valid_value", _minValidValue);
89     conf.set("max_valid_value", _maxValidValue);
90     conf.set( "tile_size", _tileSize);
91 
92     return conf;
93 }
94 
95 void
fromConfig(const Config & conf)96 TerrainLayerOptions::fromConfig(const Config& conf)
97 {
98     conf.get( "min_level", _minLevel );
99     conf.get( "max_level", _maxLevel );
100     conf.get( "min_resolution", _minResolution );
101     conf.get( "max_resolution", _maxResolution );
102     conf.get( "max_data_level", _maxDataLevel );
103     conf.get( "edge_buffer_ratio", _edgeBufferRatio);
104     conf.get( "reprojected_tilesize", _reprojectedTileSize);
105     conf.get( "vdatum", _vertDatum );
106     conf.get( "vsrs", _vertDatum );    // back compat
107     conf.get( "proxy",        _proxySettings );
108     conf.get("no_data_value", _noDataValue);
109     conf.get("nodata_value", _noDataValue); // back compat
110     conf.get("min_valid_value", _minValidValue);
111     conf.get("max_valid_value", _maxValidValue);
112     conf.get( "tile_size", _tileSize);
113 
114     if (conf.hasValue("driver"))
115         driver() = TileSourceOptions(conf);
116 }
117 
118 void
mergeConfig(const Config & conf)119 TerrainLayerOptions::mergeConfig(const Config& conf)
120 {
121     VisibleLayerOptions::mergeConfig(conf);
122     fromConfig(conf);
123 }
124 
125 //------------------------------------------------------------------------
126 
CacheBinMetadata()127 TerrainLayer::CacheBinMetadata::CacheBinMetadata() :
128 _valid(false)
129 {
130     //nop
131 }
132 
CacheBinMetadata(const TerrainLayer::CacheBinMetadata & rhs)133 TerrainLayer::CacheBinMetadata::CacheBinMetadata(const TerrainLayer::CacheBinMetadata& rhs) :
134 _valid          ( rhs._valid ),
135 _cacheBinId     ( rhs._cacheBinId ),
136 _sourceName     ( rhs._sourceName ),
137 _sourceDriver   ( rhs._sourceDriver ),
138 _sourceTileSize ( rhs._sourceTileSize ),
139 _sourceProfile  ( rhs._sourceProfile ),
140 _cacheProfile   ( rhs._cacheProfile ),
141 _cacheCreateTime( rhs._cacheCreateTime )
142 {
143     //nop
144 }
145 
CacheBinMetadata(const Config & conf)146 TerrainLayer::CacheBinMetadata::CacheBinMetadata(const Config& conf)
147 {
148     _valid = !conf.empty();
149 
150     conf.get("cachebin_id", _cacheBinId);
151     conf.get("source_name", _sourceName);
152     conf.get("source_driver", _sourceDriver);
153     conf.get("source_tile_size", _sourceTileSize);
154     conf.get("source_profile", _sourceProfile);
155     conf.get("cache_profile", _cacheProfile);
156     conf.get("cache_create_time", _cacheCreateTime);
157 
158     const Config* extentsRoot = conf.child_ptr("extents");
159     if ( extentsRoot )
160     {
161         const ConfigSet& extents = extentsRoot->children();
162 
163         for (ConfigSet::const_iterator i = extents.begin(); i != extents.end(); ++i)
164         {
165             std::string srsString;
166             double xmin, ymin, xmax, ymax;
167             optional<unsigned> minLevel, maxLevel;
168 
169             srsString = i->value("srs");
170             xmin = i->value("xmin", 0.0f);
171             ymin = i->value("ymin", 0.0f);
172             xmax = i->value("xmax", 0.0f);
173             ymax = i->value("ymax", 0.0f);
174             i->get("minlevel", minLevel);
175             i->get("maxlevel", maxLevel);
176 
177             const SpatialReference* srs = SpatialReference::get(srsString);
178             DataExtent e( GeoExtent(srs, xmin,  ymin, xmax, ymax) );
179             if (minLevel.isSet())
180                 e.minLevel() = minLevel.get();
181             if (maxLevel.isSet())
182                 e.maxLevel() = maxLevel.get();
183 
184             _dataExtents.push_back(e);
185         }
186     }
187 
188     // check for validity. This will reject older caches that don't have
189     // sufficient attribution.
190     if (_valid)
191     {
192         if (!conf.hasValue("source_tile_size") ||
193             !conf.hasChild("source_profile") ||
194             !conf.hasChild("cache_profile"))
195         {
196             _valid = false;
197         }
198     }
199 }
200 
201 Config
getConfig() const202 TerrainLayer::CacheBinMetadata::getConfig() const
203 {
204     Config conf("osgearth_terrainlayer_cachebin");
205     conf.set("cachebin_id", _cacheBinId);
206     conf.set("source_name", _sourceName);
207     conf.set("source_driver", _sourceDriver);
208     conf.set("source_tile_size", _sourceTileSize);
209     conf.set("source_profile", _sourceProfile);
210     conf.set("cache_profile", _cacheProfile);
211     conf.set("cache_create_time", _cacheCreateTime);
212 
213     if (!_dataExtents.empty())
214     {
215         Config extents;
216         for (DataExtentList::const_iterator i = _dataExtents.begin(); i != _dataExtents.end(); ++i)
217         {
218             Config extent;
219             extent.set("srs", i->getSRS()->getHorizInitString());
220             extent.set("xmin", i->xMin());
221             extent.set("ymin", i->yMin());
222             extent.set("xmax", i->xMax());
223             extent.set("ymax", i->yMax());
224             extent.set("minlevel", i->minLevel());
225             extent.set("maxlevel", i->maxLevel());
226 
227             extents.add("extent", extent);
228         }
229         conf.add("extents", extents);
230     }
231 
232     return conf;
233 }
234 
235 //------------------------------------------------------------------------
236 
TerrainLayer(TerrainLayerOptions * optionsPtr)237 TerrainLayer::TerrainLayer(TerrainLayerOptions* optionsPtr) :
238 VisibleLayer(optionsPtr ? optionsPtr : &_optionsConcrete),
239 _options(optionsPtr ? optionsPtr : &_optionsConcrete),
240 _openCalled(false),
241 _tileSourceExpected(true)
242 {
243     //nop - init() called by subclass
244 }
245 
TerrainLayer(TerrainLayerOptions * optionsPtr,TileSource * tileSource)246 TerrainLayer::TerrainLayer(TerrainLayerOptions* optionsPtr, TileSource* tileSource) :
247 VisibleLayer(optionsPtr ? optionsPtr : &_optionsConcrete),
248 _options(optionsPtr ? optionsPtr : &_optionsConcrete),
249 _tileSource(tileSource),
250 _openCalled(false),
251 _tileSourceExpected(true)
252 {
253     //nop - init() called by subclass
254 }
255 
~TerrainLayer()256 TerrainLayer::~TerrainLayer()
257 {
258     //nop
259 }
260 
261 void
init()262 TerrainLayer::init()
263 {
264     Layer::init();
265 
266     // intiailize our read-options, which store caching and IO information.
267     setReadOptions(0L);
268 
269     if (options().tileSize().isSet())
270         _tileSize = options().tileSize().get();
271     else
272         _tileSize = 256;
273 }
274 
275 const Status&
open()276 TerrainLayer::open()
277 {
278     if ( !_openCalled )
279     {
280         // Call base class
281         if (VisibleLayer::open().isError())
282             return getStatus();
283 
284         // Create an L2 mem cache that sits atop the main cache, if necessary.
285         // For now: use the same L2 cache size at the driver.
286         int l2CacheSize = options().driver()->L2CacheSize().get();
287 
288         // See if it was overridden with an env var.
289         char const* l2env = ::getenv( "OSGEARTH_L2_CACHE_SIZE" );
290         if ( l2env )
291         {
292             l2CacheSize = as<int>( std::string(l2env), 0 );
293             OE_INFO << LC << "L2 cache size set from environment = " << l2CacheSize << "\n";
294         }
295 
296         // Env cache-only mode also disables the L2 cache.
297         char const* noCacheEnv = ::getenv( "OSGEARTH_MEMORY_PROFILE" );
298         if ( noCacheEnv )
299         {
300             l2CacheSize = 0;
301         }
302 
303         // Initialize the l2 cache if it's size is > 0
304         if ( l2CacheSize > 0 )
305         {
306             _memCache = new MemCache( l2CacheSize );
307         }
308 
309         // create the unique cache ID for the cache bin.
310         //std::string cacheId;
311 
312         if (options().cacheId().isSet() && !options().cacheId()->empty())
313         {
314             // user expliticy set a cacheId in the terrain layer options.
315             // this appears to be a NOP; review for removal -gw
316             _runtimeCacheId = options().cacheId().get();
317         }
318         else
319         {
320             // system will generate a cacheId from the layer configuration.
321             Config hashConf = options().getConfig();
322 
323             // remove non-data properties.
324             hashConf.remove("name");
325             hashConf.remove("enabled");
326             hashConf.remove("cacheid");
327             hashConf.remove("cache_only");
328             hashConf.remove("cache_enabled");
329             hashConf.remove("cache_policy");
330             hashConf.remove("visible");
331             hashConf.remove("l2_cache_size");
332 
333             OE_DEBUG << "hashConfFinal = " << hashConf.toJSON(true) << std::endl;
334 
335             unsigned hash = osgEarth::hashString(hashConf.toJSON());
336             _runtimeCacheId = Stringify() << std::hex << std::setw(8) << std::setfill('0') << hash;
337         }
338 
339         // Now that we know the cache ID, establish the cache settings for this Layer.
340         // Start by cloning whatever CacheSettings were inherited in the read options
341         // (typically from the Map).
342         CacheSettings* oldSettings = CacheSettings::get(_readOptions.get());
343         _cacheSettings = oldSettings ? new CacheSettings(*oldSettings) : new CacheSettings();
344 
345         // Store for further propagation!
346         _cacheSettings->store(_readOptions.get());
347 
348         // Integrate a cache policy from this Layer's options:
349         _cacheSettings->integrateCachePolicy(options().cachePolicy());
350 
351 
352         // If you created the layer with a pre-created tile source, it will already by set.
353         if (!_tileSource.valid())
354         {
355             osg::ref_ptr<TileSource> ts;
356 
357             // as long as we're not in cache-only mode, try to create the TileSource.
358             if (_cacheSettings->cachePolicy()->isCacheOnly())
359             {
360                 OE_INFO << LC << "Opening in cache-only mode\n";
361             }
362             else if (isTileSourceExpected())
363             {
364                 // Initialize the tile source once and only once.
365                 ts = createAndOpenTileSource();
366             }
367 
368             // All good
369             if (ts.valid() && !_tileSource.valid())
370             {
371                 _tileSource = ts.release();
372             }
373         }
374         else
375         {
376             // User supplied the tile source, so attempt to initialize it:
377             _tileSource = createAndOpenTileSource();
378         }
379 
380         // Finally, open and activate a caching bin for this layer if it
381         // hasn't already been created.
382         if (_cacheSettings->isCacheEnabled() && _cacheSettings->getCacheBin() == 0L)
383         {
384             CacheBin* bin = _cacheSettings->getCache()->addBin(_runtimeCacheId);
385             if (bin)
386             {
387                 _cacheSettings->setCacheBin(bin);
388                 OE_INFO << LC << "Cache bin is [" << bin->getID() << "]\n";
389             }
390         }
391 
392         OE_INFO << LC << _cacheSettings->toString() << "\n";
393 
394         // Done!
395         _openCalled = true;
396 
397     }
398 
399     return getStatus();
400 }
401 
402 void
close()403 TerrainLayer::close()
404 {
405     setProfile(0L);
406     _tileSource = 0L;
407     _openCalled = false;
408     setStatus(Status());
409     _readOptions = 0L;
410     _cacheSettings = new CacheSettings();
411 }
412 
413 void
establishCacheSettings()414 TerrainLayer::establishCacheSettings()
415 {
416     //nop
417 }
418 
419 CacheSettings*
getCacheSettings() const420 TerrainLayer::getCacheSettings() const
421 {
422     return _cacheSettings.get();
423 }
424 
425 void
setTargetProfileHint(const Profile * profile)426 TerrainLayer::setTargetProfileHint( const Profile* profile )
427 {
428     _targetProfileHint = profile;
429 
430     // Re-read the  cache policy hint since it may change due to the target profile change.
431     refreshTileSourceCachePolicyHint( getTileSource() );
432 }
433 
434 void
refreshTileSourceCachePolicyHint(TileSource * ts)435 TerrainLayer::refreshTileSourceCachePolicyHint(TileSource* ts)
436 {
437     if ( ts && getCacheSettings() && !options().cachePolicy().isSet() )
438     {
439         CachePolicy hint = ts->getCachePolicyHint( _targetProfileHint.get() );
440 
441         if ( hint.usage().isSetTo(CachePolicy::USAGE_NO_CACHE) )
442         {
443             getCacheSettings()->cachePolicy() = hint;
444             OE_INFO << LC << "Caching disabled (by policy hint)" << std::endl;
445         }
446     }
447 }
448 
449 TileSource*
getTileSource() const450 TerrainLayer::getTileSource() const
451 {
452     return _tileSource.get();
453 }
454 
455 const Profile*
getProfile() const456 TerrainLayer::getProfile() const
457 {
458     return _profile.get();
459 }
460 
461 void
setProfile(const Profile * profile)462 TerrainLayer::setProfile(const Profile* profile)
463 {
464     _profile = profile;
465 }
466 
467 bool
isDynamic() const468 TerrainLayer::isDynamic() const
469 {
470     TileSource* ts = getTileSource();
471     return ts ? ts->isDynamic() : false;
472 }
473 
474 std::string
getAttribution() const475 TerrainLayer::getAttribution() const
476 {
477     // Get the attribution from the layer if it's set.
478     if (_options->attribution().isSet())
479     {
480         return *_options->attribution();
481     }
482 
483     // Get it from the tilesource if it's not set on the layer.
484     TileSource* ts = getTileSource();
485     if (ts)
486     {
487         return ts->getAttribution();
488     }
489     else
490     {
491         return "";
492     }
493 }
494 
495 std::string
getMetadataKey(const Profile * profile) const496 TerrainLayer::getMetadataKey(const Profile* profile) const
497 {
498     if (profile)
499         return Stringify() << profile->getHorizSignature() << "_metadata";
500     else
501         return "_metadata";
502 }
503 
504 CacheBin*
getCacheBin(const Profile * profile)505 TerrainLayer::getCacheBin(const Profile* profile)
506 {
507     if ( !_openCalled )
508     {
509         OE_WARN << LC << "Illegal- called getCacheBin() before layer is open.. did you call open()?\n";
510         return 0L;
511     }
512 
513     CacheSettings* cacheSettings = getCacheSettings();
514     if (!cacheSettings)
515         return 0L;
516 
517     if (cacheSettings->cachePolicy()->isCacheDisabled())
518         return 0L;
519 
520     CacheBin* bin = cacheSettings->getCacheBin();
521     if (!bin)
522         return 0L;
523 
524     // does the metadata need initializing?
525     std::string metaKey = getMetadataKey(profile);
526 
527     Threading::ScopedMutexLock lock(_mutex);
528 
529     CacheBinMetadataMap::iterator i = _cacheBinMetadata.find(metaKey);
530     if (i == _cacheBinMetadata.end())
531     {
532         //std::string cacheId = _runtimeOptions->cacheId().get();
533 
534         // read the metadata record from the cache bin:
535         ReadResult rr = bin->readString(metaKey, _readOptions.get());
536 
537         osg::ref_ptr<CacheBinMetadata> meta;
538         bool metadataOK = false;
539 
540         if (rr.succeeded())
541         {
542             // Try to parse the metadata record:
543             Config conf;
544             conf.fromJSON(rr.getString());
545             meta = new CacheBinMetadata(conf);
546 
547             if (meta->isOK())
548             {
549                 metadataOK = true;
550 
551                 // verify that the cache if compatible with the open tile source:
552                 if ( getTileSource() && getProfile() )
553                 {
554                     //todo: check the profile too
555                     if ( meta->_sourceDriver.get() != getTileSource()->getOptions().getDriver() )
556                     {
557                         OE_WARN << LC
558                             << "Layer \"" << getName() << "\" is requesting a \""
559                             << getTileSource()->getOptions().getDriver() << "\" cache, but a \""
560                             << meta->_sourceDriver.get() << "\" cache exists at the specified location. "
561                             << "The cache will ignored for this layer.\n";
562 
563                         cacheSettings->cachePolicy() = CachePolicy::NO_CACHE;
564                         return 0L;
565                     }
566                 }
567 
568                 // if not, see if we're in cache-only mode and still need a profile:
569                 else if (cacheSettings->cachePolicy()->isCacheOnly() && !_profile.valid())
570                 {
571                     // in cacheonly mode, create a profile from the first cache bin accessed
572                     // (they SHOULD all be the same...)
573                     setProfile( Profile::create(meta->_sourceProfile.get()) );
574                     _tileSize = meta->_sourceTileSize.get();
575                 }
576 
577                 bin->setMetadata(meta.get());
578             }
579             else
580             {
581                 OE_WARN << LC << "Metadata appears to be corrupt.\n";
582             }
583         }
584 
585         if (!metadataOK)
586         {
587             // cache metadata does not exist, so try to create it.
588             if ( getProfile() )
589             {
590                 meta = new CacheBinMetadata();
591 
592                 // no existing metadata; create some.
593                 meta->_cacheBinId      = _runtimeCacheId;
594                 meta->_sourceName      = this->getName();
595                 meta->_sourceTileSize  = getTileSize();
596                 meta->_sourceProfile   = getProfile()->toProfileOptions();
597                 meta->_cacheProfile    = profile->toProfileOptions();
598                 meta->_cacheCreateTime = DateTime().asTimeStamp();
599                 meta->_dataExtents     = getDataExtents();
600 
601                 if (getTileSource())
602                 {
603                     meta->_sourceDriver = getTileSource()->getOptions().getDriver();
604                 }
605 
606                 // store it in the cache bin.
607                 std::string data = meta->getConfig().toJSON(false);
608                 osg::ref_ptr<StringObject> temp = new StringObject(data);
609                 bin->write(metaKey, temp.get(), _readOptions.get());
610 
611                 bin->setMetadata(meta.get());
612             }
613 
614             else if ( cacheSettings->cachePolicy()->isCacheOnly() )
615             {
616                 disable(Stringify() <<
617                     "Failed to open a cache for layer "
618                     "because cache_only policy is in effect and bin [" << _runtimeCacheId << "] "
619                     "could not be located.");
620 
621                 return 0L;
622             }
623 
624             else
625             {
626                 OE_WARN << LC <<
627                     "Failed to create cache bin [" << _runtimeCacheId << "] "
628                     "because there is no valid profile."
629                     << std::endl;
630 
631                 cacheSettings->cachePolicy() = CachePolicy::NO_CACHE;
632                 return 0L;
633             }
634         }
635 
636         // If we loaded a profile from the cache metadata, apply the overrides:
637         applyProfileOverrides();
638 
639         if (meta.valid())
640         {
641             _cacheBinMetadata[metaKey] = meta.get();
642             OE_DEBUG << LC << "Established metadata for cache bin [" << _runtimeCacheId << "]" << std::endl;
643         }
644     }
645 
646     return bin;
647 }
648 
649 void
disable(const std::string & msg)650 TerrainLayer::disable(const std::string& msg)
651 {
652     setStatus(Status::Error(msg));
653 }
654 
655 TerrainLayer::CacheBinMetadata*
getCacheBinMetadata(const Profile * profile)656 TerrainLayer::getCacheBinMetadata(const Profile* profile)
657 {
658     if (!profile)
659         return 0L;
660 
661     Threading::ScopedMutexLock lock(_mutex);
662 
663     CacheBinMetadataMap::iterator i = _cacheBinMetadata.find(getMetadataKey(profile));
664     return i != _cacheBinMetadata.end() ? i->second.get() : 0L;
665 }
666 
667 TileSource*
createTileSource()668 TerrainLayer::createTileSource()
669 {
670     if (options().driver().isSet())
671     {
672         OE_INFO << LC << "Creating \"" << options().driver()->getDriver() << "\" driver\n";
673 
674         return TileSourceFactory::create(options().driver().get());
675     }
676     else
677     {
678         return 0L;
679     }
680 }
681 
682 TileSource*
createAndOpenTileSource()683 TerrainLayer::createAndOpenTileSource()
684 {
685     osg::ref_ptr<TileSource> ts;
686 
687     if ( _tileSource.valid() )
688     {
689         // this will happen if the layer was created with an explicit TileSource instance.
690         ts = _tileSource.get();
691     }
692 
693     else
694     {
695         ts = createTileSource();
696 
697         if (!ts.valid())
698         {
699             setStatus(Status::Error(Status::ServiceUnavailable, "Failed to load tile source plugin"));
700             return 0L;
701         }
702     }
703 
704     Status tileSourceStatus;
705 
706     // Initialize the profile with the context information:
707     if ( ts.valid() )
708     {
709         // add the osgDB options string if it's set.
710         const optional<std::string>& osgOptions = ts->getOptions().osgOptionString();
711         if ( osgOptions.isSet() && !osgOptions->empty() )
712         {
713             std::string s = _readOptions->getOptionString();
714             if ( !s.empty() )
715                 s = Stringify() << osgOptions.get() << " " << s;
716             else
717                 s = osgOptions.get();
718             _readOptions->setOptionString( s );
719         }
720 
721         // If we're setting any custom options, do so now before opening:
722         if (options().tileSize().isSet())
723             ts->setPixelsPerTile(options().tileSize().get());
724 
725         if (options().noDataValue().isSet())
726             ts->setNoDataValue(options().noDataValue().get());
727 
728         if (options().minValidValue().isSet())
729             ts->setMinValidValue(options().minValidValue().get());
730 
731         if (options().maxValidValue().isSet())
732             ts->setMaxValidValue(options().maxValidValue().get());
733 
734 
735         // report on a manual override profile:
736         if ( ts->getProfile() )
737         {
738             OE_INFO << LC << "Override profile: "  << ts->getProfile()->toString() << std::endl;
739         }
740 
741         // Now that the tile source exists, set up the cache.
742         if (_cacheSettings->isCacheEnabled())
743         {
744             // read the cache policy hint from the tile source unless user expressly set
745             // a policy in the initialization options. In other words, the hint takes
746             // ultimate priority (even over the Registry override) unless expressly
747             // overridden in the layer options!
748             refreshTileSourceCachePolicyHint( ts.get() );
749 
750             // Unless the user has already configured an expiration policy, use the "last modified"
751             // timestamp of the TileSource to set a minimum valid cache entry timestamp.
752             const CachePolicy& cp = options().cachePolicy().get();
753 
754             if ( !cp.minTime().isSet() && !cp.maxAge().isSet() && ts->getLastModifiedTime() > 0)
755             {
756                 // The "effective" policy overrides the runtime policy, but it does not get serialized.
757                 _cacheSettings->cachePolicy()->mergeAndOverride( cp );
758                 _cacheSettings->cachePolicy()->minTime() = ts->getLastModifiedTime();
759                 OE_INFO << LC << "driver says min valid timestamp = " << DateTime(*cp.minTime()).asRFC1123() << "\n";
760             }
761 
762             CacheBin* bin = _cacheSettings->getCache()->addBin(_runtimeCacheId);
763             if (bin)
764             {
765                 _cacheSettings->setCacheBin(bin);
766                 OE_INFO << LC << "Cache bin is [" << bin->getID() << "]\n";
767             }
768         }
769 
770         // Open the tile source (if it hasn't already been started)
771         tileSourceStatus = ts->getStatus();
772         if (!tileSourceStatus.isOK())
773         {
774             tileSourceStatus = ts->open(TileSource::MODE_READ, _readOptions.get());
775         }
776 
777         // Now that the tile source is open and ready, propagate any user-set
778         // properties to and fro.
779         if ( tileSourceStatus.isOK() )
780         {
781             if (!ts->getDataExtents().empty())
782                 _dataExtents = ts->getDataExtents();
783         }
784         else
785         {
786             //OE_WARN << LC << "Driver initialization failed: " << tileSourceStatus.message() << std::endl;
787             ts = NULL;
788         }
789     }
790 
791     // Set the profile from the TileSource if possible:
792     if ( ts.valid() )
793     {
794         if (!_profile.valid())
795         {
796             OE_DEBUG << LC << "Get Profile from tile source" << std::endl;
797             setProfile(ts->getProfile());
798         }
799 
800 
801         if (_profile.valid())
802         {
803             // create the final profile from any overrides:
804             applyProfileOverrides();
805             OE_INFO << LC << "Profile=" << _profile->toString() << std::endl;
806         }
807     }
808 
809     // Otherwise, force cache-only mode (since there is no tilesource). The layer will try to
810     // establish a profile from the metadata in the cache instead.
811     else if (getCacheSettings()->isCacheEnabled() && options().cacheId().isSet())
812     {
813         OE_WARN << LC << tileSourceStatus.message() << std::endl;
814         OE_WARN << LC << "will attempt to use the cache as a fallback data source" << std::endl;
815         getCacheSettings()->cachePolicy() = CachePolicy::CACHE_ONLY;
816     }
817 
818     // Finally: if we could not open a TileSource, and there's no cache available,
819     // just disable the layer.
820     else
821     {
822         disable(tileSourceStatus.message());
823         setStatus(tileSourceStatus);
824     }
825 
826     return ts.release();
827 }
828 
829 void
applyProfileOverrides()830 TerrainLayer::applyProfileOverrides()
831 {
832     // Check for a vertical datum override.
833     bool changed = false;
834     if ( _profile.valid() && options().verticalDatum().isSet() )
835     {
836         std::string vdatum = options().verticalDatum().get();
837         OE_INFO << "override vdatum = " << vdatum << ", profile vdatum = " << _profile->getSRS()->getVertInitString() << std::endl;
838         if ( !ciEquals(_profile->getSRS()->getVertInitString(), vdatum) )
839         {
840             ProfileOptions po = _profile->toProfileOptions();
841             po.vsrsString() = vdatum;
842             setProfile( Profile::create(po) );
843             changed = true;
844         }
845     }
846 
847     if (changed && _profile.valid())
848     {
849         OE_INFO << LC << "Override profile: " << _profile->toString() << std::endl;
850     }
851 }
852 
853 #if 0
854 bool
855 TerrainLayer::mayHaveDataInExtent(const GeoExtent& ex) const
856 {
857     if (!ex.isValid())
858     {
859         // bad extent; no data
860         return false;
861     }
862 
863     const DataExtentList& de = getDataExtents();
864     if (de.empty())
865     {
866         // not enough info, assume yes
867         return true;
868     }
869 
870     // Get extent in local profile:
871     GeoExtent localExtent = ex;
872     if (getProfile() && !getProfile()->getSRS()->isHorizEquivalentTo(ex.getSRS()))
873     {
874         localExtent = getProfile()->clampAndTransformExtent(ex);
875     }
876 
877     // Check union:
878     if (getDataExtentsUnion().intersects(localExtent))
879     {
880         // possible yes
881         return true;
882     }
883 
884     // Check each extent in turn:
885     for (DataExtentList::const_iterator i = de.begin(); i != de.end(); ++i)
886     {
887         if (i->intersects(localExtent))
888         {
889             // possible yes
890             return true;
891         }
892     }
893 
894     // definite no.
895     return false;
896 }
897 #endif
898 
899 bool
isKeyInLegalRange(const TileKey & key) const900 TerrainLayer::isKeyInLegalRange(const TileKey& key) const
901 {
902     if ( !key.valid() )
903     {
904         return false;
905     }
906 
907     // We must use the equivalent lod b/c the input key can be in any profile.
908     unsigned localLOD = getProfile() ?
909         getProfile()->getEquivalentLOD(key.getProfile(), key.getLOD()) :
910         key.getLOD();
911 
912 
913     // First check the key against the min/max level limits, it they are set.
914     if ((options().maxLevel().isSet() && localLOD > options().maxLevel().value()) ||
915         (options().minLevel().isSet() && localLOD < options().minLevel().value()))
916     {
917         return false;
918     }
919 
920     // Next check the maxDataLevel if that is set.
921     if (options().maxDataLevel().isSet() && localLOD > options().maxDataLevel().get())
922     {
923         return false;
924     }
925 
926     // Next, check against resolution limits (based on the source tile size).
927     if (options().minResolution().isSet() || options().maxResolution().isSet())
928     {
929         const Profile* profile = getProfile();
930         if ( profile )
931         {
932             // calculate the resolution in the layer's profile, which can
933             // be different that the key's profile.
934             double resKey   = key.getExtent().width() / (double)getTileSize();
935             double resLayer = key.getProfile()->getSRS()->transformUnits(resKey, profile->getSRS());
936 
937             if (options().maxResolution().isSet() &&
938                 options().maxResolution().value() > resLayer)
939             {
940                 return false;
941             }
942 
943             if (options().minResolution().isSet() &&
944                 options().minResolution().value() < resLayer)
945             {
946                 return false;
947             }
948         }
949     }
950 
951 	return true;
952 }
953 
954 bool
isKeyInVisualRange(const TileKey & key) const955 TerrainLayer::isKeyInVisualRange(const TileKey& key) const
956 {
957     if (!key.valid())
958     {
959         return false;
960     }
961 
962     // We must use the equivalent lod b/c the input key can be in any profile.
963     unsigned localLOD = getProfile() ?
964         getProfile()->getEquivalentLOD(key.getProfile(), key.getLOD()) :
965         key.getLOD();
966 
967 
968     // First check the key against the min/max level limits, it they are set.
969     if ((options().maxLevel().isSet() && localLOD > options().maxLevel().value()) ||
970         (options().minLevel().isSet() && localLOD < options().minLevel().value()))
971     {
972         return false;
973     }
974 
975     // Next, check against resolution limits (based on the source tile size).
976     if (options().minResolution().isSet() || options().maxResolution().isSet())
977     {
978         const Profile* profile = getProfile();
979         if (profile)
980         {
981             // calculate the resolution in the layer's profile, which can
982             // be different that the key's profile.
983             double resKey = key.getExtent().width() / (double)getTileSize();
984             double resLayer = key.getProfile()->getSRS()->transformUnits(resKey, profile->getSRS());
985 
986             if (options().maxResolution().isSet() &&
987                 options().maxResolution().value() > resLayer)
988             {
989                 return false;
990             }
991 
992             if (options().minResolution().isSet() &&
993                 options().minResolution().value() < resLayer)
994             {
995                 return false;
996             }
997         }
998     }
999 
1000     return true;
1001 }
1002 
1003 bool
isCached(const TileKey & key) const1004 TerrainLayer::isCached(const TileKey& key) const
1005 {
1006     // first consult the policy:
1007     if (getCacheSettings()->isCacheDisabled())
1008         return false;
1009 
1010     else if (getCacheSettings()->cachePolicy()->isCacheOnly())
1011         return true;
1012 
1013     // next check for a bin:
1014     CacheBin* bin = const_cast<TerrainLayer*>(this)->getCacheBin( key.getProfile() );
1015     if ( !bin )
1016         return false;
1017 
1018     return bin->getRecordStatus( key.str() ) == CacheBin::STATUS_OK;
1019 }
1020 
1021 void
setReadOptions(const osgDB::Options * readOptions)1022 TerrainLayer::setReadOptions(const osgDB::Options* readOptions)
1023 {
1024     // clone the options, or create it not set
1025     _readOptions = Registry::cloneOrCreateOptions(readOptions);
1026     //Layer::setReadOptions(readOptions);
1027 
1028     // store HTTP proxy settings in the options:
1029     storeProxySettings( _readOptions.get() );
1030 
1031     // store the referrer for relative-path resolution
1032     URIContext( options().referrer() ).store( _readOptions.get() );
1033 
1034     Threading::ScopedMutexLock lock(_mutex);
1035     _cacheSettings = new CacheSettings();
1036     _cacheBinMetadata.clear();
1037 }
1038 
1039 std::string
getCacheID() const1040 TerrainLayer::getCacheID() const
1041 {
1042     return _runtimeCacheId;
1043 }
1044 
1045 const DataExtentList&
getDataExtents() const1046 TerrainLayer::getDataExtents() const
1047 {
1048     if (!_dataExtents.empty())
1049     {
1050         return _dataExtents;
1051     }
1052 
1053     else if (!_cacheBinMetadata.empty())
1054     {
1055         // There are extents in the cache bin, so use those.
1056         // The DE's are the same regardless of profile so just use the first one in there.
1057         return _cacheBinMetadata.begin()->second->_dataExtents;
1058     }
1059 
1060     else
1061     {
1062         return _dataExtents;
1063     }
1064 }
1065 
1066 DataExtentList&
dataExtents()1067 TerrainLayer::dataExtents()
1068 {
1069     return const_cast<DataExtentList&>(getDataExtents());
1070 }
1071 
1072 void
dirtyDataExtents()1073 TerrainLayer::dirtyDataExtents()
1074 {
1075     Threading::ScopedMutexLock lock(_mutex);
1076     _dataExtentsUnion = GeoExtent::INVALID;
1077 }
1078 
1079 const GeoExtent&
getDataExtentsUnion() const1080 TerrainLayer::getDataExtentsUnion() const
1081 {
1082     const DataExtentList& de = getDataExtents();
1083 
1084     if (_dataExtentsUnion.isInvalid() && !de.empty())
1085     {
1086         Threading::ScopedMutexLock lock(_mutex);
1087         {
1088             if (_dataExtentsUnion.isInvalid() && !de.empty()) // double-check
1089             {
1090                 GeoExtent e(de[0]);
1091                 for (unsigned int i = 1; i < de.size(); i++)
1092                 {
1093                     e.expandToInclude(de[i]);
1094                 }
1095                 _dataExtentsUnion = e;
1096             }
1097         }
1098     }
1099     return _dataExtentsUnion;
1100 }
1101 
1102 const GeoExtent&
getExtent() const1103 TerrainLayer::getExtent() const
1104 {
1105     return getDataExtentsUnion();
1106 }
1107 
1108 void
storeProxySettings(osgDB::Options * readOptions)1109 TerrainLayer::storeProxySettings(osgDB::Options* readOptions)
1110 {
1111     //Store the proxy settings in the options structure.
1112     if (options().proxySettings().isSet())
1113     {
1114         options().proxySettings()->apply( readOptions );
1115     }
1116 }
1117 
1118 SequenceControl*
getSequenceControl()1119 TerrainLayer::getSequenceControl()
1120 {
1121     return dynamic_cast<SequenceControl*>( getTileSource() );
1122 }
1123 
1124 TileKey
getBestAvailableTileKey(const TileKey & key) const1125 TerrainLayer::getBestAvailableTileKey(const TileKey& key) const
1126 {
1127     // trivial reject
1128     if ( !key.valid() )
1129         return TileKey::INVALID;
1130 
1131     unsigned MDL = options().maxDataLevel().get();
1132 
1133     // We must use the equivalent lod b/c the input key can be in any profile.
1134     unsigned localLOD = getProfile() ?
1135         getProfile()->getEquivalentLOD(key.getProfile(), key.getLOD()) :
1136         key.getLOD();
1137 
1138     // Check against level extrema:
1139     if (localLOD < options().minLevel().get() || localLOD > options().maxLevel().get())
1140     {
1141         return TileKey::INVALID;
1142     }
1143 
1144     // Next, check against resolution limits (based on the source tile size).
1145     if (options().minResolution().isSet() || options().maxResolution().isSet())
1146     {
1147         const Profile* profile = getProfile();
1148         if ( profile )
1149         {
1150             // calculate the resolution in the layer's profile, which can
1151             // be different that the key's profile.
1152             double resKey   = key.getExtent().width() / (double)getTileSize();
1153             double resLayer = key.getProfile()->getSRS()->transformUnits(resKey, profile->getSRS());
1154 
1155             if (options().maxResolution().isSet() &&
1156                 options().maxResolution().value() > resLayer)
1157             {
1158                 return TileKey::INVALID;
1159             }
1160 
1161             if (options().minResolution().isSet() &&
1162                 options().minResolution().value() < resLayer)
1163             {
1164                 return TileKey::INVALID;
1165             }
1166         }
1167     }
1168 
1169     // Next check against the data extents.
1170     const DataExtentList& de = getDataExtents();
1171 
1172     // If we have mo data extents available, just return the MDL-limited input key.
1173     if (de.empty())
1174     {
1175         return localLOD > MDL ? key.createAncestorKey(MDL) : key;
1176     }
1177 
1178     // Transform the key's extent to the layer's extent
1179     GeoExtent localKeyExtent = getProfile()->clampAndTransformExtent(key.getExtent());
1180 
1181     // Reject if the extents don't overlap at all.
1182     if (!getDataExtentsUnion().intersects(localKeyExtent))
1183     {
1184         return TileKey::INVALID;
1185     }
1186 
1187     bool     intersects = false;
1188     unsigned highestLOD = 0;
1189 
1190     // Check each data extent in turn:
1191     for (DataExtentList::const_iterator itr = de.begin(); itr != de.end(); ++itr)
1192     {
1193         // check for 2D intersection:
1194         if (itr->intersects(localKeyExtent))
1195         {
1196             // check that the extent isn't higher-resolution than our key:
1197             if ( !itr->minLevel().isSet() || localLOD >= (int)itr->minLevel().get() )
1198             {
1199                 // Got an intersetion; now test the LODs:
1200                 intersects = true;
1201 
1202                 // Is the high-LOD set? If not, there's not enough information
1203                 // so just assume our key might be good.
1204                 if ( itr->maxLevel().isSet() == false )
1205                 {
1206                     return localLOD > MDL ? key.createAncestorKey(MDL) : key;
1207                 }
1208 
1209                 // Is our key at a lower or equal LOD than the max key in this extent?
1210                 // If so, our key is good.
1211                 else if ( localLOD <= (int)itr->maxLevel().get() )
1212                 {
1213                     return localLOD > MDL ? key.createAncestorKey(MDL) : key;
1214                 }
1215 
1216                 // otherwise, record the highest encountered LOD that
1217                 // intersects our key.
1218                 else if ( itr->maxLevel().get() > highestLOD )
1219                 {
1220                     highestLOD = itr->maxLevel().get();
1221                 }
1222             }
1223         }
1224     }
1225 
1226     if ( intersects )
1227     {
1228         return key.createAncestorKey(osg::minimum(key.getLOD(), osg::minimum(highestLOD, MDL)));
1229     }
1230 
1231     return TileKey::INVALID;
1232 }
1233 
1234 bool
mayHaveData(const TileKey & key) const1235 TerrainLayer::mayHaveData(const TileKey& key) const
1236 {
1237     return key == getBestAvailableTileKey(key);
1238 }
1239 
1240 unsigned
getTileSize() const1241 TerrainLayer::getTileSize() const
1242 {
1243     return getTileSource() ? getTileSource()->getPixelsPerTile() : options().tileSize().get();
1244 }
1245 
1246 float
getNoDataValue() const1247 TerrainLayer::getNoDataValue() const
1248 {
1249     return getTileSource() ? getTileSource()->getNoDataValue() : options().noDataValue().get();
1250 }
1251 
1252 float
getMinValidValue() const1253 TerrainLayer::getMinValidValue() const
1254 {
1255     return getTileSource() ? getTileSource()->getMinValidValue() : options().minValidValue().get();
1256 }
1257 
1258 float
getMaxValidValue() const1259 TerrainLayer::getMaxValidValue() const
1260 {
1261     return getTileSource() ? getTileSource()->getMaxValidValue() : options().maxValidValue().get();
1262 }
1263