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/LandCoverLayer>
20 #include <osgEarth/Registry>
21 #include <osgEarth/Map>
22 #include <osgEarth/SimplexNoise>
23 #include <osgEarth/Progress>
24
25 using namespace osgEarth;
26
27 #define LC "[LandCoverLayer] "
28
29 REGISTER_OSGEARTH_LAYER(land_cover, LandCoverLayer);
30
31 namespace
32 {
getSplatCoords(const TileKey & key,float baseLOD,const osg::Vec2 & covUV)33 osg::Vec2 getSplatCoords(const TileKey& key, float baseLOD, const osg::Vec2& covUV)
34 {
35 osg::Vec2 out;
36
37 float dL = (float)key.getLOD() - baseLOD;
38 float factor = pow(2.0f, dL);
39 float invFactor = 1.0/factor;
40 out.set( covUV.x()*invFactor, covUV.y()*invFactor );
41
42 // For upsampling we need to calculate an offset as well
43 if ( factor >= 1.0 )
44 {
45 unsigned wide, high;
46 key.getProfile()->getNumTiles(key.getLOD(), wide, high);
47
48 float tileX = (float)key.getTileX();
49 float tileY = (float)(wide-1-key.getTileY()); // swap Y. (not done in the shader version.)
50
51 osg::Vec2 a( floor(tileX*invFactor), floor(tileY*invFactor) );
52 osg::Vec2 b( a.x()*factor, a.y()*factor );
53 osg::Vec2 c( (a.x()+1.0f)*factor, (a.y()+1.0f)*factor );
54 osg::Vec2 offset( (tileX-b.x())/(c.x()-b.x()), (tileY-b.y())/(c.y()-b.y()) );
55
56 out += offset;
57 }
58
59 return out;
60 }
61
warpCoverageCoords(const osg::Vec2 & covIn,float noise,float warp)62 osg::Vec2 warpCoverageCoords(const osg::Vec2& covIn, float noise, float warp)
63 {
64 float n1 = 2.0 * noise - 1.0;
65 return osg::Vec2(
66 covIn.x() + sin(n1*osg::PI*2.0) * warp,
67 covIn.y() + sin(n1*osg::PI*2.0) * warp);
68 }
69
getNoise(SimplexNoise & noiseGen,const osg::Vec2 & uv)70 float getNoise(SimplexNoise& noiseGen, const osg::Vec2& uv)
71 {
72 // TODO: check that u and v are 0..s and not 0..s-1
73 double n = noiseGen.getTiledValue(uv.x(), uv.y());
74 n = osg::clampBetween(n, 0.0, 1.0);
75 return n;
76 }
77
78
79 typedef std::vector<int> CodeMap;
80
81 struct ILayer
82 {
83 GeoImage image;
84 float scale;
85 osg::Vec2 bias;
86 bool valid;
87 float warp;
88 ImageUtils::PixelReader* read;
89 unsigned* codeTable;
90 unsigned loads;
91
ILayer__anon749ad3880111::ILayer92 ILayer() : valid(true), read(0L), scale(1.0f), warp(0.0f), loads(0) { }
93
~ILayer__anon749ad3880111::ILayer94 ~ILayer() { if (read) delete read; }
95
load__anon749ad3880111::ILayer96 void load(const TileKey& key, LandCoverCoverageLayer* sourceLayer, ProgressCallback* progress)
97 {
98 if (sourceLayer->getEnabled() &&
99 sourceLayer->isKeyInLegalRange(key) &&
100 sourceLayer->mayHaveData(key))
101 {
102 for(TileKey k = key; k.valid() && !image.valid(); k = k.createParentKey())
103 {
104 image = sourceLayer->createImage(k, progress);
105
106 // check for cancelation:
107 if (progress && progress->isCanceled())
108 {
109 break;
110 }
111 }
112 }
113
114 valid = image.valid();
115
116 if ( valid )
117 {
118 scale = key.getExtent().width() / image.getExtent().width();
119 bias.x() = (key.getExtent().xMin() - image.getExtent().xMin()) / image.getExtent().width();
120 bias.y() = (key.getExtent().yMin() - image.getExtent().yMin()) / image.getExtent().height();
121
122 read = new ImageUtils::PixelReader(image.getImage());
123
124 // cannot interpolate coverage data:
125 read->setBilinear( false );
126
127 warp = sourceLayer->options().warp().get();
128 }
129 }
130 };
131
132 // Constructs a code map (int to int) for a coverage layer. We will use this
133 // code map to map coverage layer codes to dictionary codes.
buildCodeMap(const LandCoverCoverageLayer * coverage,CodeMap & codemap)134 void buildCodeMap(const LandCoverCoverageLayer* coverage, CodeMap& codemap)
135 {
136 if (!coverage) {
137 OE_WARN << LC << "ILLEGAL: coverage not passed to buildCodeMap\n";
138 return;
139 }
140 if (!coverage->getDictionary()) {
141 OE_WARN << LC << "ILLEGAL: coverage dictionary not set in buildCodeMap\n";
142 return;
143 }
144
145 int highestValue = 0;
146
147 for (LandCoverValueMappingVector::const_iterator k = coverage->getMappings().begin();
148 k != coverage->getMappings().end();
149 ++k)
150 {
151 const LandCoverValueMapping* mapping = k->get();
152 int value = mapping->getValue();
153 if (value > highestValue)
154 highestValue = value;
155 }
156
157 codemap.assign(highestValue+1, -1);
158
159 for (LandCoverValueMappingVector::const_iterator k = coverage->getMappings().begin();
160 k != coverage->getMappings().end();
161 ++k)
162 {
163 const LandCoverValueMapping* mapping = k->get();
164 int value = mapping->getValue();
165 const LandCoverClass* lcClass = coverage->getDictionary()->getClassByName(mapping->getLandCoverClassName());
166 if (lcClass)
167 {
168 codemap[value] = lcClass->getValue();
169 }
170 }
171 }
172
173 typedef std::vector<osg::ref_ptr<LandCoverCoverageLayer> > LandCoverCoverageLayerVector;
174
175 /*
176 * TileSource that provides GeoImage's to the LandCoverLayer.
177 */
178 class LandCoverTileSource : public TileSource
179 {
180 public:
181 LandCoverTileSource(const LandCoverLayerOptions& options);
182
183 public: // TileSource
184 Status initialize(const osgDB::Options* readOptions);
185
186 osg::Image* createImage(const TileKey& key, ProgressCallback* progress);
187
getCachePolicyHint() const188 CachePolicy getCachePolicyHint() const {
189 return CachePolicy::NO_CACHE;
190 }
191
192 const LandCoverLayerOptions* _options;
options() const193 const LandCoverLayerOptions& options() const { return *_options; }
194
195 void setDictionary(LandCoverDictionary*);
196
197 // image layers, one per data source
198 LandCoverCoverageLayerVector _coverages;
199
200 // code maps (vector index is the source code; value is the destination code)
201 std::vector<CodeMap> _codemaps;
202
203 // todo
204 std::vector<float> _warps;
205
206 osg::ref_ptr<osgDB::Options> _readOptions;
207
208 osg::ref_ptr<LandCoverDictionary> _lcDictionary;
209
210 friend class LandCoverLayer;
211 };
212
213
LandCoverTileSource(const LandCoverLayerOptions & options)214 LandCoverTileSource::LandCoverTileSource(const LandCoverLayerOptions& options) :
215 TileSource(options),
216 _options(&options)
217 {
218 // Increase the L2 cache size since the parent LandCoverLayer is going to be
219 // using meta-tiling to create mosaics for warping
220 setDefaultL2CacheSize(64);
221 }
222
223 Status
initialize(const osgDB::Options * readOptions)224 LandCoverTileSource::initialize(const osgDB::Options* readOptions)
225 {
226 const Profile* profile = getProfile();
227 if ( !profile )
228 {
229 profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile();
230 setProfile( profile );
231 }
232
233 for(unsigned i=0; i<options().coverages().size(); ++i)
234 {
235 LandCoverCoverageLayerOptions coverageOptions = options().coverages()[i];
236 if (coverageOptions.enabled() == false)
237 continue;
238
239 // We never want to cache data from a coverage, because the "parent" layer
240 // will be caching the entire result of a multi-coverage composite.
241 coverageOptions.cachePolicy() = CachePolicy::NO_CACHE;
242
243 // Create the coverage layer:
244 LandCoverCoverageLayer* layer = new LandCoverCoverageLayer( coverageOptions );
245
246 // Set up and open it.
247 layer->setTargetProfileHint( profile );
248 layer->setReadOptions(readOptions);
249 const Status& s = layer->open();
250 if (s.isOK())
251 {
252 _coverages.push_back(layer);
253 _codemaps.resize(_codemaps.size()+1);
254 OE_INFO << LC << "Opened coverage \"" << layer->getName() << "\"\n";
255 }
256 else
257 {
258 OE_WARN << "Layer \"" << layer->getName() << "\": " << s.toString() << std::endl;
259 }
260
261 // Integrate data extents into this tile source.
262 const DataExtentList& de = layer->getDataExtents();
263 for(DataExtentList::const_iterator dei = de.begin(); dei != de.end(); ++dei)
264 {
265 if (!profile || dei->getSRS()->isHorizEquivalentTo(profile->getSRS()))
266 {
267 getDataExtents().push_back(*dei);
268 }
269 else
270 {
271 // Transform the data extents to the layer profile
272 GeoExtent ep = dei->transform(profile->getSRS());
273 DataExtent de(ep);
274 if (dei->minLevel().isSet())
275 de.minLevel() = profile->getEquivalentLOD(layer->getProfile(), dei->minLevel().get());
276 if (dei->maxLevel().isSet())
277 de.maxLevel() = profile->getEquivalentLOD(layer->getProfile(), dei->maxLevel().get());
278 getDataExtents().push_back(de);
279 }
280 }
281 }
282
283 return STATUS_OK;
284 }
285
286 void
setDictionary(LandCoverDictionary * lcd)287 LandCoverTileSource::setDictionary(LandCoverDictionary* lcd)
288 {
289 _lcDictionary = lcd;
290
291 for (unsigned i = 0; i<_coverages.size(); ++i)
292 {
293 _coverages[i]->setDictionary(lcd);
294 buildCodeMap(_coverages[i].get(), _codemaps[i]);
295 }
296 }
297
298 //TODO: overriding createImage directly like this will bypass caching.
299 // This is a temporary solution; need to refactor.
300 osg::Image*
createImage(const TileKey & key,ProgressCallback * progress)301 LandCoverTileSource::createImage(const TileKey& key, ProgressCallback* progress)
302 {
303 if ( _coverages.empty() )
304 return 0L;
305
306 std::vector<ILayer> layers(_coverages.size());
307
308 // Allocate the new coverage image; it will contain unnormalized values.
309 osg::ref_ptr<osg::Image> out = new osg::Image();
310 ImageUtils::markAsUnNormalized(out.get(), true);
311
312 // Allocate a suitable format:
313 GLint internalFormat = GL_R16F;
314
315 int tilesize = getPixelsPerTile();
316
317 out->allocateImage(tilesize, tilesize, 1, GL_RGB, GL_FLOAT);
318 out->setInternalTextureFormat(internalFormat);
319
320 osg::Vec2 cov; // coverage coordinates
321
322 ImageUtils::PixelWriter write( out.get() );
323
324 float du = 1.0f / (float)(out->s()-1);
325 float dv = 1.0f / (float)(out->t()-1);
326
327 osg::Vec4 nodata(NO_DATA_VALUE, NO_DATA_VALUE, NO_DATA_VALUE, NO_DATA_VALUE);
328
329 unsigned pixelsWritten = 0u;
330
331 for(float u=0.0f; u<=1.0f; u+=du)
332 {
333 for(float v=0.0f; v<=1.0f; v+=dv)
334 {
335 bool wrotePixel = false;
336 for(int L = layers.size()-1; L >= 0 && !wrotePixel; --L)
337 {
338 if (progress && progress->isCanceled())
339 {
340 OE_DEBUG << LC << key.str() << " canceled" << std::endl;
341 return 0L;
342 }
343
344 ILayer& layer = layers[L];
345 if ( !layer.valid )
346 continue;
347
348 if ( !layer.image.valid() )
349 layer.load(key, _coverages[L].get(), progress);
350
351 if (!layer.valid)
352 continue;
353
354 osg::Vec2 cov(layer.scale*u + layer.bias.x(), layer.scale*v + layer.bias.y());
355
356 if ( cov.x() >= 0.0f && cov.x() <= 1.0f && cov.y() >= 0.0f && cov.y() <= 1.0f )
357 {
358 osg::Vec4 texel = (*layer.read)(cov.x(), cov.y());
359
360 if ( texel.r() != NO_DATA_VALUE )
361 {
362 // store the warp factor in the green channel
363 texel.g() = layer.warp;
364
365 // store the layer index in the blue channel
366 texel.b() = (float)L;
367
368 if (texel.r() < 1.0f)
369 {
370 // normalized code; convert
371 int code = (int)(texel.r()*255.0f);
372 if (code < _codemaps[L].size())
373 {
374 int value = _codemaps[L][code];
375 if (value >= 0)
376 {
377 texel.r() = (float)value;
378 write.f(texel, u, v);
379 wrotePixel = true;
380 pixelsWritten++;
381 }
382 }
383 }
384 else
385 {
386 // unnormalized
387 int code = (int)texel.r();
388 if (code < _codemaps[L].size() && _codemaps[L][code] >= 0)
389 {
390 texel.r() = (float)_codemaps[L][code];
391 write.f(texel, u, v);
392 wrotePixel = true;
393 pixelsWritten++;
394 }
395 }
396 }
397 }
398 }
399
400 if ( !wrotePixel )
401 {
402 write.f(nodata, u, v);
403 }
404 }
405 }
406
407 return pixelsWritten > 0u? out.release() : 0L;
408 }
409 }
410
411 //........................................................................
412
413 #undef LC
414 #define LC "[LandCoverLayerOptions] "
415
LandCoverLayerOptions(const ConfigOptions & options)416 LandCoverLayerOptions::LandCoverLayerOptions(const ConfigOptions& options) :
417 ImageLayerOptions(options),
418 _noiseLOD(12u),
419 _warp(0.0f)
420 {
421 fromConfig(_conf);
422 }
423
424 void
fromConfig(const Config & conf)425 LandCoverLayerOptions::fromConfig(const Config& conf)
426 {
427 conf.get("warp", _warp);
428 conf.get("noise_lod", _noiseLOD);
429
430 ConfigSet layerConfs = conf.child("coverages").children("coverage");
431 for (ConfigSet::const_iterator i = layerConfs.begin(); i != layerConfs.end(); ++i)
432 {
433 _coverages.push_back(LandCoverCoverageLayerOptions(*i));
434 }
435 }
436
437 Config
getConfig() const438 LandCoverLayerOptions::getConfig() const
439 {
440 Config conf = ImageLayerOptions::getConfig();
441
442 conf.set("warp", _warp);
443 conf.set("noise_lod", _noiseLOD);
444
445 if (_coverages.size() > 0)
446 {
447 Config images("coverages");
448 for (std::vector<LandCoverCoverageLayerOptions>::const_iterator i = _coverages.begin();
449 i != _coverages.end();
450 ++i)
451 {
452 images.add("coverage", i->getConfig());
453 }
454 conf.set(images);
455 }
456
457 return conf;
458 }
459
460 //...........................................................................
461
462 #undef LC
463 #define LC "[LandCoverLayer] "
464
465
LandCoverLayer()466 LandCoverLayer::LandCoverLayer() :
467 ImageLayer(&_optionsConcrete),
468 _options(&_optionsConcrete)
469 {
470 init();
471 }
472
LandCoverLayer(const LandCoverLayerOptions & options)473 LandCoverLayer::LandCoverLayer(const LandCoverLayerOptions& options) :
474 ImageLayer(&_optionsConcrete),
475 _options(&_optionsConcrete),
476 _optionsConcrete(options)
477 {
478 init();
479 }
480
481 void
init()482 LandCoverLayer::init()
483 {
484 options().coverage() = true;
485 options().visible() = false;
486 options().shared() = true;
487 ImageLayer::init();
488 }
489
490 void
addedToMap(const Map * map)491 LandCoverLayer::addedToMap(const Map* map)
492 {
493 // Find a land cover dictionary if there is one.
494 // There had better be one, or we are not going to get very far!
495 // This is called after createTileSource, so the TileSource should exist at this point.
496 // Note. If the land cover dictionary isn't already in the Map...this will fail! (TODO)
497 // Consider a LayerListener. (TODO)
498 _lcDictionary = map->getLayer<LandCoverDictionary>();
499 if (_lcDictionary.valid() && getTileSource())
500 {
501 static_cast<LandCoverTileSource*>(getTileSource())->setDictionary(_lcDictionary.get());
502 }
503 else
504 {
505 OE_WARN << LC << "Did not find a LandCoverDictionary in the Map!\n";
506 }
507 }
508
509 TileSource*
createTileSource()510 LandCoverLayer::createTileSource()
511 {
512 return new LandCoverTileSource(options());
513 }
514
515 bool
readMetaImage(MetaImage & metaImage,const TileKey & key,double u,double v,osg::Vec4f & output,ProgressCallback * progress)516 LandCoverLayer::readMetaImage(MetaImage& metaImage, const TileKey& key, double u, double v, osg::Vec4f& output, ProgressCallback* progress)
517 {
518 // Find the key containing the uv coordinates.
519 int x = int(floor(u));
520 int y = int(floor(v));
521 TileKey actualKey = (x != 0 || y != 0)? key.createNeighborKey(x, -y) : key;
522
523 if (actualKey.valid())
524 {
525 // Transform the uv to be relative to the tile it's actually in.
526 // Don't forget: stupid computer requires us to handle negatives by hand.
527 u = u >= 0.0 ? fmod(u, 1.0) : 1.0+fmod(u, 1.0);
528 v = v >= 0.0 ? fmod(v, 1.0) : 1.0+fmod(v, 1.0);
529
530 MetaImageComponent* comp = 0L;
531
532 MetaImage::iterator i = metaImage.find(actualKey);
533 if (i != metaImage.end())
534 {
535 comp = &i->second;
536 }
537 else
538 {
539 // compute the closest ancestor key with actual data for the neighbor key:
540 TileKey bestkey = getBestAvailableTileKey(actualKey);
541
542 // should not need this fallback loop but let's do it anyway
543 GeoImage tile;
544 while (bestkey.valid() && !tile.valid())
545 {
546 // load the image and store it to the metaimage.
547 tile = ImageLayer::createImageImplementation(bestkey, progress);
548 if (tile.valid())
549 {
550 comp = &metaImage[actualKey];
551 comp->image = tile.getImage();
552 actualKey.getExtent().createScaleBias(bestkey.getExtent(), comp->scaleBias);
553 comp->pixel.setImage(comp->image.get());
554 comp->pixel.setBilinear(false);
555 }
556 else
557 {
558 bestkey = bestkey.createParentKey();
559 }
560
561 if (progress && progress->isCanceled())
562 return false;
563 }
564 }
565
566 if (comp)
567 {
568 // scale/bias to this tile's extent and sample the image.
569 u = u * comp->scaleBias(0, 0) + comp->scaleBias(3, 0);
570 v = v * comp->scaleBias(1, 1) + comp->scaleBias(3, 1);
571 output = comp->pixel(u, v);
572 return true;
573 }
574 }
575 return false;
576 }
577
578 GeoImage
createImageImplementation(const TileKey & key,ProgressCallback * progress)579 LandCoverLayer::createImageImplementation(const TileKey& key, ProgressCallback* progress)
580 {
581 MetaImage metaImage;
582
583 // Grab a test sample to see what the output parameters should be:
584 osg::Vec4f dummy;
585 if (!readMetaImage(metaImage, getBestAvailableTileKey(key), 0.5, 0.5, dummy, progress))
586 return GeoImage::INVALID;
587 osg::Image* mainImage = metaImage.begin()->second.image.get();
588
589 // Allocate the output image:
590 osg::ref_ptr<osg::Image> output = new osg::Image();
591 output->allocateImage(
592 mainImage->s(),
593 mainImage->t(),
594 mainImage->r(),
595 mainImage->getPixelFormat(),
596 mainImage->getDataType(),
597 mainImage->getPacking());
598 output->setInternalTextureFormat(mainImage->getInternalTextureFormat());
599 ImageUtils::markAsUnNormalized(output.get(), true);
600 ImageUtils::PixelWriter write(output.get());
601
602 // Configure the noise function:
603 SimplexNoise noiseGen;
604 noiseGen.setNormalize(true);
605 noiseGen.setRange(0.0, 1.0);
606 noiseGen.setFrequency(4.0);
607 noiseGen.setPersistence(0.8);
608 noiseGen.setLacunarity(2.2);
609 noiseGen.setOctaves(8);
610
611 osg::Vec2d cov;
612 osg::Vec2 noiseCoords;
613 osg::Vec4 pixel;
614 osg::Vec4 warpedPixel;
615 osg::Vec4 nodata(NO_DATA_VALUE, NO_DATA_VALUE, NO_DATA_VALUE, NO_DATA_VALUE);
616
617 // scales the key's LOD to the noise function's LOD:
618 float pdL = pow(2, (float)key.getLOD() - options().noiseLOD().get());
619
620 // loop over the pixels and write each one.
621 for (int t = 0; t < output->t(); ++t)
622 {
623 double v = (double)t / (double)(output->t() - 1);
624 for (int s = 0; s < output->s(); ++s)
625 {
626 double u = (double)s / (double)(output->s() - 1);
627
628 // coverage sampling coordinates:
629 cov.set(u, v);
630
631 // first read the unwarped pixel to get the warping value.
632 // (warp is stored in pixel.g)
633 bool wrotePixel = false;
634 if (readMetaImage(metaImage, key, u, v, pixel, progress) &&
635 pixel.g() != NO_DATA_VALUE)
636 {
637 float warp = pixel.g() * pdL;
638 if (warp != 0.0)
639 {
640 // use the noise function to warp the uv coordinates:
641 noiseCoords = getSplatCoords(key, options().noiseLOD().get(), cov);
642 double noise = getNoise(noiseGen, noiseCoords);
643 cov = warpCoverageCoords(cov, noise, warp);
644
645 // now read the pixel at the warped location:
646 if (readMetaImage(metaImage, key, cov.x(), cov.y(), warpedPixel, progress) &&
647 warpedPixel.b() != NO_DATA_VALUE)
648 {
649 // only apply the warping if the location of the warped pixel
650 // came from the same source layer. Otherwise you will get some
651 // unsavory speckling. (Layer index is stored in pixel.b)
652 if (pixel.b() == warpedPixel.b())
653 write(warpedPixel, s, t);
654 else
655 write(pixel, s, t);
656
657 wrotePixel = true;
658 }
659 }
660 else
661 {
662 write(pixel, s, t);
663 wrotePixel = true;
664 }
665 }
666
667 // if we didn't get a coverage value, just write NODATA
668 if (!wrotePixel)
669 {
670 write(nodata, s, t);
671 }
672
673 if (progress && progress->isCanceled())
674 {
675 OE_DEBUG << LC << key.str() << " canceled" << std::endl;
676 return GeoImage::INVALID;
677 }
678 }
679 }
680
681 return GeoImage(output.get(), key.getExtent());
682 }
683
684 const LandCoverClass*
getClassByUV(const GeoImage & tile,double u,double v) const685 LandCoverLayer::getClassByUV(const GeoImage& tile, double u, double v) const
686 {
687 if (!tile.valid())
688 return 0L;
689
690 if (!_lcDictionary.valid())
691 return 0L;
692
693 ImageUtils::PixelReader read(tile.getImage());
694 read.setBilinear(false); // nearest neighbor only!
695 float value = read(u, v).r();
696
697 return _lcDictionary->getClassByValue((int)value);
698 }
699