Refactor TileDataset - divide code into more easily overviewable sections

This commit is contained in:
Erik Broberg
2016-06-30 22:23:39 -04:00
parent c94c7999f4
commit ea10a9e9f5
3 changed files with 187 additions and 162 deletions
+160 -140
View File
@@ -106,7 +106,6 @@ namespace openspace {
}
//3. Do any other initialization needed for the TileDataset
_dataLayout = TileDataLayout(_dataset, dataType);
_depthTransform = calculateTileDepthTransform();
_tileLevelDifference = calculateTileLevelDifference(minimumPixelSize);
@@ -119,6 +118,54 @@ namespace openspace {
delete _dataset;
}
//////////////////////////////////////////////////////////////////////////////////
// Public interface //
//////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<TileIOResult> TileDataset::readTileData(ChunkIndex chunkIndex) {
IODescription io = getIODescription(chunkIndex);
CPLErr worstError = CPLErr::CE_None;
// Build the Tile IO Result from the data we queried
std::shared_ptr<TileIOResult> result = std::make_shared<TileIOResult>();
result->imageData = readImageData(io, worstError);
result->error = worstError;
result->chunkIndex = chunkIndex;
result->dimensions = glm::uvec3(io.write.region.numPixels, 1);
result->nBytesImageData = io.write.totalNumBytes;
if (_doPreprocessing) {
result->preprocessData = preprocess(result, io.write.region);
result->error = std::max(result->error, postProcessErrorCheck(result, io));
}
return result;
}
int TileDataset::maxChunkLevel() {
if (_maxLevel < 0) {
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
_maxLevel = -_tileLevelDifference;
if (numOverviews > 0) {
_maxLevel += numOverviews - 1;
}
}
return _maxLevel;
}
TileDepthTransform TileDataset::getDepthTransform() const {
return _depthTransform;
}
const TileDataLayout& TileDataset::getDataLayout() const {
return _dataLayout;
}
//////////////////////////////////////////////////////////////////////////////////
// Initialization //
//////////////////////////////////////////////////////////////////////////////////
int TileDataset::calculateTileLevelDifference(int minimumPixelSize) {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
GDALRasterBand* maxOverview;
@@ -135,8 +182,6 @@ namespace openspace {
return diff;
}
TileDepthTransform TileDataset::calculateTileDepthTransform() {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
// Floating point types does not have a fix maximum or minimum value and
@@ -150,7 +195,68 @@ namespace openspace {
return transform;
}
PixelCoordinate TileDataset::geodeticToPixel(const Geodetic2& geo) const{
//////////////////////////////////////////////////////////////////////////////////
// GDAL helper methods //
//////////////////////////////////////////////////////////////////////////////////
bool TileDataset::gdalHasOverviews() const {
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
}
int TileDataset::gdalOverview(const PixelCoordinate& regionSizeOverviewZero) const {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
int minNumPixels0 = glm::min(regionSizeOverviewZero.x, regionSizeOverviewZero.y);
int overviews = firstBand->GetOverviewCount();
GDALRasterBand* maxOverview = overviews ? firstBand->GetOverview(overviews - 1) : firstBand;
int sizeLevel0 = maxOverview->GetXSize();
// The dataset itself may not have overviews but even if it does not, an overview
// for the data region can be calculated and possibly be used to sample greater
// Regions of the original dataset.
int ov = std::log2(minNumPixels0) - std::log2(sizeLevel0 + 1) - _tileLevelDifference;
ov = glm::clamp(ov, 0, overviews - 1);
return ov;
}
int TileDataset::gdalOverview(const ChunkIndex& chunkIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (chunkIndex.level + _tileLevelDifference + 1);
return glm::clamp(ov, 0, overviews - 1);
}
PixelRegion TileDataset::gdalPixelRegion(GDALRasterBand* rasterBand) const {
PixelRegion gdalRegion;
gdalRegion.start.x = 0;
gdalRegion.start.y = 0;
gdalRegion.numPixels.x = rasterBand->GetXSize();
gdalRegion.numPixels.y = rasterBand->GetYSize();
return gdalRegion;
}
PixelRegion TileDataset::gdalPixelRegion(const GeodeticPatch& geodeticPatch) const {
Geodetic2 nwCorner = geodeticPatch.getCorner(Quad::NORTH_WEST);
Geodetic2 swCorner = geodeticPatch.getCorner(Quad::SOUTH_EAST);
PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
PixelRegion gdalRegion(pixelStart, pixelEnd - pixelStart);
return gdalRegion;
}
GDALRasterBand* TileDataset::gdalRasterBand(int overview, int raster) const {
GDALRasterBand* rasterBand = _dataset->GetRasterBand(raster);
return gdalHasOverviews() ? rasterBand->GetOverview(overview) : rasterBand;
}
//////////////////////////////////////////////////////////////////////////////////
// ReadTileData helper functions //
//////////////////////////////////////////////////////////////////////////////////
PixelCoordinate TileDataset::geodeticToPixel(const Geodetic2& geo) const {
double padfTransform[6];
CPLErr err = _dataset->GetGeoTransform(padfTransform);
@@ -188,80 +294,13 @@ namespace openspace {
return PixelCoordinate(glm::round(P), glm::round(L));
}
PixelRegion TileDataset::gdalPixelRegion(const GeodeticPatch& geodeticPatch) const {
Geodetic2 nwCorner = geodeticPatch.getCorner(Quad::NORTH_WEST);
Geodetic2 swCorner = geodeticPatch.getCorner(Quad::SOUTH_EAST);
PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
PixelRegion gdalRegion(pixelStart, pixelEnd- pixelStart);
return gdalRegion;
}
int TileDataset::gdalOverview(const PixelCoordinate& regionSizeOverviewZero) const {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
int minNumPixels0 = glm::min(regionSizeOverviewZero.x, regionSizeOverviewZero.y);
int overviews = firstBand->GetOverviewCount();
GDALRasterBand* maxOverview = overviews ? firstBand->GetOverview(overviews - 1) : firstBand;
int sizeLevel0 = maxOverview->GetXSize();
// The dataset itself may not have overviews but even if it does not, an overview
// for the data region can be calculated and possibly be used to sample greater
// Regions of the original dataset.
int ov = std::log2(minNumPixels0) - std::log2(sizeLevel0 + 1) - _tileLevelDifference;
ov = glm::clamp(ov, 0, overviews - 1);
return ov;
}
int TileDataset::gdalOverview(const ChunkIndex& chunkIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (chunkIndex.level + _tileLevelDifference + 1);
return glm::clamp(ov, 0, overviews - 1);
}
int TileDataset::maxChunkLevel() {
if (_maxLevel < 0) {
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
_maxLevel = -_tileLevelDifference;
if (numOverviews > 0) {
_maxLevel += numOverviews - 1;
}
}
return _maxLevel;
}
TileDepthTransform TileDataset::getDepthTransform() const {
return _depthTransform;
}
bool TileDataset::gdalHasOverviews() const {
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
}
GDALRasterBand* TileDataset::gdalRasterBand(int overview, int raster) const {
GDALRasterBand* rasterBand = _dataset->GetRasterBand(raster);
return gdalHasOverviews() ? rasterBand->GetOverview(overview) : rasterBand;
}
PixelRegion TileDataset::gdalPixelRegion(GDALRasterBand* rasterBand) const {
PixelRegion gdalRegion;
gdalRegion.start.x = 0;
gdalRegion.start.y = 0;
gdalRegion.numPixels.x = rasterBand->GetXSize();
gdalRegion.numPixels.y = rasterBand->GetYSize();
return gdalRegion;
}
IODescription TileDataset::getIODescription(const ChunkIndex& chunkIndex) {
IODescription TileDataset::getIODescription(const ChunkIndex& chunkIndex) const {
// Calculate suitable overview and corresponding pixel region
int overview = gdalOverview(chunkIndex);
PixelRegion region = gdalPixelRegion(chunkIndex); // pixel region at overview zero
region.downscalePow2(overview + 1); // pixel region at suitable overview
// Create an IORegion based on that overview pixel region
// Create an IORegion based on that overview pixel region
IODescription io;
io.read.overview = overview;
io.read.region = region;
@@ -272,7 +311,7 @@ namespace openspace {
io.read.region.upscalePow2(overview + 1);
io.read.overview = 0; // no overview
}
// For correct sampling in height dataset, we need to pad the texture tile
io.read.region.pad(padding);
@@ -287,77 +326,44 @@ namespace openspace {
return io;
}
std::shared_ptr<TileIOResult> TileDataset::readTileData(ChunkIndex chunkIndex) {
IODescription io = getIODescription(chunkIndex);
char* TileDataset::readImageData(const IODescription& io, CPLErr& worstError) const {
// allocate memory for the image
char* imageData = new char[io.write.totalNumBytes];
CPLErr worstError = CPLErr::CE_None;
// Read the data (each rasterband is a separate channel)
for (size_t i = 0; i < _dataLayout.numRasters; i++) {
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
GDALRasterBand* rasterBand = gdalRasterBand(io.read.overview, i+1);
GDALRasterBand* rasterBand = gdalRasterBand(io.read.overview, i + 1);
CPLErr err = rasterBand->RasterIO(
GF_Read,
io.read.region.start.x, // Begin read x
io.read.region.start.y, // Begin read y
io.read.region.numPixels.x, // width to read x
io.read.region.numPixels.y, // width to read y
dataDestination, // Where to put data
io.write.region.numPixels.x, // width to write x in destination
io.write.region.numPixels.y, // width to write y in destination
_dataLayout.gdalType, // Type
_dataLayout.bytesPerPixel, // Pixel spacing
io.write.bytesPerLine); // Line spacing
io.read.region.start.x, // Begin read x
io.read.region.start.y, // Begin read y
io.read.region.numPixels.x, // width to read x
io.read.region.numPixels.y, // width to read y
dataDestination, // Where to put data
io.write.region.numPixels.x, // width to write x in destination
io.write.region.numPixels.y, // width to write y in destination
_dataLayout.gdalType, // Type
_dataLayout.bytesPerPixel, // Pixel spacing
io.write.bytesPerLine); // Line spacing
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
std::shared_ptr<TileIOResult> result(new TileIOResult);
result->chunkIndex = chunkIndex;
result->imageData = getImageDataFlippedY(io.write.region, imageData);
result->dimensions = glm::uvec3(io.write.region.numPixels, 1);
result->nBytesImageData = io.write.totalNumBytes;
result->error = worstError;
if (_doPreprocessing) {
result->preprocessData = preprocess(imageData, io.write.region);
int success;
double missingDataValue = gdalRasterBand(io.read.overview)->GetNoDataValue(&success);
if (!success) {
missingDataValue = 32767; // missing data value for TERRAIN.wms. Should be specified in xml
}
bool hasMissingData = false;
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
hasMissingData |= result->preprocessData->maxValues[c] == missingDataValue;
}
bool onHighLevel = chunkIndex.level > 6;
if (hasMissingData && onHighLevel) {
result->error = CE_Fatal;
}
}
delete[] imageData;
return result;
// GDAL reads pixel lines top to bottom, we want the opposit
return flipImageYAxis(imageData, io.write);
}
char* TileDataset::getImageDataFlippedY(const PixelRegion& region, const char* imageData) {
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
size_t totalNumBytes = bytesPerLine * region.numPixels.y;
// GDAL reads image data top to bottom. We want the opposite.
char* imageDataYflipped = new char[totalNumBytes];
for (size_t y = 0; y < region.numPixels.y; y++) {
size_t yi_flipped = y * bytesPerLine;
size_t yi = (region.numPixels.y - 1 - y) * bytesPerLine;
char* TileDataset::flipImageYAxis(char*& imageData, const IODescription::WriteData& writeData) const {
char* imageDataYflipped = new char[writeData.totalNumBytes];
for (size_t y = 0; y < writeData.region.numPixels.y; y++) {
size_t yi_flipped = y * writeData.bytesPerLine;
size_t yi = (writeData.region.numPixels.y - 1 - y) * writeData.bytesPerLine;
size_t i = 0;
for (size_t x = 0; x < region.numPixels.x; x++) {
for (size_t x = 0; x < writeData.region.numPixels.x; x++) {
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
for (size_t b = 0; b < _dataLayout.bytesPerDatum; b++) {
imageDataYflipped[yi_flipped + i] = imageData[yi + i];
@@ -367,17 +373,14 @@ namespace openspace {
}
}
// Delete the old data and return the new
delete[] imageData;
imageData = nullptr;
return imageDataYflipped;
}
const TileDataLayout& TileDataset::getDataLayout() const {
return _dataLayout;
}
std::shared_ptr<TilePreprocessData> TileDataset::preprocess(const char* imageData, const PixelRegion& region)
{
std::shared_ptr<TilePreprocessData> TileDataset::preprocess(std::shared_ptr<TileIOResult> result, const PixelRegion& region) const {
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
size_t totalNumBytes = bytesPerLine * region.numPixels.y;
@@ -397,7 +400,7 @@ namespace openspace {
for (size_t x = 0; x < region.numPixels.x; x++) {
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
float val = TileDataType::interpretFloat(_dataLayout.gdalType, &(imageData[yi + i]));
float val = TileDataType::interpretFloat(_dataLayout.gdalType, &(result->imageData[yi + i]));
preprocessData->maxValues[c] = std::max(val, preprocessData->maxValues[c]);
preprocessData->minValues[c] = std::min(val, preprocessData->minValues[c]);
@@ -405,6 +408,7 @@ namespace openspace {
}
}
}
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
if (preprocessData->maxValues[c] > 8800.0f) {
//LDEBUG("Bad preprocess data: " << preprocessData->maxValues[c] << " at " << region.chunkIndex);
@@ -414,7 +418,23 @@ namespace openspace {
return std::shared_ptr<TilePreprocessData>(preprocessData);
}
CPLErr TileDataset::postProcessErrorCheck(std::shared_ptr<const TileIOResult> result, const IODescription& io) const{
int success;
double missingDataValue = gdalRasterBand(io.read.overview)->GetNoDataValue(&success);
if (!success) {
missingDataValue = 32767; // missing data value for TERRAIN.wms. Should be specified in xml
}
bool hasMissingData = false;
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
hasMissingData |= result->preprocessData->maxValues[c] == missingDataValue;
}
bool onHighLevel = result->chunkIndex.level > 6;
if (hasMissingData && onHighLevel) {
return CE_Fatal;
}
return CE_None;
}
} // namespace openspace
+26 -21
View File
@@ -142,53 +142,58 @@ namespace openspace {
~TileDataset();
//////////////////////////////////////////////////////////////////////////////////
// Public interface //
//////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<TileIOResult> readTileData(ChunkIndex chunkIndex);
int maxChunkLevel();
TileDepthTransform getDepthTransform() const;
const TileDataLayout& getDataLayout() const;
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
const static PixelRegion padding; // same as the two above
private:
IODescription getIODescription(const ChunkIndex& chunkIndex);
//////////////////////////////////////////////////////////////////////////////////
// GDAL HELPER FUNCTIONS //
//////////////////////////////////////////////////////////////////////////////////
PixelRegion gdalPixelRegion(const GeodeticPatch& geodeticPatch) const;
int gdalOverview(const PixelCoordinate& baseRegionSize) const;
int gdalOverview(const ChunkIndex& chunkIndex) const;
bool gdalHasOverviews() const;
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
GDALRasterBand* gdalRasterBand(int overview, int raster = 1) const;
//////////////////////////////////////////////////////////////////////////////////
// Initialization //
//////////////////////////////////////////////////////////////////////////////////
TileDepthTransform calculateTileDepthTransform();
int calculateTileLevelDifference(int minimumPixelSize);
//////////////////////////////////////////////////////////////////////////////////
// GDAL helper methods //
//////////////////////////////////////////////////////////////////////////////////
bool gdalHasOverviews() const;
int gdalOverview(const PixelCoordinate& baseRegionSize) const;
int gdalOverview(const ChunkIndex& chunkIndex) const;
PixelRegion gdalPixelRegion(const GeodeticPatch& geodeticPatch) const;
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
GDALRasterBand* gdalRasterBand(int overview, int raster = 1) const;
//////////////////////////////////////////////////////////////////////////////////
// ReadTileData helper functions //
//////////////////////////////////////////////////////////////////////////////////
PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
char* getImageDataFlippedY(const PixelRegion& region, const char* imageData);
std::shared_ptr<TilePreprocessData> preprocess(const char* imageData, const PixelRegion& region);
IODescription getIODescription(const ChunkIndex& chunkIndex) const;
char* readImageData(const IODescription& io, CPLErr& worstError) const;
char* flipImageYAxis(char*& imageData, const IODescription::WriteData& writeData) const;
std::shared_ptr<TilePreprocessData> preprocess(std::shared_ptr<TileIOResult> result, const PixelRegion& region) const;
CPLErr postProcessErrorCheck(std::shared_ptr<const TileIOResult> ioResult, const IODescription& io) const;
//////////////////////////////////////////////////////////////////////////////////
// MEMBER VARIABLES //
// Member variables //
//////////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -59,7 +59,7 @@ namespace openspace {
struct TileIOResult {
TileIOResult();
void* imageData;
char* imageData;
glm::uvec3 dimensions;
std::shared_ptr<TilePreprocessData> preprocessData;
ChunkIndex chunkIndex;