mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-20 17:51:19 -05:00
Merge branch 'feature/globebrowsing' of github.com:OpenSpace/OpenSpace-Development into feature/globebrowsing
This commit is contained in:
@@ -83,7 +83,7 @@ vec2 compensateSourceTextureSampling(vec2 startOffset, vec2 sizeDiff, const Tile
|
||||
|
||||
vec2 TileUVToTextureSamplePosition(const Tile tile, vec2 tileUV){
|
||||
vec2 uv = tile.uvTransform.uvOffset + tile.uvTransform.uvScale * tileUV;
|
||||
uv = compensateSourceTextureSampling(vec2(0), vec2(0), tile, uv);
|
||||
uv = compensateSourceTextureSampling(vec2(-2), vec2(4), tile, uv);
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ namespace openspace {
|
||||
// Tile Dataset //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const glm::ivec2 TileDataset::tilePixelStartOffset = glm::ivec2(0, 0);
|
||||
const glm::ivec2 TileDataset::tilePixelSizeDifference = glm::ivec2(0, 0);
|
||||
const glm::ivec2 TileDataset::tilePixelStartOffset = glm::ivec2(-2);
|
||||
const glm::ivec2 TileDataset::tilePixelSizeDifference = glm::ivec2(4);
|
||||
|
||||
const PixelRegion TileDataset::padding = PixelRegion(tilePixelStartOffset, tilePixelSizeDifference);
|
||||
|
||||
@@ -92,21 +92,24 @@ namespace openspace {
|
||||
: _doPreprocessing(doPreprocessing)
|
||||
, _maxLevel(-1)
|
||||
{
|
||||
// 1. First make sure GDAL has been initialized
|
||||
if (!GdalHasBeenInitialized) {
|
||||
GDALAllRegister();
|
||||
CPLSetConfigOption("GDAL_DATA", absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str());
|
||||
|
||||
GdalHasBeenInitialized = true;
|
||||
}
|
||||
|
||||
//2. Secondly, open the GDAL dataset. Other methods depends on this
|
||||
_dataset = (GDALDataset *)GDALOpen(gdalDatasetDesc.c_str(), GA_ReadOnly);
|
||||
if (!_dataset) {
|
||||
throw ghoul::RuntimeError("Failed to load dataset:\n" + gdalDatasetDesc);
|
||||
}
|
||||
_dataLayout = TileDataLayout(_dataset, dataType);
|
||||
|
||||
//3. Do any other initialization needed for the TileDataset
|
||||
_dataLayout = TileDataLayout(_dataset, dataType);
|
||||
_depthTransform = calculateTileDepthTransform();
|
||||
_tileLevelDifference = calculateTileLevelDifference(_dataset, minimumPixelSize);
|
||||
_tileLevelDifference = calculateTileLevelDifference(minimumPixelSize);
|
||||
|
||||
LDEBUG(gdalDatasetDesc << " - " << _tileLevelDifference);
|
||||
}
|
||||
|
||||
@@ -115,8 +118,56 @@ namespace openspace {
|
||||
delete _dataset;
|
||||
}
|
||||
|
||||
int TileDataset::calculateTileLevelDifference(GDALDataset* dataset, int minimumPixelSize) {
|
||||
GDALRasterBand* firstBand = dataset->GetRasterBand(1);
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
int numOverviews = firstBand->GetOverviewCount();
|
||||
int sizeLevel0;
|
||||
@@ -131,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
|
||||
@@ -146,10 +195,71 @@ namespace openspace {
|
||||
return transform;
|
||||
}
|
||||
|
||||
PixelCoordinate TileDataset::geodeticToPixel(GDALDataset* dataSet, const Geodetic2& geo) {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
CPLErr err = _dataset->GetGeoTransform(padfTransform);
|
||||
|
||||
ghoul_assert(err != CE_Failure, "Failed to get transform");
|
||||
|
||||
@@ -184,153 +294,147 @@ 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 = TileDataset::geodeticToPixel(_dataset, nwCorner);
|
||||
PixelCoordinate pixelEnd = TileDataset::geodeticToPixel(_dataset, swCorner);
|
||||
PixelRegion gdalRegion(pixelStart, pixelEnd- pixelStart);
|
||||
return gdalRegion;
|
||||
}
|
||||
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
|
||||
|
||||
int TileDataset::gdalOverview(const PixelCoordinate& regionSizeOverviewZero) const {
|
||||
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
|
||||
// Create an IORegion based on that overview pixel region
|
||||
IODescription io;
|
||||
io.read.overview = overview;
|
||||
io.read.region = region;
|
||||
io.write.region = { PixelCoordinate(0, 0), region.numPixels };
|
||||
|
||||
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;
|
||||
}
|
||||
// Handle the case where the dataset does not have overviews
|
||||
if (!gdalHasOverviews()) {
|
||||
io.read.region.upscalePow2(overview + 1);
|
||||
io.read.overview = 0; // no overview
|
||||
}
|
||||
|
||||
return _maxLevel;
|
||||
// For correct sampling in height dataset, we need to pad the texture tile
|
||||
io.read.region.pad(padding);
|
||||
io.write.region.pad(padding);
|
||||
|
||||
// Doing this may cause invalid regions, i.e. having negative pixel coordinates
|
||||
// or being too large etc. For now, just clamp
|
||||
PixelRegion overviewRegion = gdalPixelRegion(gdalRasterBand(overview));
|
||||
//io.read.region.clampTo(overviewRegion);
|
||||
//io.write.region.clampTo(overviewRegion);
|
||||
|
||||
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
|
||||
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
TileDepthTransform TileDataset::getDepthTransform() const {
|
||||
return _depthTransform;
|
||||
}
|
||||
|
||||
std::shared_ptr<TileIOResult> TileDataset::readTileData(ChunkIndex chunkIndex) {
|
||||
//GdalDataRegion region(_dataset, chunkIndex, _tileLevelDifference);
|
||||
PixelRegion region = gdalPixelRegion(chunkIndex);
|
||||
int overview = gdalOverview(chunkIndex);
|
||||
region.shrinkPow2(overview + 1);
|
||||
|
||||
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
|
||||
size_t totalNumBytes = bytesPerLine * region.numPixels.y;
|
||||
char* imageData = new char[totalNumBytes];
|
||||
|
||||
CPLErr worstError = CPLErr::CE_None;
|
||||
char* TileDataset::readImageData(const IODescription& io, CPLErr& worstError) const {
|
||||
// allocate memory for the image
|
||||
char* imageData = new char[io.write.totalNumBytes];
|
||||
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < _dataLayout.numRasters; i++) {
|
||||
GDALRasterBand* rasterBand = gdalRasterBand(io.read.overview, i + 1);
|
||||
|
||||
// The final destination pointer is offsetted by one datum byte size
|
||||
// for every raster (or data channel, i.e. R in RGB)
|
||||
char* dataDestination = imageData + (i * _dataLayout.bytesPerDatum);
|
||||
|
||||
PixelRegion readRegion(region);
|
||||
|
||||
GDALRasterBand* rasterBand;
|
||||
if (_dataset->GetRasterBand(i + 1)->GetOverviewCount() <= 0){
|
||||
rasterBand = _dataset->GetRasterBand(i + 1);
|
||||
int pixelSourceScale = pow(2, overview + 1);
|
||||
readRegion.scale(pixelSourceScale);
|
||||
}
|
||||
else {
|
||||
rasterBand = _dataset->GetRasterBand(i + 1)->GetOverview(overview);
|
||||
}
|
||||
|
||||
PixelRegion gdalPixelRegion;
|
||||
gdalPixelRegion.start = glm::ivec2(0);
|
||||
gdalPixelRegion.numPixels = glm::ivec2(rasterBand->GetXSize(), rasterBand->GetYSize());
|
||||
|
||||
|
||||
readRegion.addPadding(padding);
|
||||
readRegion.clampTo(gdalPixelRegion);
|
||||
|
||||
// OBS! GDAL reads pixels top to bottom, but we want our pixels bottom to top.
|
||||
// Therefore, we increment the destination pointer to the last line on in the
|
||||
// buffer, and the we specify in the rasterIO call that we want negative line
|
||||
// spacing. Doing this compensates the flipped Y axis
|
||||
dataDestination += (io.write.totalNumBytes - io.write.bytesPerLine);
|
||||
|
||||
CPLErr err = rasterBand->RasterIO(
|
||||
GF_Read,
|
||||
readRegion.start.x, // Begin read x
|
||||
readRegion.start.y, // Begin read y
|
||||
readRegion.numPixels.x, // width to read x
|
||||
readRegion.numPixels.y, // width to read y
|
||||
dataDestination, // Where to put data
|
||||
region.numPixels.x, // width to write x in destination
|
||||
region.numPixels.y, // width to write y in destination
|
||||
_dataLayout.gdalType, // Type
|
||||
_dataLayout.bytesPerPixel, // Pixel spacing
|
||||
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(region, _dataLayout, imageData);
|
||||
result->dimensions = glm::uvec3(region.numPixels, 1);
|
||||
result->nBytesImageData = _dataLayout.bytesPerPixel * region.numPixels.x * region.numPixels.y;
|
||||
result->error = worstError;
|
||||
if (_doPreprocessing) {
|
||||
result->preprocessData = preprocess(imageData, region, _dataLayout);
|
||||
int success;
|
||||
auto gdalOverview = _dataset->GetRasterBand(1)->GetOverview(overview);
|
||||
double missingDataValue = gdalOverview->GetNoDataValue(&success);
|
||||
if (!success) {
|
||||
missingDataValue = 32767; // missing data value
|
||||
}
|
||||
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;
|
||||
// GDAL reads pixel lines top to bottom, we want the opposit
|
||||
return imageData;
|
||||
}
|
||||
|
||||
char* TileDataset::readImageData2(const IODescription& io, CPLErr& worstError) const {
|
||||
std::vector<char *> imageDataChannels(_dataLayout.numRasters);
|
||||
size_t numByterPerChannel = io.write.totalNumBytes / _dataLayout.numRasters;
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < _dataLayout.numRasters; i++) {
|
||||
imageDataChannels[i] = new char[numByterPerChannel];
|
||||
|
||||
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
|
||||
imageDataChannels[i], // 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
|
||||
0, // Pixel spacing
|
||||
0); // Line spacing
|
||||
|
||||
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
|
||||
worstError = std::max(worstError, err);
|
||||
}
|
||||
|
||||
// Combined image data
|
||||
char* imageData = new char[io.write.totalNumBytes];
|
||||
size_t yx = 0;
|
||||
size_t c = 0;
|
||||
for (size_t y = 0; y < io.write.region.numPixels.y; y++) {
|
||||
for (size_t x = 0; x < io.write.region.numPixels.x; x++) {
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
size_t combinedChannelIndex = (yx * _dataLayout.numRasters + c) * _dataLayout.bytesPerDatum;
|
||||
size_t separateChannelIndex = yx * _dataLayout.bytesPerDatum;
|
||||
|
||||
ghoul_assert(combinedChannelIndex < io.write.totalNumBytes, "Invalid combined index!");
|
||||
ghoul_assert(separateChannelIndex < numByterPerChannel, "invalid single index!");
|
||||
|
||||
char* channelData = imageDataChannels[c];
|
||||
for (size_t b = 0; b < _dataLayout.bytesPerDatum; b++) {
|
||||
char val = channelData[separateChannelIndex + b];
|
||||
imageData[combinedChannelIndex + b] = channelData[separateChannelIndex+b];
|
||||
}
|
||||
}
|
||||
yx++;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] imageData;
|
||||
return result;
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
char * singleChannel = imageDataChannels[c];
|
||||
delete[] singleChannel;
|
||||
}
|
||||
|
||||
// GDAL reads pixel lines top to bottom, we want the opposit
|
||||
return flipImageYAxis(imageData, io.write);
|
||||
}
|
||||
|
||||
char* TileDataset::getImageDataFlippedY(const PixelRegion& region,
|
||||
const TileDataLayout& dataLayout, 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 c = 0; c < dataLayout.numRasters; c++) {
|
||||
for (size_t b = 0; b < dataLayout.bytesPerDatum; b++) {
|
||||
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];
|
||||
i++;
|
||||
}
|
||||
@@ -338,26 +442,22 @@ 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, const TileDataLayout& dataLayout)
|
||||
{
|
||||
size_t bytesPerLine = dataLayout.bytesPerPixel * region.numPixels.x;
|
||||
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;
|
||||
|
||||
TilePreprocessData* preprocessData = new TilePreprocessData();
|
||||
preprocessData->maxValues.resize(dataLayout.numRasters);
|
||||
preprocessData->minValues.resize(dataLayout.numRasters);
|
||||
preprocessData->maxValues.resize(_dataLayout.numRasters);
|
||||
preprocessData->minValues.resize(_dataLayout.numRasters);
|
||||
|
||||
for (size_t c = 0; c < dataLayout.numRasters; c++) {
|
||||
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
|
||||
preprocessData->maxValues[c] = -FLT_MAX;
|
||||
preprocessData->minValues[c] = FLT_MAX;
|
||||
}
|
||||
@@ -367,17 +467,18 @@ namespace openspace {
|
||||
size_t yi = (region.numPixels.y - 1 - y) * bytesPerLine;
|
||||
size_t i = 0;
|
||||
for (size_t x = 0; x < region.numPixels.x; x++) {
|
||||
for (size_t c = 0; c < dataLayout.numRasters; c++) {
|
||||
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]);
|
||||
|
||||
i += dataLayout.bytesPerDatum;
|
||||
i += _dataLayout.bytesPerDatum;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t c = 0; c < dataLayout.numRasters; c++) {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -386,7 +487,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
|
||||
|
||||
@@ -69,11 +69,7 @@ namespace openspace {
|
||||
PixelRegion(const PixelCoordinate& pixelStart, const PixelCoordinate& numberOfPixels)
|
||||
: start(pixelStart), numPixels(numberOfPixels) { }
|
||||
|
||||
|
||||
PixelCoordinate start;
|
||||
PixelCoordinate numPixels;
|
||||
|
||||
void addPadding(const PixelRegion& padding) {
|
||||
void pad(const PixelRegion& padding) {
|
||||
start += padding.start;
|
||||
numPixels += padding.numPixels;
|
||||
}
|
||||
@@ -96,20 +92,41 @@ namespace openspace {
|
||||
scale(glm::dvec2(s));
|
||||
}
|
||||
|
||||
void shrinkPow2(int exponent) {
|
||||
void downscalePow2(int exponent) {
|
||||
start.x >>= exponent;
|
||||
start.y >>= exponent;
|
||||
numPixels.x >>= exponent;
|
||||
numPixels.y >>= exponent;
|
||||
}
|
||||
|
||||
void upscalePow2(int exponent) {
|
||||
start.x <<= exponent;
|
||||
start.y <<= exponent;
|
||||
numPixels.x <<= exponent;
|
||||
numPixels.y <<= exponent;
|
||||
}
|
||||
|
||||
PixelCoordinate start;
|
||||
PixelCoordinate numPixels;
|
||||
};
|
||||
|
||||
|
||||
struct IODescription {
|
||||
struct ReadData {
|
||||
int overview;
|
||||
PixelRegion region;
|
||||
} read;
|
||||
|
||||
struct WriteData {
|
||||
PixelRegion region; // should always start at 0,0
|
||||
size_t bytesPerLine;
|
||||
size_t totalNumBytes;
|
||||
} write;
|
||||
};
|
||||
|
||||
|
||||
class TileDataset {
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* Opens a GDALDataset in readonly mode and calculates meta data required for
|
||||
@@ -124,53 +141,61 @@ 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;
|
||||
|
||||
|
||||
static PixelCoordinate geodeticToPixel(GDALDataset* dataSet, const Geodetic2& geo);
|
||||
const static PixelRegion padding; // same as the two above
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// HELPER FUNCTIONS //
|
||||
// Initialization //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PixelRegion gdalPixelRegion(const GeodeticPatch& geodeticPatch) const;
|
||||
|
||||
int gdalOverview(const PixelCoordinate& baseRegionSize) const;
|
||||
int gdalOverview(const ChunkIndex& chunkIndex) const;
|
||||
|
||||
TileDepthTransform calculateTileDepthTransform();
|
||||
|
||||
std::shared_ptr<TilePreprocessData> preprocess(const char* imageData,
|
||||
const PixelRegion& region, const TileDataLayout& dataLayout);
|
||||
|
||||
|
||||
static int calculateTileLevelDifference(GDALDataset* dataset, int minimumPixelSize);
|
||||
|
||||
static char* getImageDataFlippedY(const PixelRegion& region,
|
||||
const TileDataLayout& dataLayout, const char* imageData);
|
||||
int calculateTileLevelDifference(int minimumPixelSize);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// MEMBER VARIABLES //
|
||||
// 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;
|
||||
IODescription getIODescription(const ChunkIndex& chunkIndex) const;
|
||||
char* readImageData(const IODescription& io, CPLErr& worstError) const;
|
||||
char* readImageData2(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 //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool GdalHasBeenInitialized;
|
||||
|
||||
int _maxLevel;
|
||||
double _tileLevelDifference;
|
||||
@@ -181,6 +206,9 @@ namespace openspace {
|
||||
TileDataLayout _dataLayout;
|
||||
|
||||
bool _doPreprocessing;
|
||||
|
||||
static bool GdalHasBeenInitialized;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace openspace {
|
||||
struct TileIOResult {
|
||||
TileIOResult();
|
||||
|
||||
void* imageData;
|
||||
char* imageData;
|
||||
glm::uvec3 dimensions;
|
||||
std::shared_ptr<TilePreprocessData> preprocessData;
|
||||
ChunkIndex chunkIndex;
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ return {
|
||||
-- Sets the scene that is to be loaded by OpenSpace. A scene file is a description
|
||||
-- of all entities that will be visible during an instance of OpenSpace
|
||||
-- Scene = "${SCENE}/default-moon.scene",
|
||||
Scene = "${SCENE}/default.scene",
|
||||
-- Scene = "${SCENE}/globebrowsing.scene",
|
||||
-- Scene = "${SCENE}/default.scene",
|
||||
Scene = "${SCENE}/globebrowsing.scene",
|
||||
-- Scene = "${SCENE}/rosetta.scene",
|
||||
-- Scene = "${SCENE}/dawn.scene",
|
||||
-- Scene = "${SCENE}/newhorizons.scene",
|
||||
|
||||
Reference in New Issue
Block a user