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