Skip to content

Commit

Permalink
TMS: port the XYZ elevation_encoding, stitch_edges, and interpolation…
Browse files Browse the repository at this point in the history
… support to TMSElevationLayer
  • Loading branch information
gwaldron committed Jan 13, 2025
1 parent 02d8d6a commit e94c533
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 37 deletions.
57 changes: 38 additions & 19 deletions src/osgEarth/TMS
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/

#ifndef OSGEARTH_TMS_H
#define OSGEARTH_TMS_H
#pragma once

#include <osgEarth/Common>
#include <osgEarth/ImageLayer>
#include <osgEarth/ElevationLayer>
#include <osgEarth/URI>
#include <osgEarth/Containers>

/**
* TMS (TileMapService)
Expand Down Expand Up @@ -539,7 +538,7 @@ namespace osgEarth
class OSGEARTH_EXPORT Options : public ImageLayer::Options, public TMS::Options {
public:
META_LayerOptions(osgEarth, Options, ImageLayer::Options);
virtual Config getConfig() const;
Config getConfig() const override;
private:
void fromConfig(const Config& conf);
};
Expand All @@ -564,21 +563,21 @@ namespace osgEarth
public: // Layer

//! Establishes a connection to the TMS repository
virtual Status openImplementation() override;
virtual Status closeImplementation() override;
Status openImplementation() override;
Status closeImplementation() override;

//! Creates a raster image for the given tile key
virtual GeoImage createImageImplementation(const TileKey& key, ProgressCallback* progress) const override;
GeoImage createImageImplementation(const TileKey& key, ProgressCallback* progress) const override;

//! Writes a raster image for he given tile key (if open for writing)
virtual Status writeImageImplementation(const TileKey& key, const osg::Image* image, ProgressCallback* progress) const override;
Status writeImageImplementation(const TileKey& key, const osg::Image* image, ProgressCallback* progress) const override;

protected: // Layer

//! Called by constructors
virtual void init() override;
void init() override;

virtual bool isWritingSupported() const override { return true; }
bool isWritingSupported() const override { return true; }

protected:

Expand All @@ -600,7 +599,10 @@ namespace osgEarth
class OSGEARTH_EXPORT Options : public ElevationLayer::Options, public TMS::Options {
public:
META_LayerOptions(osgEarth, Options, ElevationLayer::Options);
virtual Config getConfig() const;
OE_OPTION(std::string, elevationEncoding, {});
OE_OPTION(bool, stitchEdges, false);
OE_OPTION(RasterInterpolation, interpolation, INTERP_BILINEAR);
Config getConfig() const override;
private:
void fromConfig(const Config& conf);
};
Expand All @@ -621,24 +623,42 @@ namespace osgEarth
void setFormat(const std::string& value);
const std::string& getFormat() const;

//! Encoding encoding type
//! @param value Encoding type, one of "" (default), "mapbox", "terrarium"
void setElevationEncoding(const std::string& value);
const std::string& getElevationEncoding() const;

//! Whether to stitch the edges of abutting tiles together to prevent gaps.
//! Some elevation data is image based, and the side of one tile does not match
//! the value on the correspond side of the abutting tile. This option will
//! resample the data to make the edges match, for a small performance hit.
//! @param value True to stitch edges (default is false)
void setStitchEdges(const bool& value);
const bool& getStitchEdges() const;

//! Interpolation method to use when stitching edges = true.
//! Default is INTERP_BILINEAR.
void setInterpolation(const RasterInterpolation& value);
const RasterInterpolation& getInterpolation() const;

public: // Layer

//! Establishes a connection to the TMS repository
virtual Status openImplementation() override;
virtual Status closeImplementation() override;
Status openImplementation() override;
Status closeImplementation() override;

//! Creates a heightfield for the given tile key
virtual GeoHeightField createHeightFieldImplementation(const TileKey& key, ProgressCallback* progress) const override;
GeoHeightField createHeightFieldImplementation(const TileKey& key, ProgressCallback* progress) const override;

//! Writes a raster image for he given tile key (if open for writing)
virtual Status writeHeightFieldImplementation(const TileKey& key, const osg::HeightField* hf, ProgressCallback* progress) const override;
Status writeHeightFieldImplementation(const TileKey& key, const osg::HeightField* hf, ProgressCallback* progress) const override;

protected: // Layer

//! Called by constructors
virtual void init() override;
void init() override;

virtual bool isWritingSupported() const override { return true; }
bool isWritingSupported() const override { return true; }

protected:

Expand All @@ -647,11 +667,10 @@ namespace osgEarth

private:
osg::ref_ptr<TMSImageLayer> _imageLayer;
mutable Util::LRUCache<TileKey, GeoImage> _stitchingCache{ true, 64 };
};

} // namespace osgEarth

OSGEARTH_SPECIALIZE_CONFIG(osgEarth::TMSImageLayer::Options);
OSGEARTH_SPECIALIZE_CONFIG(osgEarth::TMSElevationLayer::Options);

#endif // OSGEARTH_TMS_H
138 changes: 120 additions & 18 deletions src/osgEarth/TMS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#include "TMS"
#include <osgEarth/Registry>
#include <osgEarth/FileUtils>
#include <osgEarth/XmlUtils>
#include <osgEarth/LandCover>
#include <osgEarth/ImageToHeightFieldConverter>
#include "Registry"
#include "FileUtils"
#include "XmlUtils"
#include "LandCover"
#include "ImageToHeightFieldConverter"
#include "MetaTile"
#include <osgDB/FileUtils>

using namespace osgEarth;
Expand Down Expand Up @@ -1237,13 +1238,23 @@ TMSElevationLayer::Options::getConfig() const
{
Config conf = ElevationLayer::Options::getConfig();
writeTo(conf);
conf.set("elevation_encoding", elevationEncoding());
conf.set("stitch_edges", stitchEdges());
conf.set("interpolation", "nearest", interpolation(), osgEarth::INTERP_NEAREST);
conf.set("interpolation", "average", interpolation(), osgEarth::INTERP_AVERAGE);
conf.set("interpolation", "bilinear", interpolation(), osgEarth::INTERP_BILINEAR);
return conf;
}

void
TMSElevationLayer::Options::fromConfig(const Config& conf)
{
readFrom(conf);
conf.get("elevation_encoding", elevationEncoding());
conf.get("stitch_edges", stitchEdges());
conf.get("interpolation", "nearest", interpolation(), osgEarth::INTERP_NEAREST);
conf.get("interpolation", "average", interpolation(), osgEarth::INTERP_AVERAGE);
conf.get("interpolation", "bilinear", interpolation(), osgEarth::INTERP_BILINEAR);
}


Expand All @@ -1254,6 +1265,9 @@ REGISTER_OSGEARTH_LAYER(tmselevation, TMSElevationLayer);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, URI, URL, url);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, std::string, TMSType, tmsType);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, std::string, Format, format);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, std::string, ElevationEncoding, elevationEncoding);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, bool, StitchEdges, stitchEdges);
OE_LAYER_PROPERTY_IMPL(TMSElevationLayer, RasterInterpolation, Interpolation, interpolation);

void
TMSElevationLayer::init()
Expand Down Expand Up @@ -1309,31 +1323,119 @@ TMSElevationLayer::closeImplementation()
GeoHeightField
TMSElevationLayer::createHeightFieldImplementation(const TileKey& key, ProgressCallback* progress) const
{

if (_imageLayer.valid() == false ||
!_imageLayer->isOpen())
if (_imageLayer.valid() == false || !_imageLayer->isOpen() || !_imageLayer->mayHaveData(key))
{
return GeoHeightField::INVALID;
}

// Make an image, then convert it to a heightfield
GeoImage image = _imageLayer->createImageImplementation(key, progress);
if (image.valid())
std::function<float(const osg::Vec4f&)> decode;

if (options().elevationEncoding() == "mapbox")
{
decode = [](const osg::Vec4f& p) -> float
{
return -10000.0f + ((p.r() * 255.0f * 256.0f * 256.0f + p.g() * 255.0f * 256.0f + p.b() * 255.0f) * 0.1f);
};
}
else if (options().elevationEncoding() == "terrarium")
{
decode = [](const osg::Vec4f& p) -> float
{
return (p.r() * 255.0f * 256.0f + p.g() * 255.0f + p.b() * 255.0f / 256.0f) - 32768.0f;
};
}
else
{
decode = [](const osg::Vec4f& p) -> float
{
return p.r();
};
}

if (options().stitchEdges() == true)
{
if (image.getImage()->s() > 1 && image.getImage()->t() > 1)
MetaTile<GeoImage> metaImage;
metaImage.setCreateTileFunction([this](const TileKey& key, ProgressCallback* progress)
{
Util::LRUCache<TileKey, GeoImage>::Record r;
if (_stitchingCache.get(key, r))
{
return r.value();
}
else
{
GeoImage image = _imageLayer->createImage(key, progress);
if (image.valid())
{
_stitchingCache.insert(key, image);
}
return image;
}
});
metaImage.setCenterTileKey(key, progress);

if (!metaImage.getCenterTile().valid() || !metaImage.getScaleBias().isIdentity())
{
ImageToHeightFieldConverter conv;
osg::HeightField* hf = conv.convert(image.getImage());
return GeoHeightField(hf, key.getExtent());
return {};
}
else

osg::ref_ptr<osg::HeightField> hf = new osg::HeightField();
hf->allocate(getTileSize(), getTileSize());
std::fill(hf->getHeightList().begin(), hf->getHeightList().end(), NO_DATA_VALUE);

double xmin, ymin, xmax, ymax;
key.getExtent().getBounds(xmin, ymin, xmax, ymax);

osg::Vec4f pixel;
for (int c = 0; c < getTileSize(); c++)
{
return GeoHeightField::INVALID;
if (progress && progress->isCanceled())
return {};

double u = (double)c / (double)(getTileSize() - 1);
double x = xmin + u * (xmax - xmin);
for (int r = 0; r < getTileSize(); r++)
{
double v = (double)r / (double)(getTileSize() - 1);
double y = ymin + v * (ymax - ymin);
if (metaImage.readAtCoord(pixel, x, y))
{
hf->setHeight(c, r, decode(pixel));
}
}
}

return GeoHeightField(hf.release(), key.getExtent());
}

else
{
return GeoHeightField(image.getStatus());
// Make an image, then convert it to a heightfield
GeoImage geoImage = _imageLayer->createImageImplementation(key, progress);
if (geoImage.valid())
{
const osg::Image* image = geoImage.getImage();

// Allocate the heightfield.
osg::HeightField* hf = new osg::HeightField();
hf->allocate(image->s(), image->t());

ImageUtils::PixelReader reader(image);
osg::Vec4f pixel;

for (int c = 0; c < image->s(); c++)
{
for (int r = 0; r < image->t(); r++)
{
reader(pixel, c, r);
hf->setHeight(c, r, decode(pixel));
}
}

return GeoHeightField(hf, key.getExtent());
}

return GeoHeightField::INVALID;
}
}

Expand Down

0 comments on commit e94c533

Please sign in to comment.