Feature/globebrowsing (#281)

* Solve bug related to corrupted texture tiles for certain sizes.

* Regard layer settings when sampling height map.

* Make Tile in to a class instead of a struct.

* Memory aware lru cache. Needs cleanup.

* Clean up and comment.

* Clean up and comment.

* Clean up

* Clean up and comment.

* Fix compilation error on Windows.

* Specify data type explicitly in GDAL xml config files for Utah height maps. Closes #242

* Update the key type for the memory aware lru cache and use a unordered map instead of a map.

* Solve pixel row size bug.

* Solve initialization bug.

* Add cache size as property of the globe browsing module.

* Use memory aware tile cache for text tile provider.

* Log GDAL errors as GHOUL messages

* Add the ability to toggle tile level limiting by available data

* Add ability to toggle GDAL logging

* Add lock guard to memory aware tile cache

* create base class rawtiledatareader that can be extended with different implementations than GDAL.

* Let GdalWrapper take care of global GDAL settings.

* Move iodescription to separate file

* Move some functionality from gdalrawtiledatareader to rawtiledatareader

* Move functionality from gdalrawtiledatareader to rawtiledatareader.

* GDAL is no longer a necessary dependency for the globebrowsing module. However to read tiles, the SimpleRawTileDataReader needs to be implemented. Otherwise GDAL is needed.

* Add ifdef check for GLOBEBROWSING_USE_GDAL

* Implement SimpleRawTileDataReader. Currently can only read pow 2 textures.

* Change ints to unsigned long longs

* Limit number of texture creations per tile provider per frame

* Solve linker error on windows

* Fix Windows build errors

* Fix crash in reading local patches

* Update lodglobe descriptions

* Abstract away overviews in gdal raw tile data reader

* Update Mars and Moon configs.

* Update screenshot script

* Update ghoul version

* Remove use of interaction depth below ellipsoid

* Normalize direction vector

* Use scale for distance swotch

* Go back to use of interaction depth below ellipsoid

* Fix comments on pull request.

* TileProviderByLevel error does not propagate up.

* Comment on mars and moon mod file

* Add model space cut off level as a property

* Update ChunkTile struct

* Minor clean up

* Go back tu constructor for ChunkTile
This commit is contained in:
Kalle Bladin
2017-04-13 10:14:47 +02:00
committed by GitHub
parent 53e2aedd81
commit 8a617ee254
88 changed files with 3895 additions and 1490 deletions

View File

@@ -56,13 +56,14 @@ return {
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {
{
Name = "ESRI VIIRS Combo",
Type = "ByLevel",
LevelTileProviders = {
{
MaxLevel = 7,
TileProvider = { FilePath = "map_service_configs/GIBS/VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
MaxLevel = 3,
TileProvider = { Type = "Temporal", FilePath = "map_service_configs/GIBS/Temporal_VIIRS_SNPP_CorrectedReflectance_TrueColor.xml", },
},
{
MaxLevel = 22,
@@ -71,6 +72,10 @@ return {
},
Enabled = true,
},
{
Name = "ESRI Imagery World 2D",
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms",
},
{
Name = "ESRI Imagery World",
FilePath = "map_service_configs/ESRI/ESRI_Imagery_World_2D.wms"
@@ -168,7 +173,7 @@ return {
Name = "Terrain tileset",
FilePath = "map_service_configs/ESRI/TERRAIN.wms",
Enabled = true,
MinimumPixelSize = 64,
TilePixelSize = 64,
DoPreProcessing = true,
},
},

View File

@@ -32,7 +32,7 @@ return {
Name = "Callisto Texture",
FilePath = "textures/callisto.jpg",
Enabled = true,
MinimumPixelSize = 112,
TilePixelSize = 112,
},
},
GrayScaleLayers = { },

View File

@@ -32,7 +32,7 @@ return {
Name = "Europa Texture",
FilePath = "textures/europa.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
},
GrayScaleLayers = { },

View File

@@ -32,7 +32,7 @@ return {
Name = "Ganymede Texture",
FilePath = "textures/ganymede.jpg",
Enabled = true,
MinimumPixelSize = 112,
TilePixelSize = 112,
},
},
GrayScaleLayers = { },

View File

@@ -12,6 +12,7 @@
<TileLevel>6</TileLevel>
<YOrigin>top</YOrigin>
</DataWindow>
<DataType>Int16</DataType>
<Projection>GEOGCS["GCS_Mars_2000_Sphere",DATUM["D_Mars_2000_Sphere",SPHEROID["Mars_2000_Sphere_IAU_IAG",3396190.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]]</Projection>
<BlockSizeX>360</BlockSizeX>
<BlockSizeY>360</BlockSizeY>

View File

@@ -32,20 +32,36 @@ return {
Type = "RenderableGlobe",
Radii = marsEllipsoid,
CameraMinHeight = 10,
SegmentsPerPatch = 90,
-- Allows camera to go down 10000 meters below the reference ellipsoid
InteractionDepthBelowEllipsoid = 10000, -- Useful when having negative height map values
SegmentsPerPatch = 64,
Layers = {
ColorLayers = {
{
Name = "Viking combo",
Type = "ByLevel",
LevelTileProviders = {
{
MaxLevel = 3,
TileProvider = { FilePath = "textures/mars.jpg", },
},
{
MaxLevel = 22,
TileProvider = { FilePath = "map_service_configs/MARS_Viking_MDIM21.xml" },
},
},
Enabled = true,
},
-- {
-- Type = "SingleImage",
-- Name = "Debug Tiles",
-- FilePath = "../../debugglobe/textures/test_tile.png",
-- },
{
Name = "MARS_Viking",
FilePath = "map_service_configs/MARS_Viking_MDIM21.xml",
Enabled = true,
},
--{
-- Name = "MARS_Viking",
-- FilePath = "map_service_configs/MARS_Viking_MDIM21.xml",
-- Enabled = true,
--},
{
Name = "MOLA Pseudo Color",
FilePath = "map_service_configs/Utah/MolaPseudoColor.xml",
@@ -66,16 +82,22 @@ return {
{
Name = "CTX Mosaic [Europe]",
FilePath = "map_service_configs/CTX_Mosaic.xml",
Enabled = true,
}, {
--Enabled = true,
},
{
Name = "CTX Mosaic [Utah]",
FilePath = "map_service_configs/Utah/CTX_Mosaic.xml",
},
{
Name = "West Candor Chasma",
FilePath = "map_datasets/CTX/West_Candor_Chasma_longlat_global.vrt",
--Enabled = true,
},
{
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Texture.vrt",
},
{
--[[{
Name = "Themis IR Day",
FilePath = "map_service_configs/Utah/ThemisIRDay.xml",
},
@@ -83,12 +105,7 @@ return {
Name = "Themis IR Night",
FilePath = "map_service_configs/Utah/ThemisIRNight.xml",
},
--[[
{
Name = "West Candor Chasma",
FilePath = "map_datasets/CTX/West_Candor_Chasma_longlat_global.vrt",
--Enabled = true,
},
{
Name = "MER_Meridianni_Endeavor_Basemap_25cm",
FilePath = "map_datasets/Basemap/MER_Meridianni_Endeavor_Basemap_25cm.vrt",
@@ -115,41 +132,49 @@ return {
},
HeightLayers = {
{
Name = "Mola Elevation",
FilePath = "map_service_configs/Utah/Mola_Elevation.xml",
Name = "Mola Elevation [Europe]",
FilePath = "map_service_configs/Mola_Elevation.xml",
Enabled = true,
MinimumPixelSize = 90,
TilePixelSize = 90,
DoPreProcessing = true,
},
--[[
{
Name = "Mola Elevation [Utah]",
FilePath = "map_service_configs/Utah/Mola_Elevation.xml",
Enabled = false,
TilePixelSize = 90,
DoPreProcessing = true,
},
{
Name = "Mola Elevation CTX",
FilePath = "map_service_configs/Utah/MolaCTX_Elevation.xml",
-- Enabled = true,
MinimumPixelSize = 90,
TilePixelSize = 90,
DoPreProcessing = true,
},
},]]
{
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Heightmap.vrt",
MinimumPixelSize = 90,
DoPreProcessing = true,
},
--[[
{
Name = "Mola Elevation",
FilePath = "map_service_configs/Mars_MGS_MOLA_DEM.xml",
Enabled = true,
MinimumPixelSize = 64,
DoPreProcessing = true,
},
--]]
--[[ {
Name = "West Candor Chasma",
FilePath = "map_datasets/CTX/West_Candor_Chasma_DEM_longlat_global.vrt",
--Enabled = true,
MinimumPixelSize = 90,
DoPreProcessing = true,
},
{
Name = "Layered Rock Outcrops in Southwest Candor Chasma",
FilePath = "map_datasets/HiRISE/Layered_Rock_Outcrops_in_Southwest_Candor_Chasma_Heightmap.vrt",
TilePixelSize = 90,
DoPreProcessing = true,
},
--[[
{
Name = "West Candor Chasma",
FilePath = "map_datasets/CTX/West_Candor_Chasma_DEM_longlat_global.vrt",
--Enabled = true,
TilePixelSize = 90,
DoPreProcessing = true,
},]]
--[[
{
Name = "Part of Area Traversed by the Mars Exploration Rover",
FilePath = "map_datasets/HiRISE/Part_of_Area_Traversed_by_the_Mars_Exploration_Rover_Heightmap.vrt",

View File

@@ -42,7 +42,7 @@ return {
Name = "Simple Texture",
FilePath = "textures/mercury.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
{
Name = "Messenger_Mosaic",

View File

@@ -12,6 +12,7 @@
<TileLevel>7</TileLevel>
<YOrigin>top</YOrigin>
</DataWindow>
<DataType>Int16</DataType>
<Projection>GEOGCS["GCS_Moon_2000",DATUM["D_Moon_2000",SPHEROID["Moon_2000_IAU_IAG",1737400.0,0.0]],PRIMEM["Reference_Meridian",0.0],UNIT["Degree",0.0174532925199433]]</Projection>
<BlockSizeX>360</BlockSizeX>
<BlockSizeY>360</BlockSizeY>

View File

@@ -20,8 +20,9 @@ return {
Type = "RenderableGlobe",
Radii = {1738140, 1738140, 1735970}, -- Moons's radius
CameraMinHeight = 300,
InteractionDepthBelowEllipsoid = 5000, -- Useful when having negative height map values
SegmentsPerPatch = 64,
-- Allows camera to go down 10000 meters below the reference ellipsoid
InteractionDepthBelowEllipsoid = 10000, -- Useful when having negative height map values
Layers = {
ColorLayers = {
@@ -63,10 +64,13 @@ return {
FilePath = "map_service_configs/OnMoonHeight.xml",
Enabled = true,
DoPreProcessing = true,
TileSize = 64,
},
{
Name = "LolaDem",
FilePath = "map_service_configs/Utah/LolaDem.wms"
FilePath = "map_service_configs/Utah/LolaDem.wms",
DoPreProcessing = true,
TileSize = 64,
}
},
},

View File

@@ -40,7 +40,7 @@ return {
Name = "Texture",
FilePath = "textures/neptune.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
},
GrayScaleLayers = { },

View File

@@ -40,7 +40,7 @@ return {
Name = "Saturn Texture",
FilePath = "textures/saturn.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
},
GrayScaleLayers = { },

View File

@@ -40,7 +40,7 @@ return {
Name = "Texture",
FilePath = "textures/uranus.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
},
GrayScaleLayers = { },

View File

@@ -45,7 +45,7 @@ return {
Name = "Venus Texture",
FilePath = "textures/venus.jpg",
Enabled = true,
MinimumPixelSize = 256,
TilePixelSize = 256,
},
},
GrayScaleLayers = { },

View File

@@ -25,6 +25,13 @@
include(${OPENSPACE_CMAKE_EXT_DIR}/module_definition.cmake)
set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/lrucache.inl
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarecacheable.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.h
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawarelrucache.inl
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.h
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.h
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.h
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/chunklevelevaluator.h
@@ -57,8 +64,6 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.h
${CMAKE_CURRENT_SOURCE_DIR}/other/concurrentqueue.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/distanceswitch.h
${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.h
${CMAKE_CURRENT_SOURCE_DIR}/other/lrucache.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.h
${CMAKE_CURRENT_SOURCE_DIR}/other/statscollector.inl
${CMAKE_CURRENT_SOURCE_DIR}/other/threadpool.h
@@ -79,15 +84,13 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layermanager.h
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layerrendersettings.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctilereader.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctiledataprovider.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/chunktile.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/pixelregion.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/textureformat.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatalayout.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledataset.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatatype.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledepthtransform.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.h
@@ -106,19 +109,26 @@ set(HEADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/simplerawtiledatareader.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalwrapper.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/iodescription.h
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/tiledatatype.h
)
set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/cache/memoryawaretilecache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunk.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunknode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/availabletiledataevaluator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/distanceevaluator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/chunklevelevaluator/projectedareaevaluator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/frustumculler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/chunk/culling/horizonculler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/geometry/aabb.cpp
${CMAKE_CURRENT_SOURCE_DIR}/geometry/ellipsoid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/geometry/geodetic2.cpp
@@ -153,13 +163,10 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layermanager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rendering/layer/layerrendersettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctilereader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/asynctiledataprovider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/pixelregion.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatalayout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledataset.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tiledatatype.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilediskcache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileindex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tilemetadata.cpp
@@ -175,6 +182,13 @@ set(SOURCE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileprovider.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbyindex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/tileprovider/tileproviderbylevel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/rawtiledatareader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalrawtiledatareader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/simplerawtiledatareader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/gdalwrapper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/iodescription.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile/rawtiledatareader/tiledatatype.cpp
)
source_group("Source Files" FILES ${SOURCE_FILES})
@@ -201,31 +215,34 @@ create_new_module(
${HEADER_FILES} ${SOURCE_FILES} ${SHADER_FILES}
)
if (WIN32)
target_include_directories(
openspace-module-globebrowsing
SYSTEM PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/include
)
option(OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL "Use GDAL" ON)
target_link_libraries(
openspace-module-globebrowsing
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal_i.lib
)
if (OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL)
if (WIN32)
target_include_directories(
openspace-module-globebrowsing
SYSTEM PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/include
)
set(EXTERNAL_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal201.dll" PARENT_SCOPE)
target_link_libraries(
openspace-module-globebrowsing
${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal_i.lib
)
set(EXTERNAL_LIBRARY "${CMAKE_CURRENT_SOURCE_DIR}/ext/gdal/lib/gdal201.dll" PARENT_SCOPE)
else (WIN32)
find_package(GDAL REQUIRED)
else (WIN32)
find_package(GDAL REQUIRED)
target_include_directories(
openspace-module-globebrowsing
SYSTEM PUBLIC
${GDAL_INCLUDE_DIR}
)
target_include_directories(
openspace-module-globebrowsing
SYSTEM PUBLIC
${GDAL_INCLUDE_DIR}
)
target_link_libraries(
openspace-module-globebrowsing
${GDAL_LIBRARY}
)
endif ()
target_link_libraries(
openspace-module-globebrowsing
${GDAL_LIBRARY}
)
endif () # WIN32
target_compile_definitions(openspace-module-globebrowsing PRIVATE GLOBEBROWSING_USE_GDAL)
endif () # OPENSPACE_MODULE_GLOBEBROWSING_USE_GDAL

View File

@@ -30,11 +30,18 @@
namespace openspace {
namespace globebrowsing {
namespace cache {
// Templated class implementing a Least-Recently-Used Cache
/**
* Templated class implementing a Least-Recently-Used Cache.
* <code>KeyType</code> needs to be an enumerable type.
*/
template<typename KeyType, typename ValueType>
class LRUCache {
public:
/**
* \param size is the maximum size of the cache given in number of cached items.
*/
LRUCache(size_t size);
void put(const KeyType& key, const ValueType& value);
@@ -51,9 +58,10 @@ private:
size_t _cacheSize;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#include <modules/globebrowsing/other/lrucache.inl>
#include <modules/globebrowsing/cache/lrucache.inl>
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___LRU_CACHE___H__

View File

@@ -26,6 +26,7 @@
namespace openspace {
namespace globebrowsing {
namespace cache {
template<typename KeyType, typename ValueType>
LRUCache<KeyType, ValueType>::LRUCache(size_t size)
@@ -78,5 +79,6 @@ void LRUCache<KeyType, ValueType>::clean() {
}
}
} // namespace cache
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,58 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__
namespace openspace {
namespace globebrowsing {
namespace cache {
/**
* Base class to be extended by classes that need to be cached and make use of the
* memoryImpact interface. A class extending <code>MemoryAwareCacheable</code> needs to
* know its memory impact at initialization and hence provide the memory impact in its
* constructors. The memory impact can not change during the lifetime of an object that is
* a <code>MemoryAwareCacheable</code>.
*/
class MemoryAwareCacheable {
public:
/**
* \param memoryImpact is the memory impact of the object. Can for example be given
* in kilobytes.
*/
MemoryAwareCacheable(size_t memoryImpact) : _memoryImpact(memoryImpact) {};
~MemoryAwareCacheable() {};
size_t memoryImpact() { return _memoryImpact; };
protected:
size_t _memoryImpact;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_CACHEABLE___H__

View File

@@ -0,0 +1,83 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__
#include <list>
#include <unordered_map>
namespace openspace {
namespace globebrowsing {
namespace cache {
/**
* Least recently used cache that knows about its memory impact. This class is templated
* and the second template argument <code>ValueType</code> needs to have a function
* <code>void memoryImpact()</code> that returns the size of the object given in whatever
* unit is used for size in the creation of the <code>MemoryAwareLRUCache</code>.
* It can for example be given in kilobytes.
* <code>KeyType</code> needs to be a size comparable type.
*/
template<typename KeyType, typename ValueType, typename HasherType>
class MemoryAwareLRUCache {
public:
/**
* \param maximumSize is the maximum size of the <code>MemoryAwareLRUCache</code>
* Once the maximum size is reached, the cache will start removing objects that were
* least recently used. The maximum size can for example be given in kilobytes. It
* must be the same size unit as used by the cached object class
* <code>ValueType</code>.
*/
MemoryAwareLRUCache(size_t maximumSize);
void put(const KeyType& key, const ValueType& value);
void clear();
bool exist(const KeyType& key) const;
ValueType get(const KeyType& key);
size_t size() const;
size_t maximumSize() const;
void setMaximumSize(size_t maximumSize);
private:
void clean();
using Item = std::pair<KeyType, ValueType>;
using Items = std::list<Item>;
Items _itemList;
std::unordered_map<KeyType, decltype(_itemList.begin()), HasherType> _itemMap;
size_t _cacheSize;
size_t _maximumCacheSize;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#include <modules/globebrowsing/cache/memoryawarelrucache.inl>
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_LRU_CACHE___H__

View File

@@ -0,0 +1,100 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <ghoul/misc/assert.h>
namespace openspace {
namespace globebrowsing {
namespace cache {
template<typename KeyType, typename ValueType, typename HasherType>
MemoryAwareLRUCache<KeyType, ValueType, HasherType>::MemoryAwareLRUCache(size_t maximumSize)
: _maximumCacheSize(maximumSize)
, _cacheSize(0)
{}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clear() {
_itemList.clear();
_itemMap.clear();
_cacheSize = 0;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::put(const KeyType& key, const ValueType& value) {
auto it = _itemMap.find(key);
if (it != _itemMap.end()) {
_cacheSize -= it->second->second.memoryImpact();
_itemList.erase(it->second);
_itemMap.erase(it);
}
_itemList.emplace_front(key, value);
_itemMap.emplace(key, _itemList.begin());
_cacheSize += _itemList.begin()->second.memoryImpact();
clean();
}
template<typename KeyType, typename ValueType, typename HasherType>
bool MemoryAwareLRUCache<KeyType, ValueType, HasherType>::exist(const KeyType& key) const {
return _itemMap.count(key) > 0;
}
template<typename KeyType, typename ValueType, typename HasherType>
ValueType MemoryAwareLRUCache<KeyType, ValueType, HasherType>::get(const KeyType& key) {
//ghoul_assert(exist(key), "Key " << key << " must exist");
auto it = _itemMap.at(key);
// Move list iterator pointing to value
_itemList.splice(_itemList.begin(), _itemList, it);
return it->second;
}
template<typename KeyType, typename ValueType, typename HasherType>
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::size() const {
return _cacheSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
size_t MemoryAwareLRUCache<KeyType, ValueType, HasherType>::maximumSize() const {
return _maximumCacheSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::setMaximumSize(size_t maximumSize) {
_maximumCacheSize = maximumSize;
}
template<typename KeyType, typename ValueType, typename HasherType>
void MemoryAwareLRUCache<KeyType, ValueType, HasherType>::clean() {
while (_cacheSize > _maximumCacheSize) {
auto last_it = _itemList.end();
last_it--;
_itemMap.erase(last_it->first);
_cacheSize -= last_it->second.memoryImpact();
_itemList.pop_back();
}
}
} // namespace cache
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,84 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <ghoul/ghoul.h>
#include <ghoul/logging/consolelog.h>
namespace openspace {
namespace globebrowsing {
namespace cache {
MemoryAwareTileCache* MemoryAwareTileCache::_singleton = nullptr;
std::mutex MemoryAwareTileCache::_mutexLock;
void MemoryAwareTileCache::create(size_t cacheSize) {
std::lock_guard<std::mutex> guard(_mutexLock);
_singleton = new MemoryAwareTileCache(cacheSize);
}
void MemoryAwareTileCache::destroy() {
std::lock_guard<std::mutex> guard(_mutexLock);
delete _singleton;
}
MemoryAwareTileCache& MemoryAwareTileCache::ref() {
std::lock_guard<std::mutex> guard(_mutexLock);
ghoul_assert(_singleton, "MemoryAwareTileCache not created");
return *_singleton;
}
void MemoryAwareTileCache::clear() {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.clear();
}
bool MemoryAwareTileCache::exist(ProviderTileKey key) const {
std::lock_guard<std::mutex> guard(_mutexLock);
return _tileCache.exist(key);
}
Tile MemoryAwareTileCache::get(ProviderTileKey key) {
std::lock_guard<std::mutex> guard(_mutexLock);
return _tileCache.get(key);
}
void MemoryAwareTileCache::put(ProviderTileKey key, Tile tile) {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.put(key, tile);
}
void MemoryAwareTileCache::setMaximumSize(size_t maximumSize) {
std::lock_guard<std::mutex> guard(_mutexLock);
_tileCache.setMaximumSize(maximumSize);
}
MemoryAwareTileCache::MemoryAwareTileCache(size_t cacheSize)
: _tileCache(cacheSize) {}
} // namespace cache
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,113 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/cache/memoryawarelrucache.h>
#include <memory>
#include <mutex>
namespace openspace {
namespace globebrowsing {
namespace cache {
struct ProviderTileKey {
TileIndex tileIndex;
unsigned int providerID;
bool operator==(const ProviderTileKey& r) const {
return (providerID == r.providerID) &&
(tileIndex == r.tileIndex);
}
};
struct ProviderTileHasher {
/**
Creates a hash which can be used as key in hash maps.
First set the bits to be unique for all tiles.
+-------+------------+-------+------------+
| USAGE | BIT RANGE | #BITS | MAX VALUE |
+-------+------------+-------+------------+
| level | 0 - 5 | 5 | 31 |
| x | 5 - 35 | 30 | 1073741824 |
| y | 35 - 64 | 29 | 536870912 |
+-------+------------+-------+------------+
Bits are then shifted depending on the tile provider used.
*/
unsigned long long operator()(const ProviderTileKey& t) const {
unsigned long long key = 0;
key |= static_cast<unsigned long long>(t.tileIndex.level);
key |= static_cast<unsigned long long>(t.tileIndex.x << 5);
key |= static_cast<unsigned long long>(t.tileIndex.y << 35);
// Now the key is unique for all tiles, however not for all tile providers.
// Add to the key depending on the tile provider to avoid some hash collisions.
// (All hash collisions can not be avoided due to the limit in 64 bit for the
// hash key)
// Idea: make some offset in the place of the bits for the x value. Lesser chance
// of having different x-value than having different tile provider ids.
key += static_cast<unsigned long long>(t.providerID << 25);
return key;
}
};
/**
* Singleton class used to cache tiles for all <code>CachingTileProvider</code>s.
*/
class MemoryAwareTileCache {
public:
static void create(size_t cacheSize);
static void destroy();
void clear();
bool exist(ProviderTileKey key) const;
Tile get(ProviderTileKey key);
void put(ProviderTileKey key, Tile tile);
void setMaximumSize(size_t maximumSize);
static MemoryAwareTileCache& ref();
private:
/**
* \param cacheSize is the cache size given in bytes.
*/
MemoryAwareTileCache(size_t cacheSize);
~MemoryAwareTileCache() = default;
static MemoryAwareTileCache* _singleton;
MemoryAwareLRUCache<ProviderTileKey, Tile, ProviderTileHasher> _tileCache;
static std::mutex _mutexLock;
};
} // namespace cache
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___MEMORY_AWARE_TILE_CACHE___H__

View File

@@ -29,6 +29,8 @@
#include <modules/globebrowsing/rendering/layer/layermanager.h>
#include <modules/globebrowsing/tile/tileselector.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/rendering/layer/layergroup.h>
namespace openspace {
namespace globebrowsing {
@@ -83,6 +85,8 @@ Chunk::Status Chunk::update(const RenderData& data) {
}
Chunk::BoundingHeights Chunk::getBoundingHeights() const {
using ChunkTileSettingsPair = std::pair<ChunkTile, const LayerRenderSettings*>;
BoundingHeights boundingHeights {
0.f, 0.f,
false
@@ -90,50 +94,58 @@ Chunk::BoundingHeights Chunk::getBoundingHeights() const {
// In the future, this should be abstracted away and more easily queryable.
// One must also handle how to sample pick one out of multiplte heightmaps
auto layerManager = owner().chunkedLodGlobe()->layerManager();
std::shared_ptr<LayerManager> layerManager =
owner().chunkedLodGlobe()->layerManager();
// The raster of a height map is the first one. We assume that the height map is
// a single raster image. If it is not we will just use the first raster
// (that is channel 0).
const size_t HeightChannel = 0;
const LayerGroup& heightmaps = layerManager->layerGroup(LayerManager::HeightLayers);
std::vector<ChunkTile> chunkTiles = tileselector::getTilesSortedByHighestResolution(
heightmaps, _tileIndex
);
std::vector<ChunkTileSettingsPair> chunkTileSettingPairs =
tileselector::getTilesAndSettingsSortedByHighestResolution(
heightmaps, _tileIndex);
bool lastHadMissingData = true;
for (const auto& chunkTile : chunkTiles) {
bool goodTile = (chunkTile.tile.status == Tile::Status::OK);
bool hasTileMetaData = (chunkTile.tile.metaData != nullptr);
for (const ChunkTileSettingsPair& chunkTileSettingsPair : chunkTileSettingPairs) {
ChunkTile chunkTile = chunkTileSettingsPair.first;
const LayerRenderSettings* settings = chunkTileSettingsPair.second;
bool goodTile = (chunkTile.tile.status() == Tile::Status::OK);
bool hasTileMetaData = (chunkTile.tile.metaData() != nullptr);
if (goodTile && hasTileMetaData) {
auto tileMetaData = chunkTile.tile.metaData;
std::shared_ptr<TileMetaData> tileMetaData = chunkTile.tile.metaData();
float minValue =
settings->performLayerSettings(tileMetaData->minValues[HeightChannel]);
float maxValue =
settings->performLayerSettings(tileMetaData->maxValues[HeightChannel]);
if (!boundingHeights.available) {
if (tileMetaData->hasMissingData[HeightChannel]) {
boundingHeights.min = std::min(
DEFAULT_HEIGHT,
tileMetaData->minValues[HeightChannel]
minValue
);
boundingHeights.max = std::max(
DEFAULT_HEIGHT,
tileMetaData->maxValues[HeightChannel]
maxValue
);
}
else {
boundingHeights.min = tileMetaData->minValues[HeightChannel];
boundingHeights.max = tileMetaData->maxValues[HeightChannel];
boundingHeights.min = minValue;
boundingHeights.max = maxValue;
}
boundingHeights.available = true;
}
else {
boundingHeights.min = std::min(
boundingHeights.min,
tileMetaData->minValues[HeightChannel]
minValue
);
boundingHeights.max = std::max(
boundingHeights.max,
tileMetaData->maxValues[HeightChannel]
maxValue
);
}
lastHadMissingData = tileMetaData->hasMissingData[HeightChannel];

View File

@@ -24,8 +24,10 @@
#include <modules/globebrowsing/globebrowsingmodule.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/other/distanceswitch.h>
#include <modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h>
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
#include <modules/globebrowsing/tile/tileprovider/singleimageprovider.h>
#include <modules/globebrowsing/tile/tileprovider/sizereferencetileprovider.h>
@@ -36,19 +38,67 @@
#include <modules/globebrowsing/tile/tileprovider/tileproviderbylevel.h>
#include <modules/globebrowsing/tile/tileprovider/tileproviderbyindex.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderable.h>
#include <openspace/util/factorymanager.h>
#include <ghoul/misc/templatefactory.h>
#include <ghoul/misc/assert.h>
#include <ghoul/systemcapabilities/generalcapabilitiescomponent.h>
namespace openspace {
GlobeBrowsingModule::GlobeBrowsingModule() : OpenSpaceModule("GlobeBrowsing") {}
GlobeBrowsingModule::GlobeBrowsingModule()
: OpenSpaceModule("GlobeBrowsing")
, _openSpaceMaximumTileCacheSize(
"maximumTileCacheSize", "Maximum tile cache size",
512, // Default: 512 MB
0, // Minimum: No caching
1024, // Maximum: 1024 MB
1) // Step: One MB
, _clearTileCache("clearTileCache", "Clear tile cache") {}
void GlobeBrowsingModule::internalInitialize() {
using namespace globebrowsing;
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Initialize, [&] {
// Set maximum cache size to 25% of total RAM
_openSpaceMaximumTileCacheSize.setMaxValue(CpuCap.installedMainMemory() * 0.25);
// Convert from MB to KB
cache::MemoryAwareTileCache::create(_openSpaceMaximumTileCacheSize * 1024);
_openSpaceMaximumTileCacheSize.onChange(
[&]{
// Convert from MB to KB
cache::MemoryAwareTileCache::ref().setMaximumSize(
_openSpaceMaximumTileCacheSize * 1024);
});
_clearTileCache.onChange(
[&]{
cache::MemoryAwareTileCache::ref().clear();
});
addProperty(_openSpaceMaximumTileCacheSize);
addProperty(_clearTileCache);
#ifdef GLOBEBROWSING_USE_GDAL
// Convert from MB to Bytes
GdalWrapper::create(
16ULL * 1024ULL * 1024ULL, // 16 MB
CpuCap.installedMainMemory() * 0.25 * 1024 * 1024); // 25% of total RAM
addPropertySubOwner(GdalWrapper::ref());
#endif // GLOBEBROWSING_USE_GDAL
});
OsEng.registerModuleCallback(OpenSpaceEngine::CallbackOption::Deinitialize, [&]{
cache::MemoryAwareTileCache::ref().clear();
cache::MemoryAwareTileCache::ref().destroy();
#ifdef GLOBEBROWSING_USE_GDAL
GdalWrapper::ref().destroy();
#endif // GLOBEBROWSING_USE_GDAL
});
auto fRenderable = FactoryManager::ref().factory<Renderable>();
ghoul_assert(fRenderable, "Renderable factory was not created");
fRenderable->registerClass<globebrowsing::RenderableGlobe>("RenderableGlobe");
@@ -58,7 +108,10 @@ void GlobeBrowsingModule::internalInitialize() {
fTileProvider->registerClass<tileprovider::CachingTileProvider>("LRUCaching");
fTileProvider->registerClass<tileprovider::SingleImageProvider>("SingleImage");
#ifdef GLOBEBROWSING_USE_GDAL
fTileProvider->registerClass<tileprovider::TemporalTileProvider>("Temporal");
#endif // GLOBEBROWSING_USE_GDAL
fTileProvider->registerClass<tileprovider::TileIndexTileProvider>("TileIndex");
fTileProvider->registerClass<tileprovider::SizeReferenceTileProvider>("SizeReference");

View File

@@ -26,6 +26,10 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___GLOBEBROWSING_MODULE___H__
#include <openspace/util/openspacemodule.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/triggerproperty.h>
#include <memory>
namespace openspace {
@@ -35,6 +39,9 @@ public:
protected:
void internalInitialize() override;
private:
properties::IntProperty _openSpaceMaximumTileCacheSize;
properties::TriggerProperty _clearTileCache;
};
} // namespace openspace

View File

@@ -143,7 +143,8 @@ int ChunkedLodGlobe::getDesiredLevel(
int desiredLevelByAvailableData = _chunkEvaluatorByAvailableTiles->getDesiredLevel(
chunk, renderData
);
if (desiredLevelByAvailableData != chunklevelevaluator::Evaluator::UnknownDesiredLevel) {
if (desiredLevelByAvailableData != chunklevelevaluator::Evaluator::UnknownDesiredLevel &&
_owner.debugProperties().limitLevelByAvailableData) {
desiredLevel = glm::min(desiredLevel, desiredLevelByAvailableData);
}
@@ -180,14 +181,14 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
const auto& tile = chunkTile.tile;
const auto& uvTransform = chunkTile.uvTransform;
const auto& depthTransform = tileProvider->depthTransform();
if (tile.status != Tile::Status::OK) {
if (tile.status() != Tile::Status::OK) {
return 0;
}
glm::vec2 transformedUv = Tile::TileUvToTextureSamplePosition(
uvTransform,
patchUV,
glm::uvec2(tile.texture->dimensions())
glm::uvec2(tile.texture()->dimensions())
);
// Sample and do linear interpolation
@@ -195,7 +196,7 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
// Suggestion: a function in ghoul::opengl::Texture that takes uv coordinates
// in range [0,1] and uses the set interpolation method and clamping.
glm::uvec3 dimensions = tile.texture->dimensions();
glm::uvec3 dimensions = tile.texture()->dimensions();
glm::vec2 samplePos = transformedUv * glm::vec2(dimensions);
glm::uvec2 samplePos00 = samplePos;
@@ -219,10 +220,10 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
glm::uvec2(dimensions) - glm::uvec2(1)
);
float sample00 = tile.texture->texelAsFloat(samplePos00).x;
float sample10 = tile.texture->texelAsFloat(samplePos10).x;
float sample01 = tile.texture->texelAsFloat(samplePos01).x;
float sample11 = tile.texture->texelAsFloat(samplePos11).x;
float sample00 = tile.texture()->texelAsFloat(samplePos00).x;
float sample10 = tile.texture()->texelAsFloat(samplePos10).x;
float sample01 = tile.texture()->texelAsFloat(samplePos01).x;
float sample11 = tile.texture()->texelAsFloat(samplePos11).x;
// In case the texture has NaN or no data values don't use this height map.
bool anySampleIsNaN =
@@ -248,6 +249,10 @@ float ChunkedLodGlobe::getHeight(glm::dvec3 position) const {
// Perform depth transform to get the value in meters
height = depthTransform.depthOffset + depthTransform.depthScale * sample;
// Make sure that the height value follows the layer settings.
// For example if the multiplier is set to a value bigger than one,
// the sampled height should be modified as well.
height = layer->renderSettings().performLayerSettings(height);
}
// Return the result
return height;

View File

@@ -61,11 +61,12 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
BoolProperty("showHeightIntensities", "show height intensities", false),
BoolProperty("performFrustumCulling", "perform frustum culling", true),
BoolProperty("performHorizonCulling", "perform horizon culling", true),
BoolProperty("levelByProjectedAreaElseDistance", "level by projected area (else distance)",false),
BoolProperty("levelByProjectedAreaElseDistance", "level by projected area (else distance)", true),
BoolProperty("resetTileProviders", "reset tile providers", false),
BoolProperty("toggleEnabledEveryFrame", "toggle enabled every frame", false),
BoolProperty("collectStats", "collect stats", false),
BoolProperty("onlyModelSpaceRendering", "Only Model Space Rendering", false)
BoolProperty("limitLevelByAvailableData", "Limit level by available data", true),
IntProperty("modelSpaceRenderingCutoffLevel", "Model Space Rendering Cutoff Level", 10, 1, 22)
})
, _texturePropertyOwner("Textures")
{
@@ -84,16 +85,21 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
dictionary.getValue(keySegmentsPerPatch, patchSegmentsd);
int patchSegments = patchSegmentsd;
dictionary.getValue(keyInteractionDepthBelowEllipsoid,
_interactionDepthBelowEllipsoid);
if (!dictionary.getValue(keyInteractionDepthBelowEllipsoid,
_interactionDepthBelowEllipsoid)) {
_interactionDepthBelowEllipsoid = 0;
}
float cameraMinHeight;
dictionary.getValue(keyCameraMinHeight, cameraMinHeight);
_generalProperties.cameraMinHeight.set(cameraMinHeight);
// Init layer manager
ghoul::Dictionary layersDictionary;
if (!dictionary.getValue(keyLayers, layersDictionary))
throw ghoul::RuntimeError(std::string(keyLayers) + " must be specified specified!");
if (!dictionary.getValue(keyLayers, layersDictionary)) {
throw ghoul::RuntimeError(
std::string(keyLayers) + " must be specified specified!");
}
_layerManager = std::make_shared<LayerManager>(layersDictionary);
@@ -127,8 +133,9 @@ RenderableGlobe::RenderableGlobe(const ghoul::Dictionary& dictionary)
_debugPropertyOwner.addProperty(_debugProperties.resetTileProviders);
_debugPropertyOwner.addProperty(_debugProperties.toggleEnabledEveryFrame);
_debugPropertyOwner.addProperty(_debugProperties.collectStats);
_debugPropertyOwner.addProperty(_debugProperties.onlyModelSpaceRendering);
_debugPropertyOwner.addProperty(_debugProperties.limitLevelByAvailableData);
_debugPropertyOwner.addProperty(_debugProperties.modelSpaceRenderingCutoffLevel);
addPropertySubOwner(_debugPropertyOwner);
addPropertySubOwner(_layerManager.get());
}

View File

@@ -31,6 +31,7 @@
#include <modules/globebrowsing/other/distanceswitch.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
namespace openspace {
namespace globebrowsing {
@@ -64,7 +65,8 @@ public:
properties::BoolProperty resetTileProviders;
properties::BoolProperty toggleEnabledEveryFrame;
properties::BoolProperty collectStats;
properties::BoolProperty onlyModelSpaceRendering;
properties::BoolProperty limitLevelByAvailableData;
properties::IntProperty modelSpaceRenderingCutoffLevel;
};
struct GeneralProperties {

View File

@@ -32,6 +32,7 @@ namespace globebrowsing {
DistanceSwitch::~DistanceSwitch() {}
bool DistanceSwitch::initialize() {
_objectScale = 1.0;
for (int i = 0; i < _renderables.size(); ++i) {
_renderables[i]->initialize();
}
@@ -54,13 +55,13 @@ void DistanceSwitch::render(const RenderData& data) {
pss pssDistanceToCamera = (data.camera.position() - data.position).length();
double distanceToCamera = pssDistanceToCamera.lengthd();
if (distanceToCamera > _maxDistances.back()) {
if (distanceToCamera > _maxDistances.back() * _objectScale) {
return;
}
// linear search through nodes to find which Renderable to render
for (int i = 0; i < _renderables.size(); ++i) {
if (distanceToCamera < _maxDistances[i]) {
if (distanceToCamera < _maxDistances[i] * _objectScale) {
_renderables[i]->render(data);
return;
}
@@ -68,6 +69,7 @@ void DistanceSwitch::render(const RenderData& data) {
}
void DistanceSwitch::update(const UpdateData& data) {
_objectScale = data.modelTransform.scale;
for (int i = 0; i < _renderables.size(); ++i) {
_renderables[i]->update(data);
}

View File

@@ -63,6 +63,7 @@ public:
private:
std::vector<std::shared_ptr<Renderable>> _renderables;
std::vector<double> _maxDistances;
double _objectScale;
};
} // namespace globebrowsing

View File

@@ -30,6 +30,7 @@
#include <modules/globebrowsing/rendering/layershadermanager.h>
#include <modules/globebrowsing/rendering/gpu/gpulayermanager.h>
#include <modules/globebrowsing/rendering/layer/layergroup.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
namespace {
const char* keyFrame = "Frame";
@@ -64,7 +65,8 @@ ChunkRenderer::ChunkRenderer(std::shared_ptr<Grid> grid,
void ChunkRenderer::renderChunk(const Chunk& chunk, const RenderData& data) {
// A little arbitrary with 10 but it works
if (chunk.owner().debugProperties().onlyModelSpaceRendering || chunk.tileIndex().level < 10) {
if (chunk.tileIndex().level <
chunk.owner().debugProperties().modelSpaceRenderingCutoffLevel) {
renderChunkGlobally(chunk, data);
}
else {
@@ -98,18 +100,25 @@ ghoul::opengl::ProgramObject* ChunkRenderer::getActivatedProgramWithTileData(
const auto& debugProps = chunk.owner().debugProperties();
auto& pairs = layeredTexturePreprocessingData.keyValuePairs;
pairs.push_back(std::make_pair("useAtmosphere",
std::to_string(generalProps.atmosphereEnabled)));
pairs.push_back(std::make_pair("performShading",
std::to_string(generalProps.performShading)));
pairs.push_back(std::make_pair("showChunkEdges",
std::to_string(debugProps.showChunkEdges)));
pairs.push_back(std::make_pair("showHeightResolution",
std::to_string(debugProps.showHeightResolution)));
pairs.push_back(std::make_pair("showHeightIntensities",
std::to_string(debugProps.showHeightIntensities)));
pairs.push_back(std::make_pair("defaultHeight",
std::to_string(Chunk::DEFAULT_HEIGHT)));
pairs.emplace_back("useAtmosphere", std::to_string(generalProps.atmosphereEnabled));
pairs.emplace_back("performShading", std::to_string(generalProps.performShading));
pairs.emplace_back("showChunkEdges", std::to_string(debugProps.showChunkEdges));
pairs.emplace_back("showHeightResolution",
std::to_string(debugProps.showHeightResolution));
pairs.emplace_back("showHeightIntensities",
std::to_string(debugProps.showHeightIntensities));
pairs.emplace_back("defaultHeight", std::to_string(Chunk::DEFAULT_HEIGHT));
pairs.emplace_back("tilePaddingStart",
"ivec2(" +
std::to_string(RawTileDataReader::padding.start.x) + "," +
std::to_string(RawTileDataReader::padding.start.y) + ")"
);
pairs.emplace_back("tilePaddingSizeDiff",
"ivec2(" +
std::to_string(RawTileDataReader::padding.numPixels.x) + "," +
std::to_string(RawTileDataReader::padding.numPixels.y) + ")"
);
// Now the shader program can be accessed
ghoul::opengl::ProgramObject* programObject =

View File

@@ -33,7 +33,7 @@ namespace globebrowsing {
void GPUChunkTile::setValue(ghoul::opengl::ProgramObject* programObject,
const ChunkTile& chunkTile)
{
gpuTexture.setValue(programObject, chunkTile.tile.texture);
gpuTexture.setValue(programObject, chunkTile.tile.texture());
gpuTileUvTransform.setValue(programObject, chunkTile.uvTransform);
}

View File

@@ -47,6 +47,7 @@ Layer::Layer(const ghoul::Dictionary& layerDict)
addProperty(_enabled);
addPropertySubOwner(_renderSettings);
addPropertySubOwner(*_tileProvider);
}
ChunkTilePile Layer::getChunkTilePile(const TileIndex& tileIndex, int pileSize) const {

View File

@@ -31,7 +31,7 @@ namespace globebrowsing {
LayerGroup::LayerGroup(std::string name)
: properties::PropertyOwner(std::move(name))
, _levelBlendingEnabled("blendTileLevels", "blend tile levels", true)
, _levelBlendingEnabled("blendTileLevels", "blend tile levels", false)
{
addProperty(_levelBlendingEnabled);
}

View File

@@ -38,5 +38,25 @@ LayerRenderSettings::LayerRenderSettings()
addProperty(multiplier);
}
float LayerRenderSettings::performLayerSettings(float currentValue) const {
float newValue = currentValue;
newValue = glm::sign(newValue) * glm::pow(glm::abs(newValue), gamma);
newValue = newValue * multiplier;
newValue = newValue * opacity;
return newValue;
}
glm::vec4 LayerRenderSettings::performLayerSettings(glm::vec4 currentValue) const {
glm::vec4 newValue = glm::vec4(
performLayerSettings(currentValue.r),
performLayerSettings(currentValue.g),
performLayerSettings(currentValue.b),
performLayerSettings(currentValue.a));
return newValue;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -37,6 +37,13 @@ struct LayerRenderSettings : public properties::PropertyOwner {
properties::FloatProperty opacity;
properties::FloatProperty gamma;
properties::FloatProperty multiplier;
/// This function matches the function with the same name in the
/// shader code
float performLayerSettings(float currentValue) const;
/// This function matches the function with the same name in the
/// shader code
glm::vec4 performLayerSettings(glm::vec4 currentValue) const;
};
} // namespace globebrowsing

View File

@@ -28,8 +28,8 @@
// Must match the values in tiledataset.cpp
// (could be set as shader preprocessing data so that multiple definitions
// are not needed)
#define TILE_PIXEL_START_OFFSET ivec2(-2)
#define TILE_PIXEL_SIZE_DIFFERENCE ivec2(4)
#define TILE_PIXEL_START_OFFSET #{tilePaddingStart}
#define TILE_PIXEL_SIZE_DIFFERENCE #{tilePaddingSizeDiff}
vec4 patchBorderOverlay(vec2 uv, vec3 borderColor, float borderSize) {
vec2 uvOffset = uv - vec2(0.5);
@@ -87,7 +87,7 @@ vec2 compensateSourceTextureSampling(vec2 startOffset, vec2 sizeDiff, const Chun
vec2 TileUVToTextureSamplePosition(const ChunkTile chunkTile, vec2 tileUV){
vec2 uv = chunkTile.uvTransform.uvOffset + chunkTile.uvTransform.uvScale * tileUV;
uv = compensateSourceTextureSampling(vec2(-2), vec2(4), chunkTile, uv);
uv = compensateSourceTextureSampling(TILE_PIXEL_START_OFFSET, TILE_PIXEL_SIZE_DIFFERENCE, chunkTile, uv);
return uv;
}

View File

@@ -22,28 +22,29 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/asynctilereader.h>
#include <modules/globebrowsing/tile/asynctiledataprovider.h>
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/tilediskcache.h>
namespace openspace {
namespace globebrowsing {
AsyncTileDataProvider::AsyncTileDataProvider(std::shared_ptr<TileDataset> tileDataset,
std::shared_ptr<ThreadPool> pool)
: _tileDataset(tileDataset)
AsyncTileDataProvider::AsyncTileDataProvider(
const std::shared_ptr<RawTileDataReader> rawTileDataReader,
std::shared_ptr<ThreadPool> pool)
: _rawTileDataReader(rawTileDataReader)
, _concurrentJobManager(pool)
{}
std::shared_ptr<TileDataset> AsyncTileDataProvider::getTextureDataProvider() const {
return _tileDataset;
std::shared_ptr<RawTileDataReader> AsyncTileDataProvider::getRawTileDataReader() const {
return _rawTileDataReader;
}
bool AsyncTileDataProvider::enqueueTileIO(const TileIndex& tileIndex) {
if (satisfiesEnqueueCriteria(tileIndex)) {
auto job = std::make_shared<TileLoadJob>(_tileDataset, tileIndex);
auto job = std::make_shared<TileLoadJob>(_rawTileDataReader, tileIndex);
//auto job = std::make_shared<DiskCachedTileLoadJob>(_tileDataset, tileIndex, tileDiskCache, "ReadAndWrite");
_concurrentJobManager.enqueueJob(job);
_enqueuedTileRequests[tileIndex.hashKey()] = tileIndex;
@@ -61,6 +62,13 @@ std::vector<std::shared_ptr<RawTile>> AsyncTileDataProvider::getRawTiles() {
return readyResults;
}
std::shared_ptr<RawTile> AsyncTileDataProvider::popFinishedRawTile() {
if (_concurrentJobManager.numFinishedJobs() > 0)
return _concurrentJobManager.popFinishedJob()->product();
else
return nullptr;
}
bool AsyncTileDataProvider::satisfiesEnqueueCriteria(const TileIndex& tileIndex) const {
// only allow tile to be enqueued if it's not already enqueued
//return _futureTileIOResults.find(tileIndex.hashKey()) == _futureTileIOResults.end();
@@ -77,7 +85,7 @@ void AsyncTileDataProvider::reset() {
while (_concurrentJobManager.numFinishedJobs() > 0) {
_concurrentJobManager.popFinishedJob();
}
getTextureDataProvider()->reset();
_rawTileDataReader->reset();
}
void AsyncTileDataProvider::clearRequestQueue() {
@@ -87,8 +95,8 @@ void AsyncTileDataProvider::clearRequestQueue() {
_enqueuedTileRequests.clear();
}
float AsyncTileDataProvider::noDataValueAsFloat() {
return _tileDataset->noDataValueAsFloat();
float AsyncTileDataProvider::noDataValueAsFloat() const {
return _rawTileDataReader->noDataValueAsFloat();
}
} // namespace globebrowsing

View File

@@ -26,7 +26,6 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___ASYNC_TILE_READER___H__
#include <modules/globebrowsing/other/concurrentjobmanager.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <unordered_map>
@@ -34,28 +33,29 @@
namespace openspace {
namespace globebrowsing {
class RawTile;
class TileDataset;
struct RawTile;
class RawTileDataReader;
class AsyncTileDataProvider {
public:
AsyncTileDataProvider(std::shared_ptr<TileDataset> textureDataProvider,
AsyncTileDataProvider(std::shared_ptr<RawTileDataReader> textureDataProvider,
std::shared_ptr<ThreadPool> pool);
bool enqueueTileIO(const TileIndex& tileIndex);
std::vector<std::shared_ptr<RawTile>> getRawTiles();
std::shared_ptr<RawTile> popFinishedRawTile();
void reset();
void clearRequestQueue();
std::shared_ptr<TileDataset> getTextureDataProvider() const;
float noDataValueAsFloat();
std::shared_ptr<RawTileDataReader> getRawTileDataReader() const;
float noDataValueAsFloat() const;
protected:
virtual bool satisfiesEnqueueCriteria(const TileIndex&) const;
private:
std::shared_ptr<TileDataset> _tileDataset;
std::shared_ptr<RawTileDataReader> _rawTileDataReader;
ConcurrentJobManager<RawTile> _concurrentJobManager;
std::unordered_map<TileIndex::TileHashKey, TileIndex> _enqueuedTileRequests;
};

View File

@@ -34,6 +34,12 @@ namespace openspace {
namespace globebrowsing {
struct ChunkTile {
ChunkTile() : tile(Tile::TileUnavailable) {};
ChunkTile(Tile tile, TileUvTransform uvTransform, TileDepthTransform depthTransform) :
tile(tile),
uvTransform(uvTransform),
depthTransform(depthTransform) {};
Tile tile;
TileUvTransform uvTransform;
TileDepthTransform depthTransform;

View File

@@ -25,17 +25,16 @@
#include <modules/globebrowsing/tile/loadjob/diskcachedtileloadjob.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/tilediskcache.h>
namespace openspace {
namespace globebrowsing {
DiskCachedTileLoadJob::DiskCachedTileLoadJob(
std::shared_ptr<TileDataset> textureDataProvider,
const TileIndex& tileIndex,
std::shared_ptr<TileDiskCache> tdc,
CacheMode m)
std::shared_ptr<RawTileDataReader> textureDataProvider,
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
CacheMode m)
: TileLoadJob(textureDataProvider, tileIndex)
, _tileDiskCache(tdc)
, _mode(m)
@@ -46,26 +45,26 @@ void DiskCachedTileLoadJob::execute() {
switch (_mode) {
case CacheMode::Disabled:
_rawTile = _tileDataset->readTileData(_chunkIndex);
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
break;
case CacheMode::ReadOnly:
_rawTile = _tileDiskCache->get(_chunkIndex);
if (_rawTile == nullptr) {
_rawTile = _tileDataset->readTileData(_chunkIndex);
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
}
break;
case CacheMode::ReadAndWrite:
_rawTile = _tileDiskCache->get(_chunkIndex);
if (_rawTile == nullptr) {
_rawTile = _tileDataset->readTileData(_chunkIndex);
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
_tileDiskCache->put(_chunkIndex, _rawTile);
}
break;
case CacheMode::WriteOnly:
_rawTile = _tileDataset->readTileData(_chunkIndex);
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
_tileDiskCache->put(_chunkIndex, _rawTile);
break;

View File

@@ -41,7 +41,7 @@ struct DiskCachedTileLoadJob : public TileLoadJob {
CacheHitsOnly,
};
DiskCachedTileLoadJob(std::shared_ptr<TileDataset> textureDataProvider,
DiskCachedTileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
const TileIndex& tileIndex, std::shared_ptr<TileDiskCache> tdc,
CacheMode cacheMode = CacheMode::ReadOnly);

View File

@@ -32,7 +32,7 @@
namespace openspace {
namespace globebrowsing {
class RawTile;
struct RawTile;
struct LoadJob : public Job<RawTile> {
virtual void execute() = 0;

View File

@@ -24,13 +24,18 @@
#include <modules/globebrowsing/tile/loadjob/tileloadjob.h>
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
namespace openspace {
namespace globebrowsing {
TileLoadJob::TileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
const TileIndex& tileIndex)
: _rawTileDataReader(rawTileDataReader)
, _chunkIndex(tileIndex) {}
void TileLoadJob::execute() {
_rawTile = _tileDataset->readTileData(_chunkIndex);
_rawTile = _rawTileDataReader->readTileData(_chunkIndex);
}
std::shared_ptr<RawTile> TileLoadJob::product() const {

View File

@@ -33,15 +33,12 @@
namespace openspace {
namespace globebrowsing {
class TileDataset;
class RawTile;
class RawTileDataReader;
struct RawTile;
struct TileLoadJob : LoadJob {
TileLoadJob(std::shared_ptr<TileDataset> textureDataProvider,
const TileIndex& tileIndex)
: _tileDataset(textureDataProvider)
, _chunkIndex(tileIndex)
{}
TileLoadJob(std::shared_ptr<RawTileDataReader> rawTileDataReader,
const TileIndex& tileIndex);
virtual ~TileLoadJob() = default;
@@ -51,7 +48,7 @@ struct TileLoadJob : LoadJob {
protected:
TileIndex _chunkIndex;
std::shared_ptr<TileDataset> _tileDataset;
std::shared_ptr<RawTileDataReader> _rawTileDataReader;
std::shared_ptr<RawTile> _rawTile;
};

View File

@@ -119,10 +119,12 @@ void PixelRegion::scale(double s) {
void PixelRegion::downscalePow2(int exponent, PixelCoordinate wrt) {
start += wrt;
start.x >>= exponent;
start.y >>= exponent;
numPixels.x >>= exponent;
numPixels.y >>= exponent;
start.x = ceil(start.x / static_cast<float>(pow(2, exponent)));// >>= exponent;
start.y = ceil(start.y / static_cast<float>(pow(2, exponent)));// >>= exponent;
numPixels.x =
ceil(numPixels.x / static_cast<float>(pow(2, exponent)));// >>= exponent;
numPixels.y =
ceil(numPixels.y / static_cast<float>(pow(2, exponent)));// >>= exponent;
start -= wrt;
}
@@ -170,15 +172,20 @@ void PixelRegion::forceNumPixelToDifferByNearestMultipleOf(unsigned int multiple
numPixels.y += sizeDiff % multiple;
}
else {
numPixels.x += static_cast<int>(std::abs(static_cast<double>(sizeDiff))) % multiple;
numPixels.x += static_cast<int>(
std::abs(static_cast<double>(sizeDiff))) % multiple;
}
}
}
void PixelRegion::roundUpNumPixelToNearestMultipleOf(unsigned int multiple) {
ghoul_assert(multiple > 0, "multiple must be 1 or larger");
numPixels.x += numPixels.x % multiple;
numPixels.y += numPixels.y % multiple;
numPixels.x += (numPixels.x % multiple == 0) ? 0 :
(multiple - (numPixels.x % multiple));
numPixels.y += (numPixels.y % multiple == 0) ? 0 :
(multiple - (numPixels.y % multiple));
ghoul_assert((numPixels.x % multiple) == 0, "Round to nearest multiple failed");
ghoul_assert((numPixels.y % multiple) == 0, "Round to nearest multiple failed");
}
void PixelRegion::roundDownToQuadratic() {

View File

@@ -73,8 +73,8 @@ struct PixelRegion {
void scale(const glm::dvec2& s);
void scale(double s);
void downscalePow2(int exponent, PixelCoordinate wrt = {0,0});
void upscalePow2(int exponent, PixelCoordinate wrt = { 0,0 });
void downscalePow2(int exponent, PixelCoordinate wrt = { 0, 0 });
void upscalePow2(int exponent, PixelCoordinate wrt = { 0, 0 });
void move(Side side, int amount);
void pad(const PixelRegion& padding);

View File

@@ -26,10 +26,8 @@
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <gdal_priv.h>
namespace {
const std::string _loggerCat = "Tile";
const char* _loggerCat = "RawTile";
}
namespace openspace {
@@ -40,7 +38,7 @@ RawTile::RawTile()
, dimensions(0, 0, 0)
, tileMetaData(nullptr)
, tileIndex(0, 0, 0)
, error(CE_None)
, error(ReadError::None)
, nBytesImageData(0)
{}
@@ -58,7 +56,7 @@ RawTile RawTile::createDefaultRes() {
void RawTile::serializeMetaData(std::ostream& os) {
os << dimensions.x << " " << dimensions.y << " " << dimensions.z << std::endl;
os << tileIndex.x << " " << tileIndex.y << " " << tileIndex.level << std::endl;
os << error << std::endl;
os << static_cast<int>(error) << std::endl;
// preprocess data
os << (tileMetaData != nullptr) << std::endl;
@@ -73,7 +71,7 @@ RawTile RawTile::deserializeMetaData(std::istream& is) {
RawTile res;
is >> res.dimensions.x >> res.dimensions.y >> res.dimensions.z;
is >> res.tileIndex.x >> res.tileIndex.y >> res.tileIndex.level;
int err; is >> err; res.error = (CPLErr) err;
int err; is >> err; res.error = static_cast<ReadError>(err);
res.tileMetaData = nullptr;
bool hastileMetaData;

View File

@@ -26,28 +26,38 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___RAWTILE___H__
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <ghoul/glm.h>
#include <memory>
#include <sstream>
#include <cpl_error.h>
namespace openspace {
namespace globebrowsing {
struct TileMetaData;
struct RawTile {
enum class ReadError {
None = 0,
Debug = 1,
Warning = 2,
Failure = 3,
Fatal = 4
};
RawTile();
char* imageData;
glm::uvec3 dimensions;
std::shared_ptr<TileMetaData> tileMetaData;
TileIndex tileIndex;
CPLErr error;
ReadError error;
size_t nBytesImageData;
GLuint glType;
TextureFormat textureFormat;
void serializeMetaData(std::ostream& s);
static RawTile deserializeMetaData(std::istream& s);

View File

@@ -0,0 +1,408 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifdef GLOBEBROWSING_USE_GDAL
#include <modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
#include <ghoul/filesystem/file.h>
#include <ghoul/misc/assert.h>
#include <ghoul/misc/dictionary.h>
#include <ogr_featurestyle.h>
#include <ogr_spatialref.h>
#include <cpl_virtualmem.h>
#include <gdal_priv.h>
namespace {
const std::string _loggerCat = "GdalRawTileDataReader";
}
namespace openspace {
namespace globebrowsing {
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x <<
", " << pr.numPixels.y;
}
GdalRawTileDataReader::GdalRawTileDataReader(
const std::string& filePath, const Configuration& config)
: RawTileDataReader(config)
, _dataset(nullptr)
{
_initData = { "", filePath, config.tilePixelSize, config.dataType };
_initData.initDirectory = CPLGetCurrentDir();
ensureInitialized();
}
GdalRawTileDataReader::~GdalRawTileDataReader() {
if (_dataset != nullptr) {
GDALClose((GDALDatasetH)_dataset);
_dataset = nullptr;
}
}
void GdalRawTileDataReader::reset() {
_cached._maxLevel = -1;
if (_dataset != nullptr) {
GDALClose((GDALDatasetH)_dataset);
_dataset = nullptr;
}
initialize();
}
int GdalRawTileDataReader::maxChunkLevel() {
ensureInitialized();
if (_cached._maxLevel < 0) {
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
_cached._maxLevel = -_cached._tileLevelDifference;
if (numOverviews > 0) {
_cached._maxLevel += numOverviews - 1;
}
}
return _cached._maxLevel;
}
float GdalRawTileDataReader::noDataValueAsFloat() const {
float noDataValue;
if (_dataset && _dataset->GetRasterBand(1)) {
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();;
}
else {
noDataValue = std::numeric_limits<float>::min();
}
return noDataValue;
}
int GdalRawTileDataReader::rasterXSize() const {
return _dataset->GetRasterXSize();
}
int GdalRawTileDataReader::rasterYSize() const {
return _dataset->GetRasterYSize();
}
float GdalRawTileDataReader::depthOffset() const {
return _dataset->GetRasterBand(1)->GetOffset();
}
float GdalRawTileDataReader::depthScale() const {
return _dataset->GetRasterBand(1)->GetScale();
}
std::array<double, 6> GdalRawTileDataReader::getGeoTransform() const {
std::array<double, 6> padfTransform;
CPLErr err = _dataset->GetGeoTransform(&padfTransform[0]);
if (err == CE_Failure) {
return RawTileDataReader::getGeoTransform();
}
return padfTransform;
}
IODescription GdalRawTileDataReader::getIODescription(const TileIndex& tileIndex) const {
IODescription io;
io.read.region = highestResPixelRegion(tileIndex);
// write region starts in origin
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
io.write.region.numPixels = PixelRegion::PixelCoordinate(_initData.tilePixelSize, _initData.tilePixelSize);
io.read.overview = 0;
io.read.fullRegion = gdalPixelRegion(
_dataset->GetRasterBand(1));
// For correct sampling in dataset, we need to pad the texture tile
PixelRegion scaledPadding = padding;
double scale = io.read.region.numPixels.x / static_cast<double>(io.write.region.numPixels.x);
scaledPadding.numPixels *= scale;
scaledPadding.start *= scale;
io.read.region.pad(scaledPadding);
io.write.region.pad(padding);
io.write.region.start = PixelRegion::PixelCoordinate(0, 0);
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
// OpenGL does not like if the number of bytes per line is not 4
if (io.write.bytesPerLine % 4 != 0) {
io.write.region.roundUpNumPixelToNearestMultipleOf(4);
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
}
return io;
}
void GdalRawTileDataReader::initialize() {
_dataset = openGdalDataset(_initData.datasetFilePath);
//Do any other initialization needed for the GdalRawTileDataReader
_dataLayout = getTileDataLayout(_initData.dataType);
_depthTransform = calculateTileDepthTransform();
_cached._tileLevelDifference =
calculateTileLevelDifference(_initData.tilePixelSize);
}
char* GdalRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError) const {
// allocate memory for the image
char* imageData = new char[io.write.totalNumBytes];
// In case there are extra channels not existing in the GDAL dataset
// we set the bytes to 255 (for example an extra alpha channel that)
// needs to be 1.
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
memset(imageData, 255, io.write.totalNumBytes);
}
if (_dataLayout.numRastersAvailable == 3) // RGB -> BGR
{
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
// 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);
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
}
else if (_dataLayout.numRastersAvailable == 4) // RGBA -> BGRA
{
for (size_t i = 0; i < 3; i++) {
// 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);
RawTile::ReadError err = repeatedRasterRead(3 - i, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
// 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 + (3 * _dataLayout.bytesPerDatum);
RawTile::ReadError err = repeatedRasterRead(4, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
else
{
// Read the data (each rasterband is a separate channel)
for (size_t i = 0; i < _dataLayout.numRastersAvailable; i++) {
GDALRasterBand* rasterBand = _dataset->GetRasterBand(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);
RawTile::ReadError err = repeatedRasterRead(i + 1, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
}
// GDAL reads pixel lines top to bottom, we want the opposite
return imageData;
}
RawTile::ReadError GdalRawTileDataReader::rasterRead(
int rasterBand, const IODescription& io, char* dataDestination) const
{
ghoul_assert(io.read.region.isInside(io.read.fullRegion), "write region of bounds!");
ghoul_assert(
io.write.region.start.x >= 0 && io.write.region.start.y >= 0,
"Invalid write region"
);
PixelRegion::PixelCoordinate end = io.write.region.end();
size_t largestIndex =
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _dataLayout.bytesPerPixel;
ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region");
char* dataDest = dataDestination;
// 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
dataDest += (io.write.totalNumBytes - io.write.bytesPerLine);
// handle requested write region. Note -= since flipped y axis
dataDest -= io.write.region.start.y * io.write.bytesPerLine;
dataDest += io.write.region.start.x * _dataLayout.bytesPerPixel;
CPLErr readError = _dataset->GetRasterBand(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
dataDest, // 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
_gdalType, // Type
_dataLayout.bytesPerPixel, // Pixel spacing
-io.write.bytesPerLine // Line spacing
);
// Convert error to RawTile::ReadError
RawTile::ReadError error;
switch (readError) {
case CE_None: error = RawTile::ReadError::None; break;
case CE_Debug: error = RawTile::ReadError::Debug; break;
case CE_Warning: error = RawTile::ReadError::Warning; break;
case CE_Failure: error = RawTile::ReadError::Failure; break;
case CE_Fatal: error = RawTile::ReadError::Fatal; break;
default: error = RawTile::ReadError::Failure; break;
}
return error;
}
GDALDataset* GdalRawTileDataReader::openGdalDataset(const std::string& filePath) {
GDALDataset* dataset = static_cast<GDALDataset*>(GDALOpen(filePath.c_str(), GA_ReadOnly));
if (!dataset) {
using namespace ghoul::filesystem;
std::string correctedPath = FileSystem::ref().pathByAppendingComponent(
_initData.initDirectory, filePath
);
dataset = (GDALDataset *)GDALOpen(correctedPath.c_str(), GA_ReadOnly);
if (!dataset) {
throw ghoul::RuntimeError("Failed to load dataset:\n" + filePath);
}
}
return dataset;
}
int GdalRawTileDataReader::calculateTileLevelDifference(int minimumPixelSize) {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
GDALRasterBand* maxOverview;
int numOverviews = firstBand->GetOverviewCount();
int sizeLevel0;
if (numOverviews <= 0) { // No overviews. Use first band.
maxOverview = firstBand;
}
else { // Pick the highest overview.
maxOverview = firstBand->GetOverview(numOverviews - 1);
}
sizeLevel0 = maxOverview->GetXSize();
double diff = log2(minimumPixelSize) - log2(sizeLevel0);
return diff;
}
bool GdalRawTileDataReader::gdalHasOverviews() const {
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
}
int GdalRawTileDataReader::gdalOverview(
const PixelRegion::PixelRange& 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) -
_cached._tileLevelDifference;
ov = glm::clamp(ov, 0, overviews - 1);
return ov;
}
int GdalRawTileDataReader::gdalOverview(const TileIndex& tileIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
return glm::clamp(ov, 0, overviews - 1);
}
int GdalRawTileDataReader::gdalVirtualOverview(const TileIndex& tileIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
return ov;
}
PixelRegion GdalRawTileDataReader::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;
}
TileDataLayout GdalRawTileDataReader::getTileDataLayout(GLuint preferredGlType) {
TileDataLayout layout;
// Assume all raster bands have the same data type
_gdalType = preferredGlType != 0 ?
tiledatatype::getGdalDataType(preferredGlType) :
_dataset->GetRasterBand(1)->GetRasterDataType();
layout.glType = tiledatatype::getOpenGLDataType(_gdalType);
layout.numRastersAvailable = _dataset->GetRasterCount();
layout.numRasters = layout.numRastersAvailable;
// This is to avoid corrupted textures that can appear when the number of
// bytes per row is not a multiplie of 4.
// Info here: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_layout
// This also mean that we need to make sure not to read from non existing
// rasters from the GDAL dataset
if (layout.numRasters == 3) {
layout.numRasters = 4;
}
layout.bytesPerDatum = tiledatatype::numberOfBytes(_gdalType);
layout.bytesPerPixel = layout.bytesPerDatum * layout.numRasters;
layout.textureFormat = tiledatatype::getTextureFormatOptimized(layout.numRasters, _gdalType);
return layout;
}
} // namespace globebrowsing
} // namespace openspace
#endif // GLOBEBROWSING_USE_GDAL

View File

@@ -0,0 +1,123 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_RAW_TILE_DATA_READER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_RAW_TILE_DATA_READER___H__
#ifdef GLOBEBROWSING_USE_GDAL
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <gdal.h>
#include <string>
class GDALDataset;
class GDALRasterBand;
namespace openspace {
namespace globebrowsing {
class GeodeticPatch;
class GdalRawTileDataReader : public RawTileDataReader {
public:
/**
* Opens a GDALDataset in readonly mode and calculates meta data required for
* reading tile using a TileIndex.
*
* \param filePath, a path to a specific file GDAL can read
* \param config, Configuration used for initialization
*/
GdalRawTileDataReader(const std::string& filePath, const Configuration& config);
virtual ~GdalRawTileDataReader() override;
// Public virtual function overloading
virtual void reset() override;
virtual int maxChunkLevel() override;
virtual float noDataValueAsFloat() const override;
virtual int rasterXSize() const override;
virtual int rasterYSize() const override;
virtual float depthOffset() const override;
virtual float depthScale() const override;
protected:
/**
* Returns the geo transform from raster space to projection coordinates as defined
* by GDAL.
* If the transform is not available, the function returns a transform to map
* the pixel coordinates to cover the whole geodetic lat long space.
*/
virtual std::array<double, 6> getGeoTransform() const override;
virtual IODescription getIODescription(const TileIndex& tileIndex) const override;
private:
// Private virtual function overloading
virtual void initialize() override;
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const override;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const override;
// GDAL Helper methods
GDALDataset* openGdalDataset(const std::string& filePath);
int calculateTileLevelDifference(int minimumPixelSize);
bool gdalHasOverviews() const;
int gdalOverview(const PixelRegion::PixelRange& baseRegionSize) const;
int gdalOverview(const TileIndex& tileIndex) const;
int gdalVirtualOverview(const TileIndex& tileIndex) const;
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
TileDataLayout getTileDataLayout(GLuint prefferedGLType);
// Member variables
struct InitData {
std::string initDirectory;
std::string datasetFilePath;
int tilePixelSize;
GLuint dataType;
} _initData;
GDALDataset* _dataset;
GDALDataType _gdalType; // The type to reinterpret to when reading
};
} // namespace globebrowsing
} // namespace openspace
#endif // GLOBEBROWSING_USE_GDAL
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_RAW_TILE_DATA_READER___H__

View File

@@ -0,0 +1,186 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifdef GLOBEBROWSING_USE_GDAL
#include <modules/globebrowsing/tile/rawtiledatareader/gdalwrapper.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/configurationmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
#include <ghoul/ghoul.h>
#include <ghoul/logging/consolelog.h>
#include <gdal_priv.h>
namespace {
const char* _loggerCat = "GdalWrapper";
}
namespace openspace {
namespace globebrowsing {
void gdalErrorHandler(CPLErr eErrClass, int errNo, const char *msg) {
if (GdalWrapper::ref().logGdalErrors()) {
switch (eErrClass) {
case CE_None: break;
case CE_Debug: LDEBUG ("GDAL " << msg); break;
case CE_Warning: LWARNING("GDAL " << msg); break;
case CE_Failure: LERROR ("GDAL " << msg); break;
case CE_Fatal: LFATAL ("GDAL " << msg); break;
}
}
}
GdalWrapper* GdalWrapper::_singleton = nullptr;
std::mutex GdalWrapper::_mutexLock;
void GdalWrapper::create(size_t maximumCacheSize,
size_t maximumMaximumCacheSize) {
std::lock_guard<std::mutex> guard(_mutexLock);
_singleton = new GdalWrapper(maximumCacheSize, maximumMaximumCacheSize);
}
void GdalWrapper::destroy() {
std::lock_guard<std::mutex> guard(_mutexLock);
ghoul_assert(_singleton, "Cannot delete null");
delete _singleton;
}
GdalWrapper& GdalWrapper::ref() {
ghoul_assert(_singleton, "GdalWrapper not created");
return *_singleton;
}
size_t GDALCacheUsed() {
return GDALGetCacheUsed64();
}
size_t GDALMaximumCacheSize() {
return GDALGetCacheMax64();
}
bool GdalWrapper::logGdalErrors() const {
return _logGdalErrors;
}
GdalWrapper::GdalWrapper(size_t maximumCacheSize,
size_t maximumMaximumCacheSize)
: PropertyOwner("GdalWrapper")
, _logGdalErrors("logGdalErrors", "Log GDAL errors", true)
, _gdalMaximumCacheSize (
"gdalMaximumCacheSize", "GDAL maximum cache size",
maximumCacheSize / (1024 * 1024), // Default
0, // Minimum: No caching
maximumMaximumCacheSize / (1024 * 1024), // Maximum
1 // Step: One MB
) {
addProperty(_logGdalErrors);
addProperty(_gdalMaximumCacheSize);
GDALAllRegister();
CPLSetConfigOption(
"GDAL_DATA",
absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()
);
setGdalProxyConfiguration();
CPLSetErrorHandler(gdalErrorHandler);
_gdalMaximumCacheSize.onChange([&] {
// MB to Bytes
GDALSetCacheMax64(
static_cast<size_t>(_gdalMaximumCacheSize) *
1024ULL * 1024ULL
);
});
}
void GdalWrapper::setGdalProxyConfiguration() {
ghoul::Dictionary proxySettings;
bool proxyEnabled = OsEng.configurationManager().getValue(
ConfigurationManager::KeyHttpProxy, proxySettings
);
if (proxyEnabled) {
std::string proxyAddress, proxyPort, proxyUser, proxyPassword,
proxyAuth;
bool success = proxySettings.getValue(
ConfigurationManager::PartHttpProxyAddress,
proxyAddress
);
success &= proxySettings.getValue(
ConfigurationManager::PartHttpProxyPort,
proxyPort
);
proxySettings.getValue(
ConfigurationManager::PartHttpProxyAuthentication,
proxyAuth
);
std::string proxyAuthString = "BASIC";
if (proxyAuth == "basic" || proxyAuth == "") {
proxyAuthString = "BASIC";
} else if (proxyAuth == "ntlm") {
proxyAuthString = "NTLM";
} else if (proxyAuth == "digest") {
proxyAuthString = "DIGEST";
} else if (proxyAuth == "any") {
proxyAuthString = "ANY";
} else {
success = false;
}
bool userAndPassword = proxySettings.getValue(
ConfigurationManager::PartHttpProxyUser,
proxyUser
);
userAndPassword &= proxySettings.getValue(
ConfigurationManager::PartHttpProxyPassword,
proxyPassword
);
if (success) {
std::string proxy = proxyAddress + ":" + proxyPort;
CPLSetConfigOption("GDAL_HTTP_PROXY", proxy.c_str());
LDEBUG("Using proxy server " << proxy);
if (userAndPassword) {
std::string proxyUserPwd = proxyUser + ":" + proxyPassword;
CPLSetConfigOption("GDAL_HTTP_PROXYUSERPWD", proxyUserPwd.c_str());
CPLSetConfigOption("GDAL_HTTP_PROXYAUTH", proxyAuthString.c_str());
LDEBUG("Using authentication method: " << proxyAuthString);
}
} else {
LERROR("Invalid proxy settings for GDAL");
}
} else {
LDEBUG("Setting up GDAL without proxy server");
}
}
} // namespace globebrowsing
} // namespace openspace
#endif // GLOBEBROWSING_USE_GDAL

View File

@@ -0,0 +1,96 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_WRAPPER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_WRAPPER___H__
#ifdef GLOBEBROWSING_USE_GDAL
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/scalarproperty.h>
#include <openspace/properties/triggerproperty.h>
#include <gdal.h>
#include <mutex>
#include <memory>
namespace openspace {
namespace globebrowsing {
/**
* Function for passing GDAL error messages to the GHOUL logging system.
*/
static void gdalErrorHandler(CPLErr eErrClass, int errNo, const char* msg);
/**
* Singleton class interfacing with global GDAL functions.
*/
class GdalWrapper : public properties::PropertyOwner {
public:
/**
* Create the singleton. Must be called before the class can be used.
* \param maximumCacheSize is the current maximum cache size GDAL can use
* for caching blocks in memory given in bytes.
* \param maximumMaximumCacheSize is the maximum cache size GDAL can use
* for caching blocks in memory given in bytes.
*/
static void create(size_t maximumCacheSize, size_t maximumMaximumCacheSize);
static void destroy();
static GdalWrapper& ref();
/**
* Get the current size of the GDAL in memory cache.
* \returns the number of bytes currently in the GDAL memory cache.
*/
static size_t GDALCacheUsed();
/**
* Get the maximum GDAL in memory cache size.
* \returns the maximum number of bytes allowed for the GDAL cache.
*/
static size_t GDALMaximumCacheSize();
bool logGdalErrors() const;
private:
GdalWrapper(size_t maximumCacheSize, size_t maximumMaximumCacheSize);
~GdalWrapper() = default;
void setGdalProxyConfiguration();
properties::IntProperty _gdalMaximumCacheSize;
properties::BoolProperty _logGdalErrors;
static GdalWrapper* _singleton;
static std::mutex _mutexLock;
};
} // namespace globebrowsing
} // namespace openspace
#endif // GLOBEBROWSING_USE_GDAL
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___GDAL_WRAPPER___H__

View File

@@ -0,0 +1,70 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
#include <modules/globebrowsing/tile/pixelregion.h>
namespace {
const char* _loggerCat = "IODescription";
}
namespace openspace {
namespace globebrowsing {
IODescription IODescription::cut(PixelRegion::Side side, int pos) {
PixelRegion readPreCut = read.region;
PixelRegion writePreCut = write.region;
glm::dvec2 ratio;
ratio.x = write.region.numPixels.x / static_cast<double>(read.region.numPixels.x);
ratio.y = write.region.numPixels.y / static_cast<double>(read.region.numPixels.y);
IODescription whatCameOff = *this;
whatCameOff.read.region = read.region.globalCut(side, pos);
PixelRegion::PixelRange cutSize = whatCameOff.read.region.numPixels;
PixelRegion::PixelRange localWriteCutSize = ratio * glm::dvec2(cutSize);
if (cutSize.x == 0 || cutSize.y == 0) {
ghoul_assert(
read.region.equals(readPreCut),
"Read region should not have been modified"
);
ghoul_assert(
write.region.equals(writePreCut),
"Write region should not have been modified"
);
}
int localWriteCutPos =
(side == PixelRegion::Side::LEFT || side == PixelRegion::Side::RIGHT)
? localWriteCutSize.x : localWriteCutSize.y;
whatCameOff.write.region = write.region.localCut(side, localWriteCutPos);
return whatCameOff;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,63 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <string>
namespace openspace {
namespace globebrowsing {
struct IODescription {
struct ReadData {
int overview;
PixelRegion region;
PixelRegion fullRegion;
} read;
struct WriteData {
PixelRegion region;
size_t bytesPerLine;
size_t totalNumBytes;
} write;
IODescription cut(PixelRegion::Side side, int pos);
};
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___IO_DESCRIPTION___H__

View File

@@ -0,0 +1,399 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <openspace/engine/configurationmanager.h>
#include <float.h>
#include <sstream>
#include <algorithm>
#include <memory>
#include <set>
#include <queue>
#include <iostream>
#include <unordered_map>
#include <limits>
#define _USE_MATH_DEFINES
#include <math.h>
namespace {
const char* _loggerCat = "RawTileDataReader";
}
namespace openspace {
namespace globebrowsing {
const glm::ivec2 RawTileDataReader::tilePixelStartOffset = glm::ivec2(-2);
const glm::ivec2 RawTileDataReader::tilePixelSizeDifference = glm::ivec2(4);
const PixelRegion RawTileDataReader::padding = PixelRegion(
tilePixelStartOffset,
tilePixelSizeDifference
);
RawTileDataReader::RawTileDataReader(const Configuration& config)
: _config(config)
, _hasBeenInitialized(false)
{}
void RawTileDataReader::ensureInitialized() {
if (!_hasBeenInitialized) {
initialize();
_hasBeenInitialized = true;
}
}
std::shared_ptr<RawTile> RawTileDataReader::defaultTileData() {
PixelRegion pixelRegion = {
PixelRegion::PixelCoordinate(0, 0),
PixelRegion::PixelRange(16, 16)
};
int bytesPerPixel = 1; // GL_R -> 1 bpp
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->tileIndex = { 0, 0, 0 };
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
rawTile->nBytesImageData =
rawTile->dimensions.x * rawTile->dimensions.y * bytesPerPixel;
rawTile->imageData = new char[rawTile->nBytesImageData];
rawTile->glType = GL_UNSIGNED_BYTE;
rawTile->textureFormat = { ghoul::opengl::Texture::Format::Red, GL_R };
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
rawTile->imageData[i] = 0;
}
rawTile->error = RawTile::ReadError::None;
return rawTile;
}
std::shared_ptr<RawTile> RawTileDataReader::readTileData(TileIndex tileIndex) {
ensureInitialized();
IODescription io = getIODescription(tileIndex);
RawTile::ReadError worstError = RawTile::ReadError::None;
// Build the RawTile from the data we querred
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->imageData = readImageData(io, worstError);
rawTile->error = worstError;
rawTile->tileIndex = tileIndex;
rawTile->dimensions = glm::uvec3(io.write.region.numPixels, 1);
rawTile->nBytesImageData = io.write.totalNumBytes;
rawTile->glType = _dataLayout.glType;
rawTile->textureFormat = _dataLayout.textureFormat;
if (_config.doPreProcessing) {
rawTile->tileMetaData = getTileMetaData(rawTile, io.write.region);
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
}
return rawTile;
}
TileDepthTransform RawTileDataReader::getDepthTransform() const {
return _depthTransform;
}
std::array<double, 6> RawTileDataReader::getGeoTransform() const {
std::array<double, 6> padfTransform;
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2.0, M_PI));
padfTransform[1] = Angle<double>::fromRadians(
globalCoverage.size().lon).asDegrees() / rasterXSize();
padfTransform[5] = -Angle<double>::fromRadians(
globalCoverage.size().lat).asDegrees() / rasterYSize();
padfTransform[0] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
padfTransform[3] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
padfTransform[2] = 0;
padfTransform[4] = 0;
return padfTransform;
}
PixelRegion::PixelCoordinate RawTileDataReader::geodeticToPixel(
const Geodetic2& geo) const {
std::array<double, 6> padfTransform = getGeoTransform();
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
// convert from pixel and line to geodetic coordinates
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
// <=>
double* a = &(padfTransform[0]);
double* b = &(padfTransform[3]);
// Xp = a[0] + P*a[1] + L*a[2];
// Yp = b[0] + P*b[1] + L*b[2];
// <=>
double divisor = (a[2] * b[1] - a[1] * b[2]);
ghoul_assert(divisor != 0.0, "Division by zero!");
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
double Xp = a[0] + P*a[1] + L*a[2];
double Yp = b[0] + P*b[1] + L*b[2];
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
}
Geodetic2 RawTileDataReader::pixelToGeodetic(
const PixelRegion::PixelCoordinate& p) const {
std::array<double, 6> padfTransform = getGeoTransform();
Geodetic2 geodetic;
// Should be using radians and not degrees?
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
return geodetic;
}
PixelRegion RawTileDataReader::highestResPixelRegion(const GeodeticPatch& geodeticPatch) const {
Geodetic2 nwCorner = geodeticPatch.getCorner(Quad::NORTH_WEST);
Geodetic2 swCorner = geodeticPatch.getCorner(Quad::SOUTH_EAST);
PixelRegion::PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
PixelRegion::PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
PixelRegion region(pixelStart, pixelEnd - pixelStart);
return region;
}
RawTile::ReadError RawTileDataReader::repeatedRasterRead(
int rasterBand, const IODescription& fullIO, char* dataDestination,
int depth) const
{
RawTile::ReadError worstError = RawTile::ReadError::None;
// NOTE:
// Ascii graphics illustrates the implementation details of this method, for one
// specific case. Even though the illustrated case is specific, readers can
// hopefully find it useful to get the general idea.
// Make a copy of the full IO desription as we will have to modify it
IODescription io = fullIO;
// Example:
// We have an io description that defines a WRITE and a READ region.
// In this case the READ region extends outside of the defined io.read.fullRegion,
// meaning we will have to perform wrapping
// io.write.region io.read.region
// | |
// V V
// +-------+ +-------+
// | | | |--------+
// | | | | |
// | | | | |
// +-------+ +-------+ |
// | | <-- io.read.fullRegion
// | |
// +--------------+
if (!io.read.region.isInside(io.read.fullRegion)) {
// Loop through each side: left, top, right, bottom
for (int i = 0; i < 4; ++i) {
// Example:
// We are currently considering the left side of the pixel region
PixelRegion::Side side = (PixelRegion::Side) i;
IODescription cutoff = io.cut(side, io.read.fullRegion.edge(side));
// Example:
// We cut off the left part that was outside the io.read.fullRegion, and we
// now have an additional io description for the cut off region.
// Note that the cut-method used above takes care of the corresponding
// WRITE region for us.
// cutoff.write.region cutoff.read.region
// | io.write.region | io.read.region
// | | | |
// V V V V
// +-+-----+ +-+-----+
// | | | | | |--------+
// | | | | | | |
// | | | | | | |
// +-+-----+ +-+-----+ |
// | | <-- io.read.fullRegion
// | |
// +--------------+
if (cutoff.read.region.area() > 0) {
// Wrap by repeating
PixelRegion::Side oppositeSide = (PixelRegion::Side) ((i + 2) % 4);
cutoff.read.region.align(
oppositeSide, io.read.fullRegion.edge(oppositeSide));
// Example:
// The cut off region is wrapped to the opposite side of the region,
// i.e. "repeated". Note that we don't want WRITE region to change,
// we're only wrapping the READ region.
// cutoff.write.region io.read.region cutoff.read.region
// | io.write.region | |
// | | V V
// V V +-----+ +-+
// +-+-----+ | |------| |
// | | | | | | |
// | | | | | | |
// | | | +-----+ +-+
// +-+-----+ | | <-- io.read.fullRegion
// | |
// +--------------+
// Example:
// The cutoff region has been repeated along one of its sides, but
// as we can see in this example, it still has a top part outside the
// defined gdal region. This is handled through recursion.
RawTile::ReadError err = repeatedRasterRead(
rasterBand, cutoff, dataDestination, depth + 1);
worstError = std::max(worstError, err);
}
}
}
RawTile::ReadError err = rasterRead(rasterBand, io, dataDestination);
// The return error from a repeated rasterRead is ONLY based on the main region,
// which in the usual case will cover the main area of the patch anyway
return err;
}
std::shared_ptr<TileMetaData> RawTileDataReader::getTileMetaData(
std::shared_ptr<RawTile> rawTile, const PixelRegion& region)
{
ensureInitialized();
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
TileMetaData* preprocessData = new TileMetaData();
preprocessData->maxValues.resize(_dataLayout.numRasters);
preprocessData->minValues.resize(_dataLayout.numRasters);
preprocessData->hasMissingData.resize(_dataLayout.numRasters);
std::vector<float> noDataValues;
noDataValues.resize(_dataLayout.numRasters);
for (size_t raster = 0; raster < _dataLayout.numRasters; ++raster) {
preprocessData->maxValues[raster] = -FLT_MAX;
preprocessData->minValues[raster] = FLT_MAX;
preprocessData->hasMissingData[raster] = false;
noDataValues[raster] = noDataValueAsFloat();
}
for (size_t y = 0; y < region.numPixels.y; ++y) {
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 raster = 0; raster < _dataLayout.numRasters; ++raster) {
float noDataValue = noDataValueAsFloat();
float val = tiledatatype::interpretFloat(
_dataLayout.glType,
&(rawTile->imageData[yi + i])
);
if (val != noDataValue) {
preprocessData->maxValues[raster] = std::max(
val,
preprocessData->maxValues[raster]
);
preprocessData->minValues[raster] = std::min(
val,
preprocessData->minValues[raster]
);
}
else {
preprocessData->hasMissingData[raster] = true;
}
i += _dataLayout.bytesPerDatum;
}
}
}
return std::shared_ptr<TileMetaData>(preprocessData);
}
float RawTileDataReader::depthOffset() const {
return 0.0f;
}
float RawTileDataReader::depthScale() const {
return 1.0f;
}
TileDepthTransform RawTileDataReader::calculateTileDepthTransform() {
bool isFloat =
(_dataLayout.glType == GL_FLOAT || _dataLayout.glType == GL_DOUBLE);
double maximumValue =
isFloat ? 1.0 : tiledatatype::getMaximumValue(_dataLayout.glType);
TileDepthTransform transform;
transform.depthOffset = depthOffset();
transform.depthScale = depthScale() * maximumValue;
return transform;
}
RawTile::ReadError RawTileDataReader::postProcessErrorCheck(
std::shared_ptr<const RawTile> rawTile, const IODescription& io)
{
ensureInitialized();
double missingDataValue = noDataValueAsFloat();
bool hasMissingData = false;
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
hasMissingData |= rawTile->tileMetaData->maxValues[c] == missingDataValue;
}
bool onHighLevel = rawTile->tileIndex.level > 6;
if (hasMissingData && onHighLevel) {
return RawTile::ReadError::Fatal;
}
return RawTile::ReadError::None;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,180 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/rawtiledatareader/iodescription.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <string>
namespace openspace {
namespace globebrowsing {
class GeodeticPatch;
class RawTileDataReader {
public:
struct Configuration {
bool doPreProcessing;
int tilePixelSize;
GLuint dataType = 0; // default = no datatype reinterpretation
};
RawTileDataReader(const Configuration& config);
virtual ~RawTileDataReader() = default;
/**
* Reads data from the current dataset and initializes a <code>RawTile</code>
* which gets returned.
*/
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex);
TileDepthTransform getDepthTransform() const;
/**
* \returns the maximum chunk level available in the dataset. Should be a value
* between 2 and 31.
*/
virtual int maxChunkLevel() = 0;
/**
* Reset the dataset to its initial state. This is the place to clear any cache used.
*/
virtual void reset() = 0;
virtual float noDataValueAsFloat() const = 0;
virtual int rasterXSize() const = 0;
virtual int rasterYSize() const = 0;
virtual float depthOffset() const;
virtual float depthScale() const;
/**
* Returns a single channeled empty <code>RawTile</code> of size 16 * 16 pixels.
*/
std::shared_ptr<RawTile> defaultTileData();
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
/// Padding around all tiles to read to make sure edge blending works.
const static PixelRegion padding; // same as the two above
protected:
/**
* This function should set the variables <code>_cached</code>,
* <code>_dataLayout</code> and <code>_depthTransform</code>.
*/
virtual void initialize() = 0;
/**
* Call this in the constructor of the class extending <code>RawTileDataReader</code>
*/
void ensureInitialized();
/**
* The function returns a transform to map
* the pixel coordinates to cover the whole geodetic lat long space.
*/
virtual std::array<double, 6> getGeoTransform() const;
/**
* Read image data as described by the given <code>IODescription</code>.
* \param <code>io</code> describes how to read the data.
* \param <code>worstError</code> should be set to the error code returned when
* reading the data.
*/
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const = 0;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const = 0;
virtual IODescription getIODescription(const TileIndex& tileIndex) const = 0;
/**
* Get the pixel corresponding to a specific position on the globe defined by the
* <code>Geodetic2</code> coordinate <code>geo</code>. If the dataset has overviews
* the function returns the pixel at the lowest overview (highest resolution).
* \param <code>geo</code> is the position on the globe to convert to pixel space.
* \returns a pixel coordinate in the dataset.
*/
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
/**
* Get the geodetic coordinate corresponding to the given pixel in the dataset. If
* The dataset has overviews it is the lowest overview that is used. That is the
* one with highest resolution.
*/
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
/**
* Get a pixel region corresponding to the given <code>GeodeticPatch</code>. If the
* dataset has overviews the function returns the pixel region at the lowest overview
* (highest resolution).
* \param <code>geodeticPatch</code> is a patch covering an area in geodetic
* coordinates
* \returns a <code>PixelRegion</code> covering the given geodetic patch at highest
* resolution.
*/
PixelRegion highestResPixelRegion(const GeodeticPatch& geodeticPatch) const;
/**
* A recursive function that is able to perform wrapping in case the read region of
* the given <code>IODescription</code> is outside of the given write region.
*/
RawTile::ReadError repeatedRasterRead(
int rasterBand, const IODescription& io, char* dst, int depth = 0) const;
std::shared_ptr<TileMetaData> getTileMetaData(
std::shared_ptr<RawTile> result, const PixelRegion& region);
TileDepthTransform calculateTileDepthTransform();
RawTile::ReadError postProcessErrorCheck(
std::shared_ptr<const RawTile> ioResult, const IODescription& io);
struct Cached {
int _maxLevel = -1;
double _tileLevelDifference;
} _cached;
const Configuration _config;
TileDataLayout _dataLayout;
TileDepthTransform _depthTransform;
private:
bool _hasBeenInitialized;
};
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___RAW_TILE_DATA_READER___H__

View File

@@ -0,0 +1,184 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
#include <ghoul/filesystem/file.h>
#include <ghoul/misc/assert.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/io/texture/texturereader.h>
namespace {
const std::string _loggerCat = "SimpleRawTileDataReader";
}
namespace openspace {
namespace globebrowsing {
SimpleRawTileDataReader::SimpleRawTileDataReader(
const std::string& filePath, const Configuration& config)
: RawTileDataReader(config)
{
_datasetFilePath = filePath;
ensureInitialized();
}
void SimpleRawTileDataReader::reset() {
initialize();
}
int SimpleRawTileDataReader::maxChunkLevel() {
return _cached._maxLevel;
}
float SimpleRawTileDataReader::noDataValueAsFloat() const {
return 0.0f;
}
int SimpleRawTileDataReader::rasterXSize() const {
return _dataTexture->dimensions().x;
}
int SimpleRawTileDataReader::rasterYSize() const {
return _dataTexture->dimensions().y;
}
float SimpleRawTileDataReader::depthOffset() const {
return 0.0f;
}
float SimpleRawTileDataReader::depthScale() const {
return 1.0f;
}
IODescription SimpleRawTileDataReader::getIODescription(const TileIndex& tileIndex) const {
IODescription io;
io.read.overview = 0;
io.read.region = highestResPixelRegion(tileIndex);
io.read.fullRegion = PixelRegion({0, 0}, {rasterXSize(), rasterYSize()});
io.write.region = PixelRegion({0, 0}, io.read.region.numPixels);
io.write.bytesPerLine = _dataTexture->bytesPerPixel() * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
return io;
}
void SimpleRawTileDataReader::initialize() {
_dataTexture = ghoul::io::TextureReader::ref().loadTexture(_datasetFilePath);
if (_dataTexture == nullptr) {
throw ghoul::RuntimeError(
"Unable to read dataset: " + _datasetFilePath +
".\nCompiling OpenSpace with GDAL will allow for better support for different"
"formats."
);
}
float exponentX = log2(_dataTexture->dimensions().x);
float exponentY = log2(_dataTexture->dimensions().y);
if ( (exponentX - static_cast<int>(exponentX)) > 0.0001 ||
(exponentY - static_cast<int>(exponentY)) > 0.0001 ) {
throw ghoul::RuntimeError(
"Unable to read dataset: " + _datasetFilePath +
".\nCurrently only supporting power of 2 textures."
);
}
_cached._maxLevel = 2;
_cached._tileLevelDifference = 0;
_dataLayout.glType = _dataTexture->dataType();
_dataLayout.bytesPerDatum = tiledatatype::numberOfBytes(_dataLayout.glType);
_dataLayout.numRasters = tiledatatype::numberOfRasters(_dataTexture->format());
_dataLayout.numRastersAvailable = _dataLayout.numRasters;
_dataLayout.bytesPerPixel = _dataLayout.bytesPerDatum * _dataLayout.numRasters;
_dataLayout.textureFormat = {_dataTexture->format(), _dataTexture->internalFormat()};
_depthTransform = {depthScale(), depthOffset()};
}
char* SimpleRawTileDataReader::readImageData(
IODescription& io, RawTile::ReadError& worstError) const {
// allocate memory for the image
char* imageData = new char[io.write.totalNumBytes];
// In case there are extra channels not existing in the dataset
// we set the bytes to 255 (for example an extra alpha channel that)
// needs to be 1.
if (_dataLayout.numRasters > _dataLayout.numRastersAvailable) {
memset(imageData, 255, io.write.totalNumBytes);
}
// Modify to match OpenGL texture layout:
IODescription modifiedIO = io;
modifiedIO.read.region.start.y = modifiedIO.read.fullRegion.numPixels.y - modifiedIO.read.region.numPixels.y - modifiedIO.read.region.start.y;
RawTile::ReadError err = repeatedRasterRead(0, modifiedIO, imageData);
// None = 0, Debug = 1, Warning = 2, Failure = 3, Fatal = 4
worstError = std::max(worstError, err);
return imageData;
}
RawTile::ReadError SimpleRawTileDataReader::rasterRead(
int rasterBand, const IODescription& io, char* dataDestination) const
{
ghoul_assert(io.read.fullRegion.numPixels.x == _dataTexture->dimensions().x,
"IODescription does not match data texture.");
ghoul_assert(io.read.fullRegion.numPixels.y == _dataTexture->dimensions().y,
"IODescription does not match data texture.");
ghoul_assert(io.read.region.numPixels.x == io.write.region.numPixels.x,
"IODescription does not match data texture.");
ghoul_assert(io.read.region.numPixels.y == io.write.region.numPixels.y,
"IODescription does not match data texture.");
char* pixelWriteRow = dataDestination;
try {
// For each row
for (int y = 0; y < io.read.region.numPixels.y; y++) {
int bytesPerLineDataTexture =
_dataTexture->bytesPerPixel() * _dataTexture->dimensions().x;
const char* textureRow = (static_cast<const char*>(_dataTexture->pixelData())
+ io.read.region.start.x * _dataTexture->bytesPerPixel())
+ io.read.region.start.y * bytesPerLineDataTexture
+ y * bytesPerLineDataTexture;
memcpy(pixelWriteRow, textureRow, io.write.bytesPerLine);
pixelWriteRow += io.write.bytesPerLine;
}
} catch (const std::exception&) {
return RawTile::ReadError::Failure;
}
return RawTile::ReadError::None;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -22,71 +22,61 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <limits>
#include <ogr_featurestyle.h>
#include <ogr_spatialref.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
#include <ghoul/misc/assert.h>
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <float.h>
#include <sstream>
#include <algorithm>
#include <gdal_priv.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/configurationmanager.h>
#include <memory>
#include <set>
#include <queue>
#include <iostream>
#include <unordered_map>
#include <ghoul/filesystem/file.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/misc/threadpool.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatatype.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
namespace {
const std::string _loggerCat = "TileDataset";
}
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <string>
namespace openspace {
namespace globebrowsing {
TileDataLayout::TileDataLayout() {}
class GeodeticPatch;
TileDataLayout::TileDataLayout(GDALDataset* dataSet, GLuint preferredGlType) {
// Assume all raster bands have the same data type
gdalType =preferredGlType != 0 ?
tiledatatype::getGdalDataType(preferredGlType) :
dataSet->GetRasterBand(1)->GetRasterDataType();
class SimpleRawTileDataReader : public RawTileDataReader {
public:
SimpleRawTileDataReader(const std::string& filePath, const Configuration& config);
// Public virtual function overloading
virtual void reset() override;
virtual int maxChunkLevel() override;
virtual float noDataValueAsFloat() const override;
virtual int rasterXSize() const override;
virtual int rasterYSize() const override;
virtual float depthOffset() const override;
virtual float depthScale() const override;
protected:
virtual IODescription getIODescription(const TileIndex& tileIndex) const override;
private:
// Private virtual function overloading
virtual void initialize() override;
virtual char* readImageData(
IODescription& io, RawTile::ReadError& worstError) const override;
virtual RawTile::ReadError rasterRead(
int rasterBand, const IODescription& io, char* dst) const override;
// Member variables
std::string _datasetFilePath;
std::unique_ptr<ghoul::opengl::Texture> _dataTexture;
};
glType = tiledatatype::getOpenGLDataType(gdalType);
numRasters = dataSet->GetRasterCount();
bytesPerDatum = tiledatatype::numberOfBytes(gdalType);
bytesPerPixel = bytesPerDatum * numRasters;
textureFormat = tiledatatype::getTextureFormat(numRasters, gdalType);
}
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___SIMPLE_RAW_TILE_DATA_READER___H__

View File

@@ -22,10 +22,12 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tiledatatype.h>
#include <modules/globebrowsing/tile/rawtiledatareader/tiledatatype.h>
#ifdef GLOBEBROWSING_USE_GDAL
#include <ogr_featurestyle.h>
#include <ogr_spatialref.h>
#endif // GLOBEBROWSING_USE_GDAL
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
@@ -39,13 +41,15 @@
#include <algorithm>
namespace {
const std::string _loggerCat = "RawTile";
const char* _loggerCat = "TileDataType";
}
namespace openspace {
namespace globebrowsing {
namespace tiledatatype {
#ifdef GLOBEBROWSING_USE_GDAL
float interpretFloat(GDALDataType gdalType, const char* src) {
switch (gdalType) {
case GDT_Byte:
@@ -233,6 +237,132 @@ TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType) {
return format;
}
TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType) {
TextureFormat format;
switch (rasterCount) {
case 1: // Red
format.ghoulFormat = ghoul::opengl::Texture::Format::Red;
switch (gdalType) {
case GDT_Byte:
format.glFormat = GL_R8;
break;
case GDT_UInt16:
format.glFormat = GL_R16UI;
break;
case GDT_Int16:
format.glFormat = GL_R16_SNORM;
break;
case GDT_UInt32:
format.glFormat = GL_R32UI;
break;
case GDT_Int32:
format.glFormat = GL_R32I;
break;
case GDT_Float32:
format.glFormat = GL_R32F;
break;
// No representation of 64 bit float?
//case GDT_Float64:
// format.glFormat = GL_RED;
// break;
default:
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
}
break;
case 2:
format.ghoulFormat = ghoul::opengl::Texture::Format::RG;
switch (gdalType) {
case GDT_Byte:
format.glFormat = GL_RG8;
break;
case GDT_UInt16:
format.glFormat = GL_RG16UI;
break;
case GDT_Int16:
format.glFormat = GL_RG16_SNORM;
break;
case GDT_UInt32:
format.glFormat = GL_RG32UI;
break;
case GDT_Int32:
format.glFormat = GL_RG32I;
break;
case GDT_Float32:
format.glFormat = GL_RG32F;
break;
case GDT_Float64:
format.glFormat = GL_RED;
break;
default:
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
}
break;
case 3:
format.ghoulFormat = ghoul::opengl::Texture::Format::BGR;
switch (gdalType) {
case GDT_Byte:
format.glFormat = GL_RGB8;
break;
case GDT_UInt16:
format.glFormat = GL_RGB16UI;
break;
case GDT_Int16:
format.glFormat = GL_RGB16_SNORM;
break;
case GDT_UInt32:
format.glFormat = GL_RGB32UI;
break;
case GDT_Int32:
format.glFormat = GL_RGB32I;
break;
case GDT_Float32:
format.glFormat = GL_RGB32F;
break;
// No representation of 64 bit float?
//case GDT_Float64:
// format.glFormat = GL_RED;
// break;
default:
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
}
break;
case 4:
format.ghoulFormat = ghoul::opengl::Texture::Format::BGRA;
switch (gdalType) {
case GDT_Byte:
format.glFormat = GL_RGBA8;
break;
case GDT_UInt16:
format.glFormat = GL_RGBA16UI;
break;
case GDT_Int16:
format.glFormat = GL_RGB16_SNORM;
break;
case GDT_UInt32:
format.glFormat = GL_RGBA32UI;
break;
case GDT_Int32:
format.glFormat = GL_RGBA32I;
break;
case GDT_Float32:
format.glFormat = GL_RGBA32F;
break;
// No representation of 64 bit float?
//case GDT_Float64:
// format.glFormat = GL_RED;
// break;
default:
LERROR("GDAL data type unknown to OpenGL: " << gdalType);
}
break;
default:
LERROR("Unknown number of channels for OpenGL texture: " << rasterCount);
break;
}
return format;
}
GLuint getOpenGLDataType(GDALDataType gdalType) {
switch (gdalType) {
case GDT_Byte:
@@ -277,6 +407,70 @@ GDALDataType getGdalDataType(GLuint glType) {
}
}
#endif // GLOBEBROWSING_USE_GDAL
size_t numberOfRasters(ghoul::opengl::Texture::Format format) {
switch (format) {
case ghoul::opengl::Texture::Format::Red: return 1;
case ghoul::opengl::Texture::Format::RG: return 2;
case ghoul::opengl::Texture::Format::RGB: return 3;
case ghoul::opengl::Texture::Format::RGBA: return 4;
default: ghoul_assert(false, "Unknown format");
}
}
size_t numberOfBytes(GLuint glType) {
switch (glType) {
case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
case GL_UNSIGNED_SHORT: return sizeof(GLushort);
case GL_SHORT: return sizeof(GLshort);
case GL_UNSIGNED_INT: return sizeof(GLuint);
case GL_INT: return sizeof(GLint);
case GL_FLOAT: return sizeof(GLfloat);
case GL_DOUBLE: return sizeof(GLdouble);
default:
ghoul_assert(false, "Unknown data type");
}
}
size_t getMaximumValue(GLuint glType) {
switch (glType) {
case GL_UNSIGNED_BYTE:
return 1 << 8;
case GL_UNSIGNED_SHORT:
return 1 << 16;
case GL_SHORT:
return 1 << 15;
case GL_UNSIGNED_INT:
return size_t(1) << 32;
case GL_INT:
return 1 << 31;
default:
ghoul_assert(false, "Unknown data type");
}
}
float interpretFloat(GLuint glType, const char* src) {
switch (glType) {
case GL_UNSIGNED_BYTE:
return static_cast<float>(*reinterpret_cast<const GLubyte*>(src));
case GL_UNSIGNED_SHORT:
return static_cast<float>(*reinterpret_cast<const GLushort*>(src));
case GL_SHORT:
return static_cast<float>(*reinterpret_cast<const GLshort*>(src));
case GL_UNSIGNED_INT:
return static_cast<float>(*reinterpret_cast<const GLuint*>(src));
case GL_INT:
return static_cast<float>(*reinterpret_cast<const GLint*>(src));
case GL_FLOAT:
return static_cast<float>(*reinterpret_cast<const GLfloat*>(src));
case GL_DOUBLE:
return static_cast<float>(*reinterpret_cast<const GLdouble*>(src));
default:
ghoul_assert(false, "Unknown data type");
}
}
} // namespace tiledatatype
} // namespace globebrowsing
} // namespace openspace

View File

@@ -26,28 +26,32 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATA_TYPE___H__
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/textureformat.h>
#include <ghoul/opengl/ghoul_gl.h>
#ifdef GLOBEBROWSING_USE_GDAL
#include <gdal.h>
#endif // GLOBEBROWSING_USE_GDAL
namespace openspace {
namespace globebrowsing {
namespace tiledatatype {
#ifdef GLOBEBROWSING_USE_GDAL
GLuint getOpenGLDataType(GDALDataType gdalType);
GDALDataType getGdalDataType(GLuint glType);
TextureFormat getTextureFormat(int rasterCount, GDALDataType gdalType);
TextureFormat getTextureFormatOptimized(int rasterCount, GDALDataType gdalType);
size_t getMaximumValue(GDALDataType gdalType);
size_t numberOfBytes(GDALDataType gdalType);
float interpretFloat(GDALDataType gdalType, const char* src);
#endif // GLOBEBROWSING_USE_GDAL
size_t numberOfRasters(ghoul::opengl::Texture::Format format);
size_t numberOfBytes(GLuint glType);
size_t getMaximumValue(GLuint glType);
float interpretFloat(GLuint glType, const char* src);
} // namespace tiledatatype
} // namespace globebrowsing

View File

@@ -33,7 +33,7 @@ namespace globebrowsing {
struct TextureFormat {
ghoul::opengl::Texture::Format ghoulFormat;
GLuint glFormat;
GLint glFormat;
};

View File

@@ -22,9 +22,10 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/tile.h>
#include <gdal_priv.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
namespace {
const std::string _loggerCat = "Tile";
@@ -33,8 +34,22 @@ namespace {
namespace openspace {
namespace globebrowsing {
const Tile Tile::TileUnavailable = {nullptr, nullptr, Tile::Status::Unavailable };
const Tile Tile::TileUnavailable = Tile(nullptr, nullptr, Tile::Status::Unavailable);
Tile::Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
std::shared_ptr<TileMetaData> metaData, Status status)
: MemoryAwareCacheable(
(sizeof(Tile) +
(metaData ? sizeof(TileMetaData) : 0) +
(texture ? sizeof(ghoul::opengl::Texture) + texture->expectedPixelDataSize() * 2 : 0))
// Multiply by two since double memory is used when creating mip maps.
/ 1000 // Convert from bytes to kilobytes
)
, _texture(texture)
, _metaData(metaData)
, _status(status)
{}
Tile Tile::createPlainTile(const glm::uvec2& size, const glm::uvec4& color) {
using namespace ghoul::opengl;
@@ -58,13 +73,9 @@ Tile Tile::createPlainTile(const glm::uvec2& size, const glm::uvec4& color) {
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
// Create tile
Tile tile;
tile.status = Tile::Status::OK;
tile.metaData = nullptr;
tile.texture = texture;
Tile tile(texture, nullptr, Tile::Status::OK);
return tile;
}
}
glm::vec2 Tile::compensateSourceTextureSampling(glm::vec2 startOffset, glm::vec2 sizeDiff,
glm::uvec2 resolution, glm::vec2 tileUV)
@@ -81,8 +92,8 @@ glm::vec2 Tile::TileUvToTextureSamplePosition(const TileUvTransform& uvTransform
{
glm::vec2 uv = uvTransform.uvOffset + uvTransform.uvScale * tileUV;
uv = compensateSourceTextureSampling(
TileDataset::tilePixelStartOffset,
TileDataset::tilePixelSizeDifference,
RawTileDataReader::tilePixelStartOffset,
RawTileDataReader::tilePixelSizeDifference,
resolution,
uv);
return uv;

View File

@@ -26,9 +26,10 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE___H__
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/tile/tileuvtransform.h>
#include <modules/globebrowsing/cache/memoryawarecacheable.h>
#include <memory>
namespace ghoul { namespace opengl {
@@ -44,42 +45,49 @@ struct TileUvTransform;
/**
* Defines a status and may have a Texture and TileMetaData
*/
struct Tile {
std::shared_ptr<ghoul::opengl::Texture> texture;
std::shared_ptr<TileMetaData> metaData;
/**
class Tile : public cache::MemoryAwareCacheable {
public:
/**
* Describe if this Tile is good for usage (OK) or otherwise
* the reason why it is not.
*/
enum class Status {
/**
* E.g when texture data is not currently in memory.
* texture and tileMetaData are both null
*/
Unavailable,
enum class Status {
/**
* E.g when texture data is not currently in memory.
* texture and tileMetaData are both null
*/
Unavailable,
/**
* Can be set by <code>TileProvider</code>s if the requested
* <code>TileIndex</code> is undefined for that particular
* provider.
* texture and metaData are both null
*/
OutOfRange,
/**
* Can be set by <code>TileProvider</code>s if the requested
* <code>TileIndex</code> is undefined for that particular
* provider.
* texture and metaData are both null
*/
OutOfRange,
/**
* An IO Error happend
* texture and metaData are both null
*/
IOError,
/**
* An IO Error happend
* texture and metaData are both null
*/
IOError,
/**
* The Texture is uploaded to the GPU and good for usage.
* texture is defined. metaData may be defined.
*/
OK
};
Tile(std::shared_ptr<ghoul::opengl::Texture> texture,
std::shared_ptr<TileMetaData> metaData,
Status status);
~Tile() = default;
std::shared_ptr<TileMetaData> metaData() const { return _metaData; };
Status status() const { return _status; };
std::shared_ptr<ghoul::opengl::Texture> texture() const { return _texture; };
/**
* The Texture is uploaded to the GPU and good for usage.
* texture is defined. metaData may be defined.
*/
OK
} status;
/**
* Instantiates a new tile with a single color.
*
@@ -90,18 +98,20 @@ struct Tile {
* with the requested size and color
*/
static Tile createPlainTile(const glm::uvec2& size, const glm::uvec4& color);
static glm::vec2 compensateSourceTextureSampling(glm::vec2 startOffset,
glm::vec2 sizeDiff, glm::uvec2 resolution, glm::vec2 tileUV);
static glm::vec2 TileUvToTextureSamplePosition(const TileUvTransform& uvTransform,
glm::vec2 tileUV, glm::uvec2 resolution);
/**
* A tile with status unavailable that any user can return to
* indicate that a tile was unavailable.
*/
static const Tile TileUnavailable;
/**
* A tile with status unavailable that any user can return to
* indicate that a tile was unavailable.
*/
static const Tile TileUnavailable;
private:
std::shared_ptr<ghoul::opengl::Texture> _texture;
std::shared_ptr<TileMetaData> _metaData;
Status _status;
};
} // namespace globebrowsing

View File

@@ -30,22 +30,21 @@
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <gdal.h>
class GDALDataset;
namespace openspace {
namespace globebrowsing {
struct TileDataLayout {
TileDataLayout();
TileDataLayout(GDALDataset* dataSet, GLuint preferredGlType);
GDALDataType gdalType;
GLuint glType;
size_t bytesPerDatum;
size_t numRasters;
/// Number of rasters available in the GDAL dataset.
/// Does not necessarily have to be equal to numRasters.
/// In case an extra alpha channel needs to be added that
/// does not exist in the GDAL dataset for example
size_t numRastersAvailable;
size_t bytesPerPixel;
TextureFormat textureFormat;

View File

@@ -0,0 +1,150 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tiledatareader.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatatype.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/assert.h>
#include <algorithm>
#include <float.h>
#include <limits>
#include <sstream>
namespace {
const char* _loggerCat = "TileDataReader";
}
namespace openspace {
namespace globebrowsing {
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
return os << pr.start.x << ", " << pr.start.y <<
" with size " << pr.numPixels.x << ", " << pr.numPixels.y;
}
TileDataReader::TileDataReader(const Configuration& config)
: _config(config)
{}
std::shared_ptr<RawTile> TileDataReader::defaultTileData() {
ensureInitialized();
PixelRegion pixelRegion = {
PixelRegion::PixelCoordinate(0, 0),
PixelRegion::PixelRange(16, 16)
};
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->tileIndex = { 0, 0, 0 };
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
rawTile->nBytesImageData =
rawTile->dimensions.x * rawTile->dimensions.y * _dataLayout.bytesPerPixel;
rawTile->imageData = new char[rawTile->nBytesImageData];
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
rawTile->imageData[i] = 0;
}
rawTile->error = RawTile::ReadError::None;
if (_config.doPreProcessing) {
rawTile->tileMetaData = getTileMetaData(rawTile, pixelRegion);
}
return rawTile;
}
std::array<double, 6> TileDataset::getGeoTransform() const {
std::array<double, 6> padfTransform;
// Global coverage of the whole latlon space
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2, M_PI));
padfTransform[1] = Angle<double>::fromRadians(
globalCoverage.size().lon).asDegrees() / rasterXSize();
padfTransform[5] = -Angle<double>::fromRadians(
globalCoverage.size().lat).asDegrees() / rasterYSize();
padfTransform[0] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
padfTransform[3] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
padfTransform[2] = 0;
padfTransform[4] = 0;
return padfTransform;
}
PixelRegion::PixelCoordinate TileDataReader::geodeticToPixel(const Geodetic2& geo) const {
std::array<double, 6> padfTransform = getGeoTransform();
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
// convert from pixel and line to geodetic coordinates
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
// <=>
double* a = &(padfTransform[0]);
double* b = &(padfTransform[3]);
// Xp = a[0] + P*a[1] + L*a[2];
// Yp = b[0] + P*b[1] + L*b[2];
// <=>
double divisor = (a[2] * b[1] - a[1] * b[2]);
ghoul_assert(divisor != 0.0, "Division by zero!");
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
double Xp = a[0] + P*a[1] + L*a[2];
double Yp = b[0] + P*b[1] + L*b[2];
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
}
Geodetic2 TileDataReader::pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const {
std::array<double, 6> padfTransform = getGeoTransform();
Geodetic2 geodetic;
// Should be using radians and not degrees?
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
return geodetic;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,92 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATAREADER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATAREADER___H__
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <gdal.h>
#include <string>
namespace openspace {
namespace globebrowsing {
struct RawTile;
class GeodeticPatch;
/**
* Interface for reading <code>RawTile</code>s given a <code>TileIndex</code>
*/
class TileDataReader {
public:
struct Configuration {
bool doPreProcessing;
int minimumTilePixelSize;
GLuint dataType = 0; // default = no datatype reinterpretation
};
virtual TileDataReader(const Configuration& config);
std::shared_ptr<RawTile> defaultTileData();
virtual std::shared_ptr<RawTile> readTileData(TileIndex tileIndex) = 0;
virtual int maxChunkLevel() = 0;
virtual TileDepthTransform getDepthTransform() = 0;
virtual const TileDataLayout& getDataLayout() = 0;
virtual void reset() = 0;
virtual float noDataValueAsFloat() = 0;
virtual size_t rasterXSize() = 0;
virtual size_t rasterYSize() = 0;
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
const static PixelRegion padding; // same as the two above
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
const static PixelRegion padding; // same as the two above
static bool logReadErrors;
protected:
Configuration _config;
virtual std::array<double, 6> padfTransform getGeoTransform();
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
};
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATAREADER___H__

View File

@@ -1,823 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tiledataset.h>
#include <limits>
#include <ogr_featurestyle.h>
#include <ogr_spatialref.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/filesystem/filesystem.h> // abspath
#include <ghoul/misc/assert.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/geometry/angle.h>
#include <float.h>
#include <sstream>
#include <algorithm>
#include <gdal_priv.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/configurationmanager.h>
#include <memory>
#include <set>
#include <queue>
#include <iostream>
#include <unordered_map>
#include <ghoul/filesystem/file.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/misc/threadpool.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledatatype.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#include <modules/globebrowsing/geometry/geodeticpatch.h>
namespace {
const std::string _loggerCat = "TileDataset";
}
namespace openspace {
namespace globebrowsing {
std::ostream& operator<<(std::ostream& os, const PixelRegion& pr) {
return os << pr.start.x << ", " << pr.start.y << " with size " << pr.numPixels.x << ", " << pr.numPixels.y;
}
TileDataset::IODescription TileDataset::IODescription::cut(PixelRegion::Side side, int pos) {
PixelRegion readPreCut = read.region;
PixelRegion writePreCut = write.region;
glm::dvec2 ratio;
ratio.x = write.region.numPixels.x / (double) read.region.numPixels.x;
ratio.y = write.region.numPixels.y / (double) read.region.numPixels.y;
// double ratioRatio = ratio.x / ratio.y;
//ghoul_assert(glm::abs(ratioRatio - 1.0) < 0.01, "Different read/write aspect ratio!");
IODescription whatCameOff = *this;
whatCameOff.read.region = read.region.globalCut(side, pos);
PixelRegion::PixelRange cutSize = whatCameOff.read.region.numPixels;
PixelRegion::PixelRange localWriteCutSize = ratio * glm::dvec2(cutSize);
if (cutSize.x == 0 || cutSize.y == 0) {
ghoul_assert(
read.region.equals(readPreCut),
"Read region should not have been modified"
);
ghoul_assert(
write.region.equals(writePreCut),
"Write region should not have been modified"
);
}
int localWriteCutPos = (side == PixelRegion::Side::LEFT || side == PixelRegion::Side::RIGHT)
? localWriteCutSize.x : localWriteCutSize.y;
whatCameOff.write.region = write.region.localCut(side, localWriteCutPos);
return whatCameOff;
}
const glm::ivec2 TileDataset::tilePixelStartOffset = glm::ivec2(-2);
const glm::ivec2 TileDataset::tilePixelSizeDifference = glm::ivec2(4);
const PixelRegion TileDataset::padding = PixelRegion(
tilePixelStartOffset,
tilePixelSizeDifference
);
bool TileDataset::GdalHasBeenInitialized = false;
TileDataset::TileDataset(const std::string& gdalDatasetDesc, const Configuration& config)
: _config(config)
, hasBeenInitialized(false)
{
_initData = { "", gdalDatasetDesc, config.minimumTilePixelSize, config.dataType };
ensureInitialized();
_initData.initDirectory = CPLGetCurrentDir();
}
void TileDataset::reset() {
_cached._maxLevel = -1;
if (_dataset != nullptr) {
GDALClose((GDALDatasetH)_dataset);
}
initialize();
}
float TileDataset::noDataValueAsFloat() {
float noDataValue;
if (_dataset && _dataset->GetRasterBand(1)) {
noDataValue = _dataset->GetRasterBand(1)->GetNoDataValue();;
}
else {
noDataValue = std::numeric_limits<float>::min();
}
return noDataValue;
}
void TileDataset::ensureInitialized() {
if (!hasBeenInitialized) {
initialize();
hasBeenInitialized = true;
}
}
void TileDataset::initialize() {
gdalEnsureInitialized();
_dataset = gdalDataset(_initData.gdalDatasetDesc);
//Do any other initialization needed for the TileDataset
_dataLayout = TileDataLayout(_dataset, _initData.dataType);
_depthTransform = calculateTileDepthTransform();
_cached._tileLevelDifference = calculateTileLevelDifference(_initData.minimumPixelSize);
}
void TileDataset::gdalEnsureInitialized() {
if (!GdalHasBeenInitialized) {
GDALAllRegister();
CPLSetConfigOption(
"GDAL_DATA",
absPath("${MODULE_GLOBEBROWSING}/gdal_data").c_str()
);
setGdalProxyConfiguration();
GdalHasBeenInitialized = true;
}
}
void TileDataset::setGdalProxyConfiguration() {
ghoul::Dictionary proxySettings;
bool proxyEnabled = OsEng.configurationManager().getValue(
ConfigurationManager::KeyHttpProxy, proxySettings
);
if (proxyEnabled) {
std::string proxyAddress, proxyPort, proxyUser, proxyPassword, proxyAuth;
bool success = proxySettings.getValue(
ConfigurationManager::PartHttpProxyAddress,
proxyAddress
);
success &= proxySettings.getValue(
ConfigurationManager::PartHttpProxyPort,
proxyPort
);
proxySettings.getValue(
ConfigurationManager::PartHttpProxyAuthentication,
proxyAuth
);
std::string proxyAuthString = "BASIC";
if (proxyAuth == "basic" || proxyAuth == "") {
proxyAuthString = "BASIC";
} else if (proxyAuth == "ntlm") {
proxyAuthString = "NTLM";
} else if (proxyAuth == "digest") {
proxyAuthString = "DIGEST";
} else if (proxyAuth == "any") {
proxyAuthString = "ANY";
} else {
success = false;
}
bool userAndPassword = proxySettings.getValue(
ConfigurationManager::PartHttpProxyUser,
proxyUser
);
userAndPassword &= proxySettings.getValue(
ConfigurationManager::PartHttpProxyPassword,
proxyPassword
);
if (success) {
std::string proxy = proxyAddress + ":" + proxyPort;
CPLSetConfigOption("GDAL_HTTP_PROXY", proxy.c_str());
LDEBUG("Using proxy server " << proxy);
if (userAndPassword) {
std::string proxyUserPwd = proxyUser + ":" + proxyPassword;
CPLSetConfigOption("GDAL_HTTP_PROXYUSERPWD", proxyUserPwd.c_str());
CPLSetConfigOption("GDAL_HTTP_PROXYAUTH", proxyAuthString.c_str());
LDEBUG("Using authentication method: " << proxyAuthString);
}
} else {
LERROR("Invalid proxy settings for GDAL");
}
} else {
LDEBUG("Setting up GDAL without proxy server");
}
}
GDALDataset* TileDataset::gdalDataset(const std::string& gdalDatasetDesc) {
GDALDataset* dataset = (GDALDataset *)GDALOpen(gdalDatasetDesc.c_str(), GA_ReadOnly);
if (!dataset) {
using namespace ghoul::filesystem;
std::string correctedPath = FileSystem::ref().pathByAppendingComponent(
_initData.initDirectory, gdalDatasetDesc
);
dataset = (GDALDataset *)GDALOpen(correctedPath.c_str(), GA_ReadOnly);
if (!dataset) {
throw ghoul::RuntimeError("Failed to load dataset:\n" + gdalDatasetDesc);
}
}
// Commenting away the following for now since it is not supported for older
// versions of GDAL. Only used for debug info.
/*
const std::string originalDriverName = dataset->GetDriverName();
if (originalDriverName != "WMS") {
LDEBUG(" " << originalDriverName);
LDEBUG(" " << dataset->GetGCPProjection());
LDEBUG(" " << dataset->GetProjectionRef());
GDALDriver* driver = dataset->GetDriver();
char** metadata = driver->GetMetadata();
for (int i = 0; metadata[i] != nullptr; i++) {
LDEBUG(" " << metadata[i]);
}
const char* in_memory = "";
//GDALDataset* vrtDataset = driver->CreateCopy(in_memory, dataset, false, nullptr, nullptr, nullptr);
}
*/
return dataset;
}
TileDataset::~TileDataset() {
delete _dataset;
}
std::shared_ptr<RawTile> TileDataset::readTileData(TileIndex tileIndex) {
ensureInitialized();
IODescription io = getIODescription(tileIndex);
CPLErr worstError = CPLErr::CE_None;
// Build the RawTile from the data we querred
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->imageData = readImageData(io, worstError);
rawTile->error = worstError;
rawTile->tileIndex = tileIndex;
rawTile->dimensions = glm::uvec3(io.write.region.numPixels, 1);
rawTile->nBytesImageData = io.write.totalNumBytes;
if (_config.doPreProcessing) {
rawTile->tileMetaData = getTileMetaData(rawTile, io.write.region);
rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
}
return rawTile;
}
std::shared_ptr<RawTile> TileDataset::defaultTileData() {
ensureInitialized();
PixelRegion pixelRegion = {
PixelRegion::PixelCoordinate(0, 0),
PixelRegion::PixelRange(16, 16)
};
std::shared_ptr<RawTile> rawTile = std::make_shared<RawTile>();
rawTile->tileIndex = { 0, 0, 0 };
rawTile->dimensions = glm::uvec3(pixelRegion.numPixels, 1);
rawTile->nBytesImageData =
rawTile->dimensions.x * rawTile->dimensions.y * _dataLayout.bytesPerPixel;
rawTile->imageData = new char[rawTile->nBytesImageData];
for (size_t i = 0; i < rawTile->nBytesImageData; ++i) {
rawTile->imageData[i] = 0;
}
rawTile->error = CPLErr::CE_None;
if (_config.doPreProcessing) {
rawTile->tileMetaData = getTileMetaData(rawTile, pixelRegion);
//rawTile->error = std::max(rawTile->error, postProcessErrorCheck(rawTile, io));
}
return rawTile;
}
int TileDataset::maxChunkLevel() {
ensureInitialized();
if (_cached._maxLevel < 0) {
int numOverviews = _dataset->GetRasterBand(1)->GetOverviewCount();
_cached._maxLevel = -_cached._tileLevelDifference;
if (numOverviews > 0) {
_cached._maxLevel += numOverviews - 1;
}
}
return _cached._maxLevel;
}
TileDepthTransform TileDataset::getDepthTransform() {
ensureInitialized();
return _depthTransform;
}
const TileDataLayout& TileDataset::getDataLayout() {
ensureInitialized();
return _dataLayout;
}
int TileDataset::calculateTileLevelDifference(int minimumPixelSize) {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
GDALRasterBand* maxOverview;
int numOverviews = firstBand->GetOverviewCount();
int sizeLevel0;
if (numOverviews <= 0) { // No overviews. Use first band.
maxOverview = firstBand;
}
else { // Pick the highest overview.
maxOverview = firstBand->GetOverview(numOverviews - 1);
}
sizeLevel0 = maxOverview->GetXSize();
double diff = log2(minimumPixelSize) - log2(sizeLevel0);
return diff;
}
TileDepthTransform TileDataset::calculateTileDepthTransform() {
GDALRasterBand* firstBand = _dataset->GetRasterBand(1);
bool isFloat =
(_dataLayout.gdalType == GDT_Float32 || _dataLayout.gdalType == GDT_Float64);
double maximumValue =
isFloat ? 1.0 : tiledatatype::getMaximumValue(_dataLayout.gdalType);
TileDepthTransform transform;
transform.depthOffset = firstBand->GetOffset();
transform.depthScale = firstBand->GetScale() * maximumValue;
return transform;
}
bool TileDataset::gdalHasOverviews() const {
return _dataset->GetRasterBand(1)->GetOverviewCount() > 0;
}
int TileDataset::gdalOverview(const PixelRegion::PixelRange& 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) -
_cached._tileLevelDifference;
ov = glm::clamp(ov, 0, overviews - 1);
return ov;
}
int TileDataset::gdalOverview(const TileIndex& tileIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
return glm::clamp(ov, 0, overviews - 1);
}
int TileDataset::gdalVirtualOverview(const TileIndex& tileIndex) const {
int overviews = _dataset->GetRasterBand(1)->GetOverviewCount();
int ov = overviews - (tileIndex.level + _cached._tileLevelDifference + 1);
return ov;
}
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);
PixelRegion::PixelCoordinate pixelStart = geodeticToPixel(nwCorner);
PixelRegion::PixelCoordinate pixelEnd = geodeticToPixel(swCorner);
PixelRegion gdalRegion(pixelStart, pixelEnd - pixelStart);
return gdalRegion;
}
GDALRasterBand* TileDataset::gdalRasterBand(int overview, int raster) const {
GDALRasterBand* rasterBand = _dataset->GetRasterBand(raster);
// int numberOfOverviews = rasterBand->GetOverviewCount();
rasterBand = gdalHasOverviews() ? rasterBand->GetOverview(overview) : rasterBand;
ghoul_assert(rasterBand != nullptr, "Rasterband is null");
return rasterBand;
}
std::array<double, 6> TileDataset::getGeoTransform() const {
std::array<double, 6> padfTransform;
CPLErr err = _dataset->GetGeoTransform(&padfTransform[0]);
if (err == CE_Failure) {
GeodeticPatch globalCoverage(Geodetic2(0,0), Geodetic2(M_PI / 2, M_PI));
padfTransform[1] = Angle<double>::fromRadians(
globalCoverage.size().lon).asDegrees() / _dataset->GetRasterXSize();
padfTransform[5] = -Angle<double>::fromRadians(
globalCoverage.size().lat).asDegrees() / _dataset->GetRasterYSize();
padfTransform[0] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lon).asDegrees();
padfTransform[3] = Angle<double>::fromRadians(
globalCoverage.getCorner(Quad::NORTH_WEST).lat).asDegrees();
padfTransform[2] = 0;
padfTransform[4] = 0;
}
return padfTransform;
}
PixelRegion::PixelCoordinate TileDataset::geodeticToPixel(const Geodetic2& geo) const {
std::array<double, 6> padfTransform = getGeoTransform();
double Y = Angle<double>::fromRadians(geo.lat).asDegrees();
double X = Angle<double>::fromRadians(geo.lon).asDegrees();
// convert from pixel and line to geodetic coordinates
// Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2];
// Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5];
// <=>
double* a = &(padfTransform[0]);
double* b = &(padfTransform[3]);
// Xp = a[0] + P*a[1] + L*a[2];
// Yp = b[0] + P*b[1] + L*b[2];
// <=>
double divisor = (a[2] * b[1] - a[1] * b[2]);
ghoul_assert(divisor != 0.0, "Division by zero!");
//ghoul_assert(a[2] != 0.0, "a2 must not be zero!");
double P = (a[0] * b[2] - a[2] * b[0] + a[2] * Y - b[2] * X) / divisor;
double L = (-a[0] * b[1] + a[1] * b[0] - a[1] * Y + b[1] * X) / divisor;
// ref: https://www.wolframalpha.com/input/?i=X+%3D+a0+%2B+a1P+%2B+a2L,+Y+%3D+b0+%2B+b1P+%2B+b2L,+solve+for+P+and+L
double Xp = a[0] + P*a[1] + L*a[2];
double Yp = b[0] + P*b[1] + L*b[2];
ghoul_assert(abs(X - Xp) < 1e-10, "inverse should yield X as before");
ghoul_assert(abs(Y - Yp) < 1e-10, "inverse should yield Y as before");
return PixelRegion::PixelCoordinate(glm::round(P), glm::round(L));
}
Geodetic2 TileDataset::pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const {
std::array<double, 6> padfTransform = getGeoTransform();
Geodetic2 geodetic;
// Should be using radians and not degrees?
geodetic.lon = padfTransform[0] + p.x * padfTransform[1] + p.y * padfTransform[2];
geodetic.lat = padfTransform[3] + p.x * padfTransform[4] + p.y * padfTransform[5];
return geodetic;
}
TileDataset::IODescription TileDataset::getIODescription(const TileIndex& tileIndex) const {
IODescription io;
io.read.region = gdalPixelRegion(tileIndex);
if (gdalHasOverviews()) {
int overview = gdalOverview(tileIndex);
io.read.overview = overview;
io.read.region.downscalePow2(overview + 1);
io.write.region = io.read.region;
io.read.region.pad(padding);
}
else {
io.read.overview = 0;
io.write.region = io.read.region;
int virtualOverview = gdalVirtualOverview(tileIndex);
io.write.region.downscalePow2(virtualOverview + 1);
PixelRegion scaledPadding = padding;
scaledPadding.upscalePow2(std::max(virtualOverview + 1, 0));
io.read.region.pad(scaledPadding);
}
// For correct sampling in height dataset, we need to pad the texture tile
io.write.region.pad(padding);
PixelRegion::PixelRange preRound = io.write.region.numPixels;
io.write.region.roundDownToQuadratic();
io.write.region.roundUpNumPixelToNearestMultipleOf(2);
if (preRound != io.write.region.numPixels) {
LDEBUG(tileIndex << " | " << preRound.x << ", " << preRound.y << " --> " << io.write.region.numPixels.x << ", " << io.write.region.numPixels.y);
}
io.write.region.start = PixelRegion::PixelCoordinate(0, 0); // write region starts in origin
io.write.bytesPerLine = _dataLayout.bytesPerPixel * io.write.region.numPixels.x;
io.write.totalNumBytes = io.write.bytesPerLine * io.write.region.numPixels.y;
return io;
}
char* TileDataset::readImageData(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);
CPLErr err = repeatedRasterIO(rasterBand, io, dataDestination);
// CE_None = 0, CE_Debug = 1, CE_Warning = 2, CE_Failure = 3, CE_Fatal = 4
worstError = std::max(worstError, err);
}
// GDAL reads pixel lines top to bottom, we want the opposit
return imageData;
}
CPLErr TileDataset::repeatedRasterIO(GDALRasterBand* rasterBand, const IODescription& fullIO, char* dataDestination, int depth) const {
std::string spaces = " ";
std::string indentation = spaces.substr(0, 2 * depth);
CPLErr worstError = CPLErr::CE_None;
// NOTE:
// Ascii graphics illustrates the implementation details of this method, for one
// specific case. Even though the illustrated case is specific, readers can
// hopefully find it useful to get the general idea.
// Make a copy of the full IO desription as we will have to modify it
IODescription io = fullIO;
PixelRegion gdalRegion = gdalPixelRegion(rasterBand);
// Example:
// We have an io description that defines a WRITE and a READ region.
// In this case the READ region extends outside of the defined gdal region,
// meaning we will have to do wrapping
// io.write.region io.read.region
// | |
// V V
// +-------+ +-------+
// | | | |--------+
// | | | | |
// | | | | |
// +-------+ +-------+ |
// | | <-- gdalRegion
// | |
// +--------------+
//LDEBUG(indentation << "-");
//LDEBUG(indentation << "repeated read: " << io.read.region);
//LDEBUG(indentation << "repeated write: " << io.write.region);
bool didCutOff = false;
if (!io.read.region.isInside(gdalRegion)) {
// Loop through each side: left, top, right, bottom
for (int i = 0; i < 4; ++i) {
// Example:
// We are currently considering the left side of the pixel region
PixelRegion::Side side = (PixelRegion::Side) i;
IODescription cutoff = io.cut(side, gdalRegion.edge(side));
// Example:
// We cut off the left part that was outside the gdal region, and we now
// have an additional io description for the cut off region.
// Note that the cut-method used above takes care of the corresponding
// WRITE region for us.
// cutoff.write.region cutoff.read.region
// | io.write.region | io.read.region
// | | | |
// V V V V
// +-+-----+ +-+-----+
// | | | | | |--------+
// | | | | | | |
// | | | | | | |
// +-+-----+ +-+-----+ |
// | | <-- gdalRegion
// | |
// +--------------+
if (cutoff.read.region.area() > 0) {
// didCutOff = true;
// Wrap by repeating
PixelRegion::Side oppositeSide = (PixelRegion::Side) ((i + 2) % 4);
cutoff.read.region.align(oppositeSide, gdalRegion.edge(oppositeSide));
// Example:
// The cut off region is wrapped to the opposite side of the region,
// i.e. "repeated". Note that we don't want WRITE region to change,
// we're only wrapping the READ region.
// cutoff.write.region io.read.region cutoff.read.region
// | io.write.region | |
// | | V V
// V V +-----+ +-+
// +-+-----+ | |------| |
// | | | | | | |
// | | | | | | |
// | | | +-----+ +-+
// +-+-----+ | | <-- gdalRegion
// | |
// +--------------+
// Example:
// The cutoff region has been repeated along one of its sides, but
// as we can see in this example, it still has a top part outside the
// defined gdal region. This is handled through recursion.
CPLErr err = repeatedRasterIO(rasterBand, cutoff, dataDestination, depth + 1);
worstError = std::max(worstError, err);
}
}
}
else if (worstError > CPLErr::CE_None) {
LDEBUG(indentation << "Error reading padding: " << worstError);
}
CPLErr err = rasterIO(rasterBand, io, dataDestination);
// worstError = std::max(worstError, err);
// The return error from a repeated rasterIO is ONLY based on the main region,
// which in the usual case will cover the main area of the patch anyway
return err;
}
CPLErr TileDataset::rasterIO(GDALRasterBand* rasterBand, const IODescription& io,
char* dataDestination) const
{
PixelRegion gdalRegion = gdalPixelRegion(rasterBand);
ghoul_assert(io.read.region.isInside(gdalRegion), "write region of bounds!");
ghoul_assert(
io.write.region.start.x >= 0 && io.write.region.start.y >= 0,
"Invalid write region"
);
PixelRegion::PixelCoordinate end = io.write.region.end();
size_t largestIndex =
(end.y - 1) * io.write.bytesPerLine + (end.x - 1) * _dataLayout.bytesPerPixel;
ghoul_assert(largestIndex <= io.write.totalNumBytes, "Invalid write region");
char* dataDest = dataDestination;
// 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
dataDest += (io.write.totalNumBytes - io.write.bytesPerLine);
// handle requested write region
dataDest -= io.write.region.start.y * io.write.bytesPerLine; // note -= since flipped y axis
dataDest += io.write.region.start.x * _dataLayout.bytesPerPixel;
return 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
dataDest, // 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
);
}
std::shared_ptr<TileMetaData> TileDataset::getTileMetaData(
std::shared_ptr<RawTile> rawTile,
const PixelRegion& region) const
{
size_t bytesPerLine = _dataLayout.bytesPerPixel * region.numPixels.x;
// size_t totalNumBytes = bytesPerLine * region.numPixels.y;
TileMetaData* preprocessData = new TileMetaData();
preprocessData->maxValues.resize(_dataLayout.numRasters);
preprocessData->minValues.resize(_dataLayout.numRasters);
preprocessData->hasMissingData.resize(_dataLayout.numRasters);
std::vector<float> noDataValues;
noDataValues.resize(_dataLayout.numRasters);
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
preprocessData->maxValues[c] = -FLT_MAX;
preprocessData->minValues[c] = FLT_MAX;
preprocessData->hasMissingData[c] = false;
noDataValues[c] = _dataset->GetRasterBand(1)->GetNoDataValue();
}
for (size_t y = 0; y < region.numPixels.y; y++) {
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++) {
float noDataValue = _dataset->GetRasterBand(c + 1)->GetNoDataValue();
float val = tiledatatype::interpretFloat(
_dataLayout.gdalType,
&(rawTile->imageData[yi + i])
);
if (val != noDataValue) {
preprocessData->maxValues[c] = std::max(
val,
preprocessData->maxValues[c]
);
preprocessData->minValues[c] = std::min(
val,
preprocessData->minValues[c]
);
}
else {
preprocessData->hasMissingData[c] = true;
}
i += _dataLayout.bytesPerDatum;
}
}
}
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
if (preprocessData->maxValues[c] > 8800.0f) {
//LDEBUG("Bad preprocess data: " << preprocessData->maxValues[c] << " at " << region.tileIndex);
}
}
return std::shared_ptr<TileMetaData>(preprocessData);
}
CPLErr TileDataset::postProcessErrorCheck(std::shared_ptr<const RawTile> rawTile,
const IODescription& io) const
{
int success;
double missingDataValue = gdalRasterBand(io.read.overview)->GetNoDataValue(&success);
if (!success) {
// missing data value for TERRAIN.wms. Should be specified in XML
missingDataValue = 32767;
}
bool hasMissingData = false;
for (size_t c = 0; c < _dataLayout.numRasters; c++) {
hasMissingData |= rawTile->tileMetaData->maxValues[c] == missingDataValue;
}
bool onHighLevel = rawTile->tileIndex.level > 6;
if (hasMissingData && onHighLevel) {
return CE_Fatal;
}
// ugly test for heightmap overlay
if (_dataLayout.textureFormat.ghoulFormat == ghoul::opengl::Texture::Format::RG) {
// check the alpha
if (rawTile->tileMetaData->maxValues[1] == 0.0
&& rawTile->tileMetaData->minValues[1] == 0.0)
{
//return CE_Warning;
}
}
return CE_None;
}
} // namespace globebrowsing
} // namespace openspace

View File

@@ -1,188 +0,0 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATASET___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATASET___H__
#include <modules/globebrowsing/tile/textureformat.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/tile/tiledepthtransform.h>
#include <modules/globebrowsing/tile/tiledatalayout.h>
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/pixelregion.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/texture.h>
#include <gdal.h>
#include <string>
class GDALDataset;
class GDALRasterBand;
namespace openspace {
namespace globebrowsing {
class RawTile;
class GeodeticPatch;
class TileDataset {
public:
struct Configuration {
bool doPreProcessing;
int minimumTilePixelSize;
GLuint dataType = 0; // default = no datatype reinterpretation
};
/**
* Opens a GDALDataset in readonly mode and calculates meta data required for
* reading tile using a TileIndex.
*
* \param gdalDatasetDesc - A path to a specific file or raw XML describing the dataset
* \param minimumPixelSize - minimum number of pixels per side per tile requested
* \param datatype - datatype for storing pixel data in requested tile
*/
TileDataset(const std::string& gdalDatasetDesc, const Configuration& config);
~TileDataset();
//////////////////////////////////////////////////////////////////////////////////
// Public interface //
//////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<RawTile> readTileData(TileIndex tileIndex);
std::shared_ptr<RawTile> defaultTileData();
int maxChunkLevel();
TileDepthTransform getDepthTransform();
const TileDataLayout& getDataLayout();
void reset();
float noDataValueAsFloat();
const static glm::ivec2 tilePixelStartOffset;
const static glm::ivec2 tilePixelSizeDifference;
const static PixelRegion padding; // same as the two above
private:
struct IODescription {
struct ReadData {
int overview;
PixelRegion region;
} read;
struct WriteData {
PixelRegion region;
size_t bytesPerLine;
size_t totalNumBytes;
} write;
IODescription cut(PixelRegion::Side side, int pos);
};
//////////////////////////////////////////////////////////////////////////////////
// Initialization //
//////////////////////////////////////////////////////////////////////////////////
void initialize();
void ensureInitialized();
TileDepthTransform calculateTileDepthTransform();
int calculateTileLevelDifference(int minimumPixelSize);
//////////////////////////////////////////////////////////////////////////////////
// GDAL helper methods //
//////////////////////////////////////////////////////////////////////////////////
void gdalEnsureInitialized();
void setGdalProxyConfiguration();
GDALDataset* gdalDataset(const std::string& gdalDatasetDesc);
bool gdalHasOverviews() const;
int gdalOverview(const PixelRegion::PixelRange& baseRegionSize) const;
int gdalOverview(const TileIndex& tileIndex) const;
int gdalVirtualOverview(const TileIndex& tileIndex) const;
PixelRegion gdalPixelRegion(const GeodeticPatch& geodeticPatch) const;
PixelRegion gdalPixelRegion(GDALRasterBand* rasterBand) const;
GDALRasterBand* gdalRasterBand(int overview, int raster = 1) const;
//////////////////////////////////////////////////////////////////////////////////
// ReadTileData helper functions //
//////////////////////////////////////////////////////////////////////////////////
/**
Returns the geo transform from raster space to projection coordinates as defined
by GDAL.
If the transform is not available, the function returns a transform to map
the pixel coordinates to cover the whole geodetic lat long space.
*/
std::array<double, 6> getGeoTransform() const;
PixelRegion::PixelCoordinate geodeticToPixel(const Geodetic2& geo) const;
Geodetic2 pixelToGeodetic(const PixelRegion::PixelCoordinate& p) const;
IODescription getIODescription(const TileIndex& tileIndex) const;
char* readImageData(IODescription& io, CPLErr& worstError) const;
CPLErr rasterIO(GDALRasterBand* rasterBand, const IODescription& io, char* dst) const;
CPLErr repeatedRasterIO(GDALRasterBand* rasterBand, const IODescription& io, char* dst, int depth = 0) const;
std::shared_ptr<TileMetaData> getTileMetaData(std::shared_ptr<RawTile> result, const PixelRegion& region) const;
CPLErr postProcessErrorCheck(std::shared_ptr<const RawTile> ioResult, const IODescription& io) const;
//////////////////////////////////////////////////////////////////////////////////
// Member variables //
//////////////////////////////////////////////////////////////////////////////////
// init data
struct InitData {
std::string initDirectory;
std::string gdalDatasetDesc;
int minimumPixelSize;
GLuint dataType;
} _initData;
struct Cached {
int _maxLevel = -1;
double _tileLevelDifference;
} _cached;
const Configuration _config;
GDALDataset* _dataset;
TileDepthTransform _depthTransform;
TileDataLayout _dataLayout;
static bool GdalHasBeenInitialized;
bool hasBeenInitialized;
};
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TILE_DATASET___H__

View File

@@ -24,8 +24,6 @@
#include <modules/globebrowsing/tile/tilemetadata.h>
#include <gdal_priv.h>
namespace openspace {
namespace globebrowsing {

View File

@@ -24,9 +24,12 @@
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
#include <modules/globebrowsing/tile/asynctilereader.h>
#include <modules/globebrowsing/tile/tiledataset.h>
#include <modules/globebrowsing/tile/asynctiledataprovider.h>
#include <modules/globebrowsing/tile/rawtiledatareader/gdalrawtiledatareader.h>
#include <modules/globebrowsing/tile/rawtiledatareader/simplerawtiledatareader.h>
#include <modules/globebrowsing/tile/rawtiledatareader/rawtiledatareader.h>
#include <modules/globebrowsing/tile/rawtile.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
@@ -35,9 +38,8 @@ namespace {
const char* _loggerCat = "CachingTileProvider";
const char* KeyDoPreProcessing = "DoPreProcessing";
const char* KeyMinimumPixelSize = "MinimumPixelSize";
const char* KeyTilePixelSize = "TilePixelSize";
const char* KeyFilePath = "FilePath";
const char* KeyCacheSize = "CacheSize";
const char* KeyFlushInterval = "FlushInterval";
}
@@ -46,7 +48,9 @@ namespace globebrowsing {
namespace tileprovider {
CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
: _framesSinceLastRequestFlush(0)
: TileProvider(dictionary)
, _framesSinceLastRequestFlush(0)
, _defaultTile(Tile::TileUnavailable)
{
std::string name = "Name unspecified";
dictionary.getValue("Name", name);
@@ -59,25 +63,21 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
}
// 2. Initialize default values for any optional Keys
TileDataset::Configuration config;
RawTileDataReader::Configuration config;
config.doPreProcessing = false;
config.minimumTilePixelSize = 512;
config.tilePixelSize = 512;
// getValue does not work for integers
double minimumPixelSize;
double cacheSize = 512;
double minimumPixelSize;
double framesUntilRequestFlush = 60;
// 3. Check for used spcified optional keys
if (dictionary.getValue<bool>(KeyDoPreProcessing, config.doPreProcessing)) {
LDEBUG("Default doPreProcessing overridden: " << config.doPreProcessing);
}
if (dictionary.getValue<double>(KeyMinimumPixelSize, minimumPixelSize)) {
if (dictionary.getValue<double>(KeyTilePixelSize, minimumPixelSize)) {
LDEBUG("Default minimumPixelSize overridden: " << minimumPixelSize);
config.minimumTilePixelSize = static_cast<int>(minimumPixelSize);
}
if (dictionary.getValue<double>(KeyCacheSize, cacheSize)) {
LDEBUG("Default cacheSize overridden: " << cacheSize);
config.tilePixelSize = static_cast<int>(minimumPixelSize);
}
if (dictionary.getValue<double>(KeyFlushInterval, framesUntilRequestFlush)) {
LDEBUG("Default framesUntilRequestFlush overridden: " <<
@@ -85,7 +85,11 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
}
// Initialize instance variables
auto tileDataset = std::make_shared<TileDataset>(filePath, config);
#ifdef GLOBEBROWSING_USE_GDAL
auto tileDataset = std::make_shared<GdalRawTileDataReader>(filePath, config);
#else // GLOBEBROWSING_USE_GDAL
auto tileDataset = std::make_shared<SimpleRawTileDataReader>(filePath, config);
#endif // GLOBEBROWSING_USE_GDAL
// only one thread per provider supported atm
// (GDAL does not handle multiple threads for a single dataset very well
@@ -94,18 +98,16 @@ CachingTileProvider::CachingTileProvider(const ghoul::Dictionary& dictionary)
_asyncTextureDataProvider = std::make_shared<AsyncTileDataProvider>(
tileDataset, threadPool);
_tileCache = std::make_shared<TileCache>(static_cast<size_t>(cacheSize));
_framesUntilRequestFlush = framesUntilRequestFlush;
}
CachingTileProvider::CachingTileProvider(
std::shared_ptr<AsyncTileDataProvider> tileReader,
std::shared_ptr<TileCache> tileCache,
int framesUntilFlushRequestQueue)
: _asyncTextureDataProvider(tileReader)
, _tileCache(tileCache)
, _framesUntilRequestFlush(framesUntilFlushRequestQueue)
, _framesSinceLastRequestFlush(0)
, _defaultTile(Tile::TileUnavailable)
{}
CachingTileProvider::~CachingTileProvider(){
@@ -120,32 +122,29 @@ void CachingTileProvider::update() {
}
void CachingTileProvider::reset() {
_tileCache->clear();
_asyncTextureDataProvider->reset();
cache::MemoryAwareTileCache::ref().clear();
//_asyncTextureDataProvider->reset();
}
int CachingTileProvider::maxLevel() {
return _asyncTextureDataProvider->getTextureDataProvider()->maxChunkLevel();
return _asyncTextureDataProvider->getRawTileDataReader()->maxChunkLevel();
}
Tile CachingTileProvider::getTile(const TileIndex& tileIndex) {
Tile tile = Tile::TileUnavailable;
if (tileIndex.level > maxLevel()) {
tile.status = Tile::Status::OutOfRange;
return tile;
return Tile(nullptr, nullptr, Tile::Status::OutOfRange);
}
TileIndex::TileHashKey key = tileIndex.hashKey();
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
if (_tileCache->exist(key)) {
return _tileCache->get(key);
if (cache::MemoryAwareTileCache::ref().exist(key)) {
return cache::MemoryAwareTileCache::ref().get(key);
}
else {
_asyncTextureDataProvider->enqueueTileIO(tileIndex);
}
return tile;
return Tile::TileUnavailable;
}
float CachingTileProvider::noDataValueAsFloat() {
@@ -153,20 +152,20 @@ float CachingTileProvider::noDataValueAsFloat() {
}
Tile CachingTileProvider::getDefaultTile() {
if (_defaultTile.texture == nullptr) {
if (_defaultTile.texture() == nullptr) {
_defaultTile = createTile(
_asyncTextureDataProvider->getTextureDataProvider()->defaultTileData()
_asyncTextureDataProvider->getRawTileDataReader()->defaultTileData()
);
}
return _defaultTile;
}
void CachingTileProvider::initTexturesFromLoadedData() {
auto rawTiles = _asyncTextureDataProvider->getRawTiles();
for (auto rawTile : rawTiles){
TileIndex::TileHashKey key = rawTile->tileIndex.hashKey();
std::shared_ptr<RawTile> rawTile = _asyncTextureDataProvider->popFinishedRawTile();
if (rawTile) {
cache::ProviderTileKey key = { rawTile->tileIndex, uniqueIdentifier() };
Tile tile = createTile(rawTile);
_tileCache->put(key, tile);
cache::MemoryAwareTileCache::ref().put(key, tile);
}
}
@@ -176,56 +175,49 @@ void CachingTileProvider::clearRequestQueue() {
}
Tile::Status CachingTileProvider::getTileStatus(const TileIndex& tileIndex) {
auto tileDataset = _asyncTextureDataProvider->getTextureDataProvider();
if (tileIndex.level > tileDataset->maxChunkLevel()) {
auto rawTileDataReader = _asyncTextureDataProvider->getRawTileDataReader();
if (tileIndex.level > rawTileDataReader->maxChunkLevel()) {
return Tile::Status::OutOfRange;
}
TileIndex::TileHashKey key = tileIndex.hashKey();
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
if (_tileCache->exist(key)) {
return _tileCache->get(key).status;
if (cache::MemoryAwareTileCache::ref().exist(key)) {
return cache::MemoryAwareTileCache::ref().get(key).status();
}
return Tile::Status::Unavailable;
}
TileDepthTransform CachingTileProvider::depthTransform() {
return _asyncTextureDataProvider->getTextureDataProvider()->getDepthTransform();
return _asyncTextureDataProvider->getRawTileDataReader()->getDepthTransform();
}
Tile CachingTileProvider::createTile(std::shared_ptr<RawTile> rawTile) {
if (rawTile->error != CE_None) {
return{ nullptr, nullptr, Tile::Status::IOError };
if (rawTile->error != RawTile::ReadError::None) {
return Tile(nullptr, nullptr, Tile::Status::IOError);
}
// TileIndex::TileHashKey key = rawTile->tileIndex.hashKey();
TileDataLayout dataLayout =
_asyncTextureDataProvider->getTextureDataProvider()->getDataLayout();
//TileDataLayout dataLayout =
// _asyncTextureDataProvider->getTextureDataProvider()->getDataLayout();
// The texture should take ownership of the data
using ghoul::opengl::Texture;
std::shared_ptr<Texture> texture = std::make_shared<Texture>(
rawTile->imageData,
rawTile->dimensions,
dataLayout.textureFormat.ghoulFormat,
dataLayout.textureFormat.glFormat,
dataLayout.glType,
rawTile->textureFormat.ghoulFormat,
rawTile->textureFormat.glFormat,
rawTile->glType,
Texture::FilterMode::Linear,
Texture::WrappingMode::ClampToEdge);
texture->uploadTexture();
// AnisotropicMipMap must be set after texture is uploaded. Why?!
// AnisotropicMipMap must be set after texture is uploaded
texture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
Tile tile = {
texture,
rawTile->tileMetaData,
Tile::Status::OK
};
return tile;
return Tile(texture, rawTile->tileMetaData, Tile::Status::OK);
}
} // namespace tileprovider

View File

@@ -26,12 +26,13 @@
#define __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
namespace openspace {
namespace globebrowsing {
class AsyncTileDataProvider;
class RawTile;
struct RawTile;
namespace tileprovider {
@@ -44,8 +45,7 @@ public:
CachingTileProvider(const ghoul::Dictionary& dictionary);
CachingTileProvider(
std::shared_ptr<AsyncTileDataProvider> tileReader,
std::shared_ptr<TileCache> tileCache,
std::shared_ptr<AsyncTileDataProvider> tileReader,
int framesUntilFlushRequestQueue);
virtual ~CachingTileProvider();
@@ -87,7 +87,7 @@ private:
void clearRequestQueue();
std::shared_ptr<AsyncTileDataProvider> _asyncTextureDataProvider;
std::shared_ptr<TileCache> _tileCache;
//std::shared_ptr<TileCache> _tileCache;
int _framesSinceLastRequestFlush;
int _framesUntilRequestFlush;

View File

@@ -0,0 +1,385 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/globebrowsing/tile/tileprovider/projectiontileprovider.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/time.h>
#include <openspace/util/powerscaledcoordinate.h>
#include <openspace/scene/scenegraphnode.h>
#include <modules/space/rendering/planetgeometry.h>
#include <modules/newhorizons/util/imagesequencer.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/opengl/textureunit.h>
namespace {
const char* _loggerCat = "ProjectionTileProvider";
const char* keyGeometry = "Geometry";
const char* keyProjection = "Projection";
const char* keyMeridianShift = "Textures.MeridianShift";
const char* keyColorTexture = "Textures.Color";
const char* keyHeightTexture = "Textures.Height";
const char* keyRadius = "Geometry.Radius";
const char* keyShading = "PerformShading";
const char* _mainFrame = "GALACTIC";
}
namespace openspace {
namespace globebrowsing {
namespace tileprovider {
/*
documentation::Documentation ProjectionTileProvider::Documentation() {
using namespace openspace::documentation;
return {
"Renderable Planet Projection",
"newhorizons_renderable_planetprojection",
{
{
"Type",
new StringEqualVerifier("RenderablePlanetProjection"),
"",
Optional::No
},
{
keyGeometry,
new ReferencingVerifier("space_geometry_planet"),
"The geometry that is used for rendering this planet.",
Optional::No
},
{
keyProjection,
new ReferencingVerifier("newhorizons_projectioncomponent"),
"Contains information about projecting onto this planet.",
Optional::No
},
{
keyMeridianShift,
new BoolVerifier,
"Determines whether the meridian of the planet should be shifted by 180 "
"degrees. The default value is 'false'",
Optional::Yes
},
{
keyColorTexture,
new StringVerifier,
"The path to the base color texture that is used on the planet prior to "
"any image projection. The path can use tokens of the form '${...}' or "
"be specified relative to the directory of the mod file.",
Optional::No
},
{
keyHeightTexture,
new StringVerifier,
"The path to the height map texture that is used on the planet. The path "
"can use tokens of the form '${...}' or be specified relative to the "
"directory of the mod file. If no height map is specified the planet "
"does not use a height field.",
Optional::Yes
}
}
};
}
*/
ProjectionTileProvider::ProjectionTileProvider(const ghoul::Dictionary& dictionary)
: _fboProgramObject(nullptr)
, _capture(false)
, _defaultTile(Tile::TileUnavailable)
{
ghoul::Dictionary geometryDictionary;
bool success = dictionary.getValue(
keyGeometry, geometryDictionary);
if (success) {
geometryDictionary.setValue(SceneGraphNode::KeyName, "TestGeometry");
using planetgeometry::PlanetGeometry;
_geometry = std::unique_ptr<PlanetGeometry>(
PlanetGeometry::createFromDictionary(geometryDictionary)
);
}
_projectionComponent.initialize(dictionary.value<ghoul::Dictionary>(keyProjection));
addPropertySubOwner(_geometry.get());
addPropertySubOwner(_projectionComponent);
}
ProjectionTileProvider::~ProjectionTileProvider(){
}
bool ProjectionTileProvider::initialize() {
bool completeSuccess = true;
completeSuccess &= TileProvider::initialize();
_fboProgramObject = ghoul::opengl::ProgramObject::Build("fboPassProgram",
"${MODULE_NEWHORIZONS}/shaders/renderablePlanetProjection_vs.glsl",
"${MODULE_NEWHORIZONS}/shaders/renderablePlanetProjection_fs.glsl"
);
completeSuccess &= _projectionComponent.initializeGL();
completeSuccess &= _geometry->initialize(nullptr);
if (completeSuccess) {
//completeSuccess &= auxiliaryRendertarget();
// SCREEN-QUAD
const GLfloat size = 1.f;
const GLfloat w = 1.f;
const GLfloat vertex_data[] = {
-size, -size, 0.f, w, 0.f, 0.f,
size, size, 0.f, w, 1.f, 1.f,
-size, size, 0.f, w, 0.f, 1.f,
-size, -size, 0.f, w, 0.f, 0.f,
size, -size, 0.f, w, 1.f, 0.f,
size, size, 0.f, w, 1.f, 1.f,
};
glGenVertexArrays(1, &_quad);
glBindVertexArray(_quad);
glGenBuffers(1, &_vertexPositionBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast<void*>(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast<void*>(sizeof(GLfloat) * 4));
glBindVertexArray(0);
}
return completeSuccess;
}
bool ProjectionTileProvider::deinitialize() {
_projectionComponent.deinitialize();
_geometry = nullptr;
glDeleteVertexArrays(1, &_quad);
glDeleteBuffers(1, &_vertexPositionBuffer);
_fboProgramObject = nullptr;
return true;
}
void ProjectionTileProvider::update() {
// Update
if (_fboProgramObject->isDirty()) {
_fboProgramObject->rebuildFromFile();
}
_projectionComponent.update();
_time = Time::ref().j2000Seconds();
_capture = false;
if (openspace::ImageSequencer::ref().isReady()){
openspace::ImageSequencer::ref().updateSequencer(_time);
if (_projectionComponent.doesPerformProjection()) {
_capture = openspace::ImageSequencer::ref().getImagePaths(
_imageTimes,
_projectionComponent.projecteeId(),
_projectionComponent.instrumentId()
);
}
}
_stateMatrix = glm::dmat3(1.0);
// Projection
if (_projectionComponent.needsClearProjection())
_projectionComponent.clearAllProjections();
_camScaling = glm::vec2(1.0);// data.camera.scaling();
_up = glm::vec3(0,1,0);// data.camera.lookUpVectorCameraSpace();
if (_capture && _projectionComponent.doesPerformProjection()) {
for (const Image& img : _imageTimes) {
attitudeParameters(img.timeRange.start);
imageProjectGPU(_projectionComponent.loadProjectionTexture(img.path));
}
_capture = false;
}
attitudeParameters(_time);
_imageTimes.clear();
}
void ProjectionTileProvider::reset() {
}
int ProjectionTileProvider::maxLevel() {
return 3;
}
Tile ProjectionTileProvider::getTile(const TileIndex& tileIndex) {
_projectionComponent.projectionTexture();
}
float ProjectionTileProvider::noDataValueAsFloat() {
}
Tile ProjectionTileProvider::getDefaultTile() {
}
Tile::Status ProjectionTileProvider::getTileStatus(const TileIndex& tileIndex) {
return Tile::Status::OK;
}
TileDepthTransform ProjectionTileProvider::depthTransform() {
}
void ProjectionTileProvider::attitudeParameters(double time) {
// precomputations for shader
_instrumentMatrix = SpiceManager::ref().positionTransformMatrix(
_projectionComponent.instrumentId(), _mainFrame, time
);
_transform = glm::mat4(1);
//90 deg rotation w.r.t spice req.
glm::mat4 rot = glm::rotate(
_transform,
static_cast<float>(M_PI_2),
glm::vec3(1, 0, 0)
);
glm::mat4 roty = glm::rotate(
_transform,
static_cast<float>(M_PI_2),
glm::vec3(0, -1, 0)
);
_transform = glm::mat4(_stateMatrix);
glm::dvec3 bs;
try {
SpiceManager::FieldOfViewResult res = SpiceManager::ref().fieldOfView(_projectionComponent.instrumentId());
bs = std::move(res.boresightVector);
}
catch (const SpiceManager::SpiceException& e) {
LERRORC(e.component, e.what());
return;
}
double lightTime;
glm::dvec3 p = SpiceManager::ref().targetPosition(
_projectionComponent.projectorId(),
_projectionComponent.projecteeId(),
_mainFrame,
_projectionComponent.aberration(),
time,
lightTime
);
psc position = PowerScaledCoordinate::CreatePowerScaledCoordinate(p.x, p.y, p.z);
//change to KM and add psc camera scaling.
position[3] += (3 + _camScaling[1]);
//position[3] += 3;
glm::vec3 cpos = position.vec3();
float distance = glm::length(cpos);
float radius = 1185000.0f;
_projectorMatrix = _projectionComponent.computeProjectorMatrix(
cpos,
bs,
_up,
_instrumentMatrix,
_projectionComponent.fieldOfViewY(),
_projectionComponent.aspectRatio(),
distance - radius,
distance + radius,
_boresight
);
}
void ProjectionTileProvider::imageProjectGPU(
std::shared_ptr<ghoul::opengl::Texture> projectionTexture)
{
_projectionComponent.imageProjectBegin();
_fboProgramObject->activate();
ghoul::opengl::TextureUnit unitFbo;
unitFbo.activate();
projectionTexture->bind();
_fboProgramObject->setUniform("projectionTexture", unitFbo);
_fboProgramObject->setUniform("ProjectorMatrix", _projectorMatrix);
_fboProgramObject->setUniform("ModelTransform" , _transform);
_fboProgramObject->setUniform("_scaling" , _camScaling);
_fboProgramObject->setUniform("boresight" , _boresight);
if (_geometry->hasProperty("radius")){
ghoul::any r = _geometry->property("radius")->get();
if (glm::vec4* radius = ghoul::any_cast<glm::vec4>(&r)){
_fboProgramObject->setUniform("_radius", radius);
}
}else{
LERROR("Geometry object needs to provide radius");
}
if (_geometry->hasProperty("segments")){
ghoul::any s = _geometry->property("segments")->get();
if (int* segments = ghoul::any_cast<int>(&s)){
_fboProgramObject->setUniform("_segments", segments[0]);
}
}else{
LERROR("Geometry object needs to provide segment count");
}
glBindVertexArray(_quad);
glDrawArrays(GL_TRIANGLES, 0, 6);
_fboProgramObject->deactivate();
_projectionComponent.imageProjectEnd();
}
} // namespace tileprovider
} // namespace globebrowsing
} // namespace openspace

View File

@@ -0,0 +1,121 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <ghoul/opengl/programobject.h>
#include <modules/space/rendering/planetgeometry.h>
#include <modules/newhorizons/util/projectioncomponent.h>
#include <modules/newhorizons/util/sequenceparser.h>
namespace openspace {
namespace globebrowsing {
class AsyncTileDataProvider;
class RawTile;
namespace tileprovider {
class ProjectionTileProvider : public TileProvider {
public:
ProjectionTileProvider(const ghoul::Dictionary& dictionary);
virtual ~ProjectionTileProvider() override;
virtual Tile getTile(const TileIndex& tileIndex) override;
virtual Tile getDefaultTile() override;
virtual Tile::Status getTileStatus(const TileIndex& tileIndex) override;
virtual TileDepthTransform depthTransform() override;
virtual void update() override;
virtual void reset() override;
virtual int maxLevel() override;
virtual float noDataValueAsFloat() override;
bool initialize() override;
bool deinitialize() override;
//static documentation::Documentation Documentation();
protected:
bool loadTextures();
void attitudeParameters(double time);
private:
void imageProjectGPU(std::shared_ptr<ghoul::opengl::Texture> projectionTexture);
ProjectionComponent _projectionComponent;
//properties::StringProperty _colorTexturePath;
//properties::StringProperty _heightMapTexturePath;
//properties::IntProperty _rotation;
std::unique_ptr<ghoul::opengl::ProgramObject> _fboProgramObject;
//std::unique_ptr<ghoul::opengl::Texture> _baseTexture;
//std::unique_ptr<ghoul::opengl::Texture> _heightMapTexture;
//properties::BoolProperty _shiftMeridianBy180;
//properties::FloatProperty _heightExaggeration;
//properties::FloatProperty _debugProjectionTextureRotation;
std::unique_ptr<planetgeometry::PlanetGeometry> _geometry;
glm::vec2 _camScaling;
glm::vec3 _up;
glm::mat4 _transform;
glm::mat4 _projectorMatrix;
glm::dmat3 _stateMatrix;
glm::dmat3 _instrumentMatrix;
glm::vec3 _boresight;
double _time;
std::vector<Image> _imageTimes;
std::string _frame;
bool _capture;
GLuint _quad;
GLuint _vertexPositionBuffer;
private:
Tile _defaultTile;
};
} // namespace tileprovider
} // namespace globebrowsing
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___CACHING_TILE_PROVIDER___H__

View File

@@ -36,7 +36,9 @@ namespace openspace {
namespace globebrowsing {
namespace tileprovider {
SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) {
SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary)
: _tile(nullptr, nullptr, Tile::Status::Unavailable)
{
// Required input
if (!dictionary.getValue<std::string>(KeyFilePath, _imagePath)) {
throw std::runtime_error(std::string("Must define key '") + KeyFilePath + "'");
@@ -47,6 +49,7 @@ SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary) {
SingleImageProvider::SingleImageProvider(const std::string& imagePath)
: _imagePath(imagePath)
, _tile(nullptr, nullptr, Tile::Status::Unavailable)
{
reset();
}
@@ -60,7 +63,7 @@ Tile SingleImageProvider::getDefaultTile() {
}
Tile::Status SingleImageProvider::getTileStatus(const TileIndex& index) {
return _tile.status;
return _tile.status();
}
TileDepthTransform SingleImageProvider::depthTransform() {
@@ -75,15 +78,16 @@ void SingleImageProvider::update() {
}
void SingleImageProvider::reset() {
_tile = Tile();
_tile.texture = std::shared_ptr<ghoul::opengl::Texture>(
auto tileTexture = std::shared_ptr<ghoul::opengl::Texture>(
std::move(ghoul::io::TextureReader::ref().loadTexture(_imagePath))
);
_tile.status = _tile.texture != nullptr ? Tile::Status::OK : Tile::Status::IOError;
_tile.metaData = nullptr;
auto tileStatus = tileTexture != nullptr ? Tile::Status::OK : Tile::Status::IOError;
auto tileMetaData = nullptr;
_tile.texture->uploadTexture();
_tile.texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
tileTexture->uploadTexture();
tileTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
_tile = Tile(tileTexture, tileMetaData, tileStatus);
}
int SingleImageProvider::maxLevel() {

View File

@@ -46,6 +46,7 @@ namespace {
}
SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary)
: _backgroundTile(Tile::TileUnavailable)
{
_fontSize = 50;
_font = OsEng.fontManager().font("Mono", _fontSize);
@@ -56,16 +57,19 @@ SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& di
}
_ellipsoid = Ellipsoid(radii);
_backgroundTile.status = Tile::Status::Unavailable;
std::string backgroundImagePath;
auto backgroundTileStatus = Tile::Status::Unavailable;
std::shared_ptr<ghoul::opengl::Texture> backgroundTileTexture;
std::string backgroundImagePath;
if (dictionary.getValue(KeyBackgroundImagePath, backgroundImagePath)) {
using namespace ghoul::io;
std::string imgAbsPath = absPath(backgroundImagePath);
_backgroundTile.texture = TextureReader::ref().loadTexture(imgAbsPath);
_backgroundTile.texture->uploadTexture();
_backgroundTile.texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
_backgroundTile.status = Tile::Status::OK;
backgroundTileTexture = TextureReader::ref().loadTexture(imgAbsPath);
backgroundTileTexture->uploadTexture();
backgroundTileTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
backgroundTileStatus = Tile::Status::OK;
}
_backgroundTile = Tile(backgroundTileTexture, nullptr, backgroundTileStatus);
}
void SizeReferenceTileProvider::renderText(const ghoul::fontrendering::FontRenderer&
@@ -124,12 +128,11 @@ TileIndex::TileHashKey SizeReferenceTileProvider::toHash(const TileIndex& tileIn
}
Tile SizeReferenceTileProvider::backgroundTile(const TileIndex& tileIndex) const {
if (_backgroundTile.status == Tile::Status::OK) {
Tile tile;
auto t = _backgroundTile.texture;
if (_backgroundTile.status() == Tile::Status::OK) {
auto t = _backgroundTile.texture();
void* pixelData = new char[t->expectedPixelDataSize()];
memcpy(pixelData, t->pixelData(), t->expectedPixelDataSize());
tile.texture = std::make_shared<ghoul::opengl::Texture>(
auto tileTexture = std::make_shared<ghoul::opengl::Texture>(
pixelData,
t->dimensions(),
t->format(),
@@ -138,10 +141,10 @@ Tile SizeReferenceTileProvider::backgroundTile(const TileIndex& tileIndex) const
t->filter(),
t->wrapping()
);
tile.texture->uploadTexture();
tile.texture->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::Yes);
tile.status = Tile::Status::OK;
return tile;
tileTexture->uploadTexture();
tileTexture->setDataOwnership(ghoul::opengl::Texture::TakeOwnership::Yes);
auto tileStatus = Tile::Status::OK;
return Tile(tileTexture, nullptr, tileStatus);
}
else {
// use default background

View File

@@ -22,6 +22,8 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifdef GLOBEBROWSING_USE_GDAL
#include <modules/globebrowsing/tile/tileprovider/temporaltileprovider.h>
#include <modules/globebrowsing/tile/tileprovider/cachingtileprovider.h>
@@ -58,7 +60,6 @@ const char* TemporalTileProvider::TemporalXMLTags::TIME_FORMAT = "OpenSpaceTimeI
TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
: _initDict(dictionary)
{
if (!dictionary.getValue<std::string>(KeyFilePath, _datasetFile)) {
throw std::runtime_error(std::string("Must define key '") + KeyFilePath + "'");
}
@@ -74,9 +75,6 @@ TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
(std::istreambuf_iterator<char>())
);
_gdalXmlTemplate = consumeTemporalMetaData(xml);
std::shared_ptr<TileProvider> tileProvider = getTileProvider();
ghoul_assert(tileProvider, "No tile provider found");
_defaultTile = tileProvider->getDefaultTile();
}
std::string TemporalTileProvider::consumeTemporalMetaData(const std::string& xml) {
@@ -168,7 +166,7 @@ Tile TemporalTileProvider::getTile(const TileIndex& tileIndex) {
}
Tile TemporalTileProvider::getDefaultTile() {
return _defaultTile;
return getTileProvider()->getDefaultTile();
}
int TemporalTileProvider::maxLevel() {
@@ -384,3 +382,5 @@ bool TimeQuantizer::quantize(Time& t, bool clamp) const {
} // namespace tileprovider
} // namespace globebrowsing
} // namespace openspace
#endif // GLOBEBROWSING_USE_GDAL

View File

@@ -22,6 +22,7 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifdef GLOBEBROWSING_USE_GDAL
#ifndef __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
#define __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
@@ -287,8 +288,6 @@ private:
// Used for creation of time specific instances of CachingTileProvider
ghoul::Dictionary _initDict;
Tile _defaultTile;
std::shared_ptr<TileProvider> _currentTileProvider;
TimeFormat* _timeFormat;
@@ -300,3 +299,4 @@ private:
} // namespace openspace
#endif // __OPENSPACE_MODULE_GLOBEBROWSING___TEMPORAL_TILE_PROVIDER___H__
#endif // GLOBEBROWSING_USE_GDAL

View File

@@ -26,6 +26,7 @@
#include <modules/globebrowsing/geometry/geodeticpatch.h>
#include <modules/globebrowsing/tile/tileindex.h>
#include <modules/globebrowsing/cache/memoryawaretilecache.h>
#include <openspace/engine/openspaceengine.h>
@@ -42,8 +43,7 @@ namespace globebrowsing {
namespace tileprovider {
TextTileProvider::TextTileProvider(const glm::uvec2& textureSize, size_t fontSize)
: _tileCache(500)
, _textureSize(textureSize)
: _textureSize(textureSize)
, _fontSize(fontSize)
{
_font = OsEng.fontManager().font("Mono", _fontSize);
@@ -59,13 +59,14 @@ TextTileProvider::~TextTileProvider() {
}
Tile TextTileProvider::getTile(const TileIndex& tileIndex) {
TileIndex::TileHashKey key = tileIndex.hashKey();
if (!_tileCache.exist(key)) {
_tileCache.put(key, createChunkIndexTile(tileIndex));
}
cache::ProviderTileKey key = { tileIndex, uniqueIdentifier() };
return _tileCache.get(key);
if (!cache::MemoryAwareTileCache::ref().exist(key)) {
cache::MemoryAwareTileCache::ref().put(
key, createChunkIndexTile(tileIndex));
}
return cache::MemoryAwareTileCache::ref().get(key);
}
Tile TextTileProvider::getDefaultTile() {
@@ -86,7 +87,7 @@ TileDepthTransform TextTileProvider::depthTransform() {
void TextTileProvider::update() {}
void TextTileProvider::reset() {
_tileCache.clear();
cache::MemoryAwareTileCache::ref().clear();
}
Tile TextTileProvider::createChunkIndexTile(const TileIndex& tileIndex) {
@@ -104,14 +105,14 @@ Tile TextTileProvider::createChunkIndexTile(const TileIndex& tileIndex) {
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
*(tile.texture),
*(tile.texture()),
0
);
glViewport(
0, 0,
static_cast<GLsizei>(tile.texture->width()),
static_cast<GLsizei>(tile.texture->height())
static_cast<GLsizei>(tile.texture()->width()),
static_cast<GLsizei>(tile.texture()->height())
);
ghoul_assert(_fontRenderer != nullptr, "_fontRenderer must not be null");

View File

@@ -95,7 +95,6 @@ private:
Tile createChunkIndexTile(const TileIndex& tileIndex);
std::unique_ptr<ghoul::fontrendering::FontRenderer> _fontRenderer;
TileCache _tileCache;
GLuint _fbo;
};

View File

@@ -31,6 +31,8 @@
#include <ghoul/misc/dictionary.h>
#include <ghoul/logging/logmanager.h>
#include <climits>
namespace {
const std::string _loggerCat = "TileProvider";
@@ -40,7 +42,9 @@ namespace {
namespace openspace {
namespace globebrowsing {
namespace tileprovider {
unsigned int TileProvider::_numTileProviders = 0;
std::unique_ptr<TileProvider> TileProvider::createFromDictionary(const ghoul::Dictionary& dictionary) {
std::string type = "LRUCaching";
dictionary.getValue(KeyType, type);
@@ -55,13 +59,26 @@ std::unique_ptr<TileProvider> TileProvider::createFromDictionary(const ghoul::Di
return result;
}
TileProvider::TileProvider(const ghoul::Dictionary& dictionary) {};
TileProvider::TileProvider() :
properties::PropertyOwner("tileProvider"),
_initialized(false) {
initialize();
}
TileProvider::TileProvider(const ghoul::Dictionary& dictionary)
: properties::PropertyOwner("tileProvider")
, _initialized(false)
{
initialize();
};
float TileProvider::noDataValueAsFloat() {
ghoul_assert(_initialized, "TileProvider was not initialized.");
return std::numeric_limits<float>::min();
}
ChunkTile TileProvider::getChunkTile(TileIndex tileIndex, int parents, int maxParents) {
ghoul_assert(_initialized, "TileProvider was not initialized.");
TileUvTransform uvTransform;
uvTransform.uvOffset = glm::vec2(0, 0);
uvTransform.uvScale = glm::vec2(1, 1);
@@ -80,34 +97,35 @@ ChunkTile TileProvider::getChunkTile(TileIndex tileIndex, int parents, int maxPa
maxParents--;
}
if(maxParents < 0){
return { Tile::TileUnavailable, uvTransform };
return ChunkTile{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
}
// Step 3. Traverse 0 or more parents up the chunkTree until we find a chunk that
// has a loaded tile ready to use.
while (tileIndex.level > 1) {
Tile tile = getTile(tileIndex);
if (tile.status != Tile::Status::OK) {
if (tile.status() != Tile::Status::OK) {
if (--maxParents < 0){
return{ Tile::TileUnavailable, uvTransform };
return ChunkTile{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
}
tileselector::ascendToParent(tileIndex, uvTransform);
}
else {
return { tile, uvTransform };
return ChunkTile{ tile, uvTransform, TileDepthTransform() };
}
}
return { Tile::TileUnavailable, uvTransform };
return ChunkTile{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
}
ChunkTilePile TileProvider::getChunkTilePile(TileIndex tileIndex, int pileSize){
ghoul_assert(_initialized, "TileProvider was not initialized.");
ghoul_assert(pileSize >= 0, "pileSize must be positive");
ChunkTilePile chunkTilePile;
chunkTilePile.resize(pileSize);
for (size_t i = 0; i < pileSize; ++i) {
chunkTilePile[i] = getChunkTile(tileIndex, i);
if (chunkTilePile[i].tile.status == Tile::Status::Unavailable) {
if (chunkTilePile[i].tile.status() == Tile::Status::Unavailable) {
if (i>0) {
chunkTilePile[i].tile = chunkTilePile[i-1].tile;
chunkTilePile[i].uvTransform.uvOffset = chunkTilePile[i-1].uvTransform.uvOffset;
@@ -123,6 +141,24 @@ ChunkTilePile TileProvider::getChunkTilePile(TileIndex tileIndex, int pileSize){
return std::move(chunkTilePile);
}
bool TileProvider::initialize() {
ghoul_assert(!_initialized, "TileProvider can only be initialized once.");
_uniqueIdentifier = _numTileProviders;
_numTileProviders++;
if (_numTileProviders == UINT_MAX) {
_numTileProviders--;
return false;
}
_initialized = true;
return true;
}
unsigned int TileProvider::uniqueIdentifier() const {
ghoul_assert(_initialized, "TileProvider was not initialized.");
return _uniqueIdentifier;
}
} // namespace tileprovider
} // namespace globebrowsing
} // namespace openspace

View File

@@ -27,7 +27,9 @@
#include <modules/globebrowsing/tile/chunktile.h>
#include <modules/globebrowsing/tile/tile.h>
#include <modules/globebrowsing/other/lrucache.h>
#include <modules/globebrowsing/cache/lrucache.h>
#include <openspace/properties/propertyowner.h>
#include <vector>
@@ -39,7 +41,7 @@ namespace tileprovider {
* Interface for providing <code>Tile</code>s given a
* <code>TileIndex</code>.
*/
class TileProvider {
class TileProvider : public properties::PropertyOwner {
public:
/**
* Factory method for instantiating different implementations of
@@ -50,9 +52,9 @@ public:
static std::unique_ptr<TileProvider> createFromDictionary(const ghoul::Dictionary& dictionary);
/**
* Empty default constructor
* Default constructor.
*/
TileProvider() = default;
TileProvider();
/**
* Implementations of the TileProvider interface must implement
@@ -66,7 +68,10 @@ public:
* Virtual destructor that subclasses should override to do
* clean up.
*/
virtual ~TileProvider() { }
virtual ~TileProvider() = default;
virtual bool initialize();
virtual bool deinitialize() { return true; };
/**
* Method for querying tiles, given a specified <code>TileIndex</code>.
@@ -140,9 +145,19 @@ public:
* \returns the no data value for the dataset. Default is the minimum float avalue.
*/
virtual float noDataValueAsFloat();
};
using TileCache = LRUCache<TileIndex::TileHashKey, Tile>;
/**
* \returns a unique identifier for the <code>TileProvider<\code>. All
* <code>TileProviders<\code> have an ID starting at 0 from the first created.
* The maximum number of unique identifiers is UINT_MAX
*/
unsigned int uniqueIdentifier() const;
private:
static unsigned int _numTileProviders;
unsigned int _uniqueIdentifier;
bool _initialized;
};
} // namespace tileprovider
} // namespace globebrowsing

View File

@@ -25,6 +25,7 @@
#include <modules/globebrowsing/tile/tileprovider/tileproviderbylevel.h>
#include <ghoul/misc/dictionary.h>
#include <ghoul/logging/logmanager.h>
namespace {
const char* KeyProviders = "LevelTileProviders";
@@ -37,43 +38,56 @@ namespace globebrowsing {
namespace tileprovider {
TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) {
std::string name = "Name unspecified";
dictionary.getValue("Name", name);
const char* _loggerCat = ("TileProviderByLevel" + name).c_str();
ghoul::Dictionary providers = dictionary.value<ghoul::Dictionary>(KeyProviders);
for (size_t i = 0; i < providers.size(); i++) {
std::string dictKey = std::to_string(i + 1);
ghoul::Dictionary levelProviderDict = providers.value<ghoul::Dictionary>(
dictKey
);
double floatMaxLevel;
int maxLevel = 0;
if (!levelProviderDict.getValue<double>(KeyMaxLevel, floatMaxLevel)) {
throw std::runtime_error(
"Must define key '" + std::string(KeyMaxLevel) + "'"
try {
std::string dictKey = std::to_string(i + 1);
ghoul::Dictionary levelProviderDict = providers.value<ghoul::Dictionary>(
dictKey
);
}
maxLevel = std::round(floatMaxLevel);
ghoul::Dictionary providerDict;
if (!levelProviderDict.getValue<ghoul::Dictionary>(KeyTileProvider, providerDict))
{
throw std::runtime_error(
"Must define key '" + std::string(KeyTileProvider) + "'"
double floatMaxLevel;
int maxLevel = 0;
if (!levelProviderDict.getValue<double>(KeyMaxLevel, floatMaxLevel)) {
throw std::runtime_error(
"Must define key '" + std::string(KeyMaxLevel) + "'"
);
}
maxLevel = std::round(floatMaxLevel);
ghoul::Dictionary providerDict;
if (!levelProviderDict.getValue<ghoul::Dictionary>(KeyTileProvider, providerDict)) {
throw std::runtime_error(
"Must define key '" + std::string(KeyTileProvider) + "'"
);
}
_levelTileProviders.push_back(
std::shared_ptr<TileProvider>(TileProvider::createFromDictionary(providerDict))
);
}
_levelTileProviders.push_back(
std::shared_ptr<TileProvider>(TileProvider::createFromDictionary(providerDict))
);
// Ensure we can represent the max level
if(_providerIndices.size() < maxLevel){
_providerIndices.resize(maxLevel+1, -1);
// Ensure we can represent the max level
if(_providerIndices.size() < maxLevel){
_providerIndices.resize(maxLevel+1, -1);
}
// map this level to the tile provider index
_providerIndices[maxLevel] = _levelTileProviders.size() - 1;
}
// map this level to the tile provider index
_providerIndices[maxLevel] = _levelTileProviders.size() - 1;
catch (const ghoul::RuntimeError& e) {
LWARNING("Unable to create tile provider: " + std::string(e.what()));
}
}
// If all failed
if (_levelTileProviders.size() == 0) {
throw ghoul::RuntimeError("Failed to create tile provider: " + name);
}
// Fill in the gaps (value -1) in provider indices, from back to end
for(int i = _providerIndices.size() - 2; i >= 0; --i){
if(_providerIndices[i] == -1){

View File

@@ -26,20 +26,23 @@
#include <modules/globebrowsing/rendering/layer/layergroup.h>
#include <modules/globebrowsing/tile/tileprovider/tileprovider.h>
#include <modules/globebrowsing/rendering/layer/layerrendersettings.h>
namespace openspace {
namespace globebrowsing {
namespace tileselector {
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, TileIndex tileIndex) {
ChunkTile mostHighResolution;
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, const TileIndex& tileIndex) {
TileUvTransform uvTransform;
uvTransform.uvScale.x = 0;
ChunkTile mostHighResolution{ Tile::TileUnavailable, uvTransform, TileDepthTransform() };
mostHighResolution.tile = Tile::TileUnavailable;
mostHighResolution.uvTransform.uvScale.x = 0;
for (const auto& layer : layerGroup.activeLayers()) {
ChunkTile chunkTile = layer->tileProvider()->getChunkTile(tileIndex);
bool tileIsOk = chunkTile.tile.status == Tile::Status::OK;
bool tileHasMetaData = chunkTile.tile.metaData != nullptr;
bool tileIsOk = chunkTile.tile.status() == Tile::Status::OK;
bool tileHasMetaData = chunkTile.tile.metaData() != nullptr;
bool tileIsHigherResolution =
chunkTile.uvTransform.uvScale.x > mostHighResolution.uvTransform.uvScale.x;
if (tileIsOk && tileHasMetaData && tileIsHigherResolution) {
@@ -69,6 +72,28 @@ std::vector<ChunkTile> getTilesSortedByHighestResolution(const LayerGroup& layer
return tiles;
}
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> >
getTilesAndSettingsSortedByHighestResolution(const LayerGroup& layerGroup,
const TileIndex& tileIndex)
{
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> > tilesAndSettings;
for (const auto& layer : layerGroup.activeLayers()) {
tilesAndSettings.push_back({ layer->tileProvider()->getChunkTile(tileIndex), &layer->renderSettings() });
}
std::sort(
tilesAndSettings.begin(),
tilesAndSettings.end(),
[](const std::pair<ChunkTile, const LayerRenderSettings*> & lhs,
const std::pair<ChunkTile, const LayerRenderSettings*> & rhs)
{
return lhs.first.uvTransform.uvScale.x > rhs.first.uvTransform.uvScale.x;
}
);
return tilesAndSettings;
}
void ascendToParent(TileIndex& tileIndex, TileUvTransform& uv) {
uv.uvOffset *= 0.5;
uv.uvScale *= 0.5;

View File

@@ -34,13 +34,17 @@ namespace globebrowsing {
struct LayerGroup;
struct TileIndex;
struct LayerRenderSettings;
namespace tileselector {
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, TileIndex& tileIndex);
ChunkTile getHighestResolutionTile(const LayerGroup& layerGroup, const TileIndex& tileIndex);
std::vector<ChunkTile> getTilesSortedByHighestResolution(const LayerGroup& layerGroup,
const TileIndex& tileIndex);
const TileIndex& tileIndex);
std::vector<std::pair<ChunkTile, const LayerRenderSettings*> >
getTilesAndSettingsSortedByHighestResolution(const LayerGroup& layerGroup,
const TileIndex& tileIndex);
void ascendToParent(TileIndex& tileIndex, TileUvTransform& uv);

View File

@@ -28,10 +28,9 @@ helper.setCommonKeys = function()
"openspace.toggleShutdown()",
"Toggles the shutdown that will stop OpenSpace after a grace period. Press again to cancel the shutdown during this period."
)
openspace.bindKeyLocal(
"PRINT_SCREEN",
"openspace.takeScreenshot()",
"openspace.setPropertyValueSingle('RenderEngine.takeScreenshot', nil)",
"Saves the contents of the screen to a file in the working directory."
)
openspace.bindKey(

View File

@@ -551,7 +551,7 @@ void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& came
_globe->ellipsoid().geodeticSurfaceNormal(
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
dvec3 directionFromSurfaceToCamera =
dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace;
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
dvec3 centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
dvec3 ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);
@@ -639,7 +639,7 @@ void GlobeBrowsingInteractionMode::updateCameraStateFromMouseStates(Camera& came
_globe->ellipsoid().geodeticSurfaceNormal(
_globe->ellipsoid().cartesianToGeodetic2(cameraPositionModelSpace));
directionFromSurfaceToCamera =
dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace;
normalize(dmat3(modelTransform) * directionFromSurfaceToCameraModelSpace);
centerToEllipsoidSurface = dmat3(modelTransform) * (_globe->projectOnEllipsoid(cameraPositionModelSpace) -
directionFromSurfaceToCameraModelSpace * ellipsoidShrinkTerm);
ellipsoidSurfaceToCamera = camPos - (centerPos + centerToEllipsoidSurface);

View File

@@ -24,6 +24,8 @@
#include "gtest/gtest.h"
#ifdef GLOBEBROWSING_USE_GDAL
#include "gdal.h"
#include "gdal_priv.h"
@@ -60,3 +62,5 @@ TEST_F(GdalWmsTest, Simple) {
// This assertion fails
//ASSERT_NE(poDataset, nullptr) << "Failed to load testFile";
}
#endif // GLOBEBROWSING_USE_GDAL

View File

@@ -24,7 +24,7 @@
#include "gtest/gtest.h"
#include <modules/globebrowsing/other/lrucache.h>
#include <modules/globebrowsing/cache/lrucache.h>
#define _USE_MATH_DEFINES
#include <math.h>
@@ -33,14 +33,14 @@
class LRUCacheTest : public testing::Test {};
TEST_F(LRUCacheTest, Get) {
openspace::globebrowsing::LRUCache<int, std::string> lru(4);
openspace::globebrowsing::cache::LRUCache<int, std::string> lru(4);
lru.put(1, "hej");
lru.put(12, "san");
ASSERT_STREQ(lru.get(1).c_str(), "hej") << "testing get";
}
TEST_F(LRUCacheTest, CleaningCache) {
openspace::globebrowsing::LRUCache<int, double> lru(4);
openspace::globebrowsing::cache::LRUCache<int, double> lru(4);
lru.put(1, 1.2);
lru.put(12, 2.3);
lru.put(123, 33.4);
@@ -72,7 +72,7 @@ namespace std {
}
TEST_F(LRUCacheTest, StructKey) {
openspace::globebrowsing::LRUCache<MyKey, std::string> lru(4);
openspace::globebrowsing::cache::LRUCache<MyKey, std::string> lru(4);
// These two custom keys should be treated as equal
MyKey key1 = { 2, 3 };