mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-05 11:09:37 -06:00
Carving out cachable geodetic tiles from GDAL datasets
This commit is contained in:
@@ -3,4 +3,11 @@
|
||||
<ServerUrl>http://198.102.45.23/arcgis/rest/services/worldelevation3d/terrain3d?</ServerUrl>
|
||||
<TiledGroupName>GCS_Elevation</TiledGroupName>
|
||||
</Service>
|
||||
<DataWindow>
|
||||
<UpperLeftX>-180.0</UpperLeftX>
|
||||
<UpperLeftY>90.0</UpperLeftY>
|
||||
<LowerRightX>180.0</LowerRightX>
|
||||
<LowerRightY>-90.0</LowerRightY>
|
||||
<YOrigin>bottom</YOrigin>
|
||||
</DataWindow>
|
||||
</GDAL_WMS>
|
||||
|
||||
@@ -23,9 +23,8 @@
|
||||
****************************************************************************************/
|
||||
|
||||
#include <modules/globebrowsing/geodetics/geodetic2.h>
|
||||
|
||||
#include <modules/globebrowsing/globes/chunknode.h>
|
||||
#include <modules/globebrowsing/geodetics/angle.h>
|
||||
#include <modules/globebrowsing/geodetics/ellipsoid.h>
|
||||
|
||||
#include <ghoul/misc/assert.h>
|
||||
|
||||
@@ -33,260 +32,285 @@
|
||||
#include <math.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "Geodetic2";
|
||||
const std::string _loggerCat = "Geodetic2";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// GEODETIC2 //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// GEODETIC2 //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Geodetic2::Geodetic2()
|
||||
: lat(0)
|
||||
, lon(0)
|
||||
{}
|
||||
Geodetic2::Geodetic2()
|
||||
: lat(0)
|
||||
, lon(0)
|
||||
{}
|
||||
|
||||
Geodetic2::Geodetic2(Scalar latitude, Scalar longitude)
|
||||
: lat(latitude)
|
||||
, lon(longitude)
|
||||
{
|
||||
|
||||
}
|
||||
Geodetic2::Geodetic2(Scalar latitude, Scalar longitude)
|
||||
: lat(latitude)
|
||||
, lon(longitude)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Geodetic2::Geodetic2(const Geodetic2& p)
|
||||
: Geodetic2(p.lat, p.lon)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Vec2 Geodetic2::toLonLatVec2() const {
|
||||
return Vec2(lon, lat);
|
||||
}
|
||||
|
||||
bool Geodetic2::operator==(const Geodetic2& other) const {
|
||||
return lat == other.lat && lon == other.lon;
|
||||
}
|
||||
|
||||
Geodetic2 Geodetic2 ::operator+(const Geodetic2& other) const {
|
||||
return Geodetic2(lat - other.lat, lon - other.lon);
|
||||
}
|
||||
|
||||
Geodetic2 Geodetic2 ::operator-(const Geodetic2& other) const {
|
||||
return Geodetic2(lat - other.lat, lon - other.lon);
|
||||
}
|
||||
|
||||
Geodetic2 Geodetic2::operator*(Scalar scalar) const {
|
||||
return Geodetic2(lat * scalar, lon * scalar);
|
||||
}
|
||||
|
||||
Geodetic2 Geodetic2::operator/(Scalar scalar) const {
|
||||
return Geodetic2(lat / scalar, lon / scalar);
|
||||
}
|
||||
Geodetic2::Geodetic2(const Geodetic2& p)
|
||||
: Geodetic2(p.lat, p.lon)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// GEODETICPATCH //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
Vec2 Geodetic2::toLonLatVec2() const {
|
||||
return Vec2(lon, lat);
|
||||
}
|
||||
|
||||
GeodeticPatch::GeodeticPatch(
|
||||
Scalar centerLat,
|
||||
Scalar centerLon,
|
||||
Scalar halfSizeLat,
|
||||
Scalar halfSizeLon)
|
||||
: _center(Geodetic2(centerLat, centerLon))
|
||||
, _halfSize(Geodetic2(halfSizeLat, halfSizeLon))
|
||||
{
|
||||
|
||||
}
|
||||
bool Geodetic2::operator==(const Geodetic2& other) const {
|
||||
return lat == other.lat && lon == other.lon;
|
||||
}
|
||||
|
||||
GeodeticPatch::GeodeticPatch(
|
||||
const Geodetic2& center,
|
||||
const Geodetic2& halfSize)
|
||||
: _center(center)
|
||||
, _halfSize(halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
Geodetic2 Geodetic2::operator+(const Geodetic2& other) const {
|
||||
return Geodetic2(lat + other.lat, lon + other.lon);
|
||||
}
|
||||
|
||||
GeodeticPatch::GeodeticPatch(const GeodeticPatch& patch)
|
||||
: _center(patch._center)
|
||||
, _halfSize(patch._halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
Geodetic2 Geodetic2::operator-(const Geodetic2& other) const {
|
||||
return Geodetic2(lat - other.lat, lon - other.lon);
|
||||
}
|
||||
|
||||
void GeodeticPatch::setCenter(const Geodetic2& center) {
|
||||
_center = center;
|
||||
}
|
||||
Geodetic2 Geodetic2::operator*(Scalar scalar) const {
|
||||
return Geodetic2(lat * scalar, lon * scalar);
|
||||
}
|
||||
|
||||
void GeodeticPatch::setHalfSize(const Geodetic2& halfSize) {
|
||||
_halfSize = halfSize;
|
||||
}
|
||||
|
||||
Scalar GeodeticPatch::minimalBoundingRadius(const Ellipsoid& ellipsoid) const {
|
||||
// TODO: THIS FUNCTION IS CURRENTLY ERROR PRONE SINCE THE PATCH IS NOW COVERING
|
||||
// A PART OF AN ELLIPSOID AND NOT A SPHERE!MUST CHECK IF THIS FUNCTION IS STILL
|
||||
// VALID.
|
||||
const Geodetic2& cornerNearEquator = _center.lat > 0 ? southWestCorner() : northWestCorner();
|
||||
return glm::length(ellipsoid.geodetic2ToCartesian(_center) - ellipsoid.geodetic2ToCartesian(cornerNearEquator));
|
||||
}
|
||||
/*
|
||||
Scalar GeodeticPatch::unitArea() const {
|
||||
Scalar deltaTheta = 2 * _halfSize.lon;
|
||||
Scalar phiMin = _center.lat - _halfSize.lat;
|
||||
Scalar phiMax = _center.lat + _halfSize.lat;
|
||||
return deltaTheta * (sin(phiMax) - sin(phiMin));
|
||||
}
|
||||
*/
|
||||
const Geodetic2& GeodeticPatch::center() const {
|
||||
return _center;
|
||||
}
|
||||
|
||||
const Geodetic2& GeodeticPatch::halfSize() const {
|
||||
return _halfSize;
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::size() const {
|
||||
return Geodetic2(2 * _halfSize.lat, 2 * _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::northWestCorner() const{
|
||||
return Geodetic2(_center.lat + _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::northEastCorner() const{
|
||||
return Geodetic2(_center.lat + _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::southWestCorner() const{
|
||||
return Geodetic2(_center.lat - _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::southEastCorner() const{
|
||||
return Geodetic2(_center.lat - _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
Geodetic2 Geodetic2::operator/(Scalar scalar) const {
|
||||
return Geodetic2(lat / scalar, lon / scalar);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::clamp(const Geodetic2& p) const {
|
||||
using Ang = Angle<Scalar>;
|
||||
|
||||
// Convert to Angles for normalization
|
||||
Ang centerLat = Ang::fromRadians(_center.lat);
|
||||
Ang centerLon = Ang::fromRadians(_center.lon);
|
||||
Ang pointLat = Ang::fromRadians(p.lat);
|
||||
Ang pointLon = Ang::fromRadians(p.lon);
|
||||
|
||||
// Normalize w.r.t. the center in order for the clamping to done correctly
|
||||
//
|
||||
// Example:
|
||||
// centerLat = 0 deg, halfSize.lat = 10 deg, pointLat = 330 deg
|
||||
// --> Just clamping pointLat would be clamp(330, -10, 10) = 10 // WRONG!
|
||||
// Instead, if we first normalize 330 deg around 0, we get -30 deg
|
||||
// --> clamp(-30, -10, 10) = -10 // CORRECT!
|
||||
pointLat.normalizeAround(centerLat);
|
||||
pointLon.normalizeAround(centerLon);
|
||||
|
||||
// get clamp bounds
|
||||
Geodetic2 max = northEastCorner();
|
||||
Geodetic2 min = southWestCorner();
|
||||
|
||||
return Geodetic2(
|
||||
glm::clamp(pointLat.asRadians(), min.lat, max.lat),
|
||||
glm::clamp(pointLon.asRadians(), min.lon, max.lon)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::closestCorner(const Geodetic2& p) const {
|
||||
using Ang = Angle<Scalar>;
|
||||
|
||||
// LatLon vector from patch center to the point
|
||||
Geodetic2 centerToPoint = p - _center;
|
||||
|
||||
// Normalize the difference angles to be centered around 0.
|
||||
Ang latDiff = Ang::fromRadians(centerToPoint.lat).normalizeAround(Ang::ZERO);
|
||||
Ang lonDiff = Ang::fromRadians(centerToPoint.lon).normalizeAround(Ang::ZERO);
|
||||
|
||||
// If latDiff > 0
|
||||
// --> point p is north of the patch center
|
||||
// --> the closest corner to the point must be a northern one
|
||||
// --> set the corner's latitude coordinate to center.lat + halfSize.lat
|
||||
// else
|
||||
// --> set corner's latidude coordinate to center.lat - halfSize.lat
|
||||
Scalar cornerLat = _center.lat + _halfSize.lat * (latDiff > Ang::ZERO ? 1 : -1);
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// TILE INDEX //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// We then assigned the corner's longitude coordinate in a similar fashion
|
||||
Scalar cornerLon = _center.lon + _halfSize.lon * (lonDiff > Ang::ZERO ? 1 : -1);
|
||||
HashKey GeodeticTileIndex::hashKey() const{
|
||||
return x ^ (y << 16) ^ (level << 21);
|
||||
}
|
||||
|
||||
return Geodetic2(cornerLat, cornerLon);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::closestPoint(const Geodetic2& p) const {
|
||||
// This method finds the closest point on the patch, to the provided
|
||||
// point p. As we are deali ng with latitude-longitude patches, distance in this
|
||||
// context refers to great-circle distance.
|
||||
// (https://en.wikipedia.org/wiki/Great-circle_distance)
|
||||
//
|
||||
// This uses a simple clamping approach to find the closest point on the
|
||||
// patch. A naive castesian clamp is not sufficient for this purpose,
|
||||
// as illustrated with an example below.
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// GEODETICPATCH //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Example: (degrees are used for latidude, longitude)
|
||||
// patchCenter = (0,0), patchHalfSize = (45,45), point = (5, 170)
|
||||
// Note, the point and the patch are on opposite sides of the sphere
|
||||
//
|
||||
// cartesian clamp:
|
||||
// --> clampedPointLat = clamp(5, -45, 45) = 5
|
||||
// --> clampedPointLon = clamp(170, -45, 45) = 45
|
||||
// --> result: (5, 45)
|
||||
// --> closest point is actually (45, 45)
|
||||
// --> The error is significant
|
||||
//
|
||||
// This method simply adds an extra clamp on the latitude in these cases. In the
|
||||
// above example, that would be the following:
|
||||
// --> clampedPointLat = clamp(180 - 5, -45, 45) = 45
|
||||
//
|
||||
// Just doing this actually makes points returned from this methods being the
|
||||
// true closest point, great-circle distance-wise.
|
||||
|
||||
GeodeticPatch::GeodeticPatch(
|
||||
Scalar centerLat,
|
||||
Scalar centerLon,
|
||||
Scalar halfSizeLat,
|
||||
Scalar halfSizeLon)
|
||||
: _center(Geodetic2(centerLat, centerLon))
|
||||
, _halfSize(Geodetic2(halfSizeLat, halfSizeLon))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
using Ang = Angle<Scalar>;
|
||||
GeodeticPatch::GeodeticPatch(
|
||||
const Geodetic2& center,
|
||||
const Geodetic2& halfSize)
|
||||
: _center(center)
|
||||
, _halfSize(halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Convert to Angles for normalization
|
||||
Ang centerLat = Ang::fromRadians(_center.lat);
|
||||
Ang centerLon = Ang::fromRadians(_center.lon);
|
||||
Ang pointLat = Ang::fromRadians(p.lat);
|
||||
Ang pointLon = Ang::fromRadians(p.lon);
|
||||
GeodeticPatch::GeodeticPatch(const GeodeticPatch& patch)
|
||||
: _center(patch._center)
|
||||
, _halfSize(patch._halfSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Normalize point with respect to center. This is done because the point
|
||||
// will later be clamped. See LatLonPatch::clamp(const LatLon&) for explanation
|
||||
pointLat.normalizeAround(centerLat);
|
||||
pointLon.normalizeAround(centerLon);
|
||||
|
||||
// Calculate the longitud difference between center and point. We normalize around
|
||||
// zero because we want the "shortest distance" difference, i.e the difference
|
||||
// should be in the interval [-180 deg, 180 deg]
|
||||
Ang centerToPointLon = (centerLon - pointLon).normalizeAround(Ang::ZERO);
|
||||
GeodeticPatch::GeodeticPatch(const GeodeticTileIndex& tileIndex) {
|
||||
Scalar deltaLat = (2*M_PI) / ((double)(1 << tileIndex.level));
|
||||
Scalar deltaLon = (2*M_PI) / ((double)(1 << tileIndex.level));
|
||||
Geodetic2 nwCorner(M_PI / 2 - deltaLat * tileIndex.y, -M_PI + deltaLon * tileIndex.x);
|
||||
_halfSize = Geodetic2(deltaLat / 2, deltaLon / 2);
|
||||
_center = Geodetic2(nwCorner.lat - _halfSize.lat, nwCorner.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
// Calculate the longitudinal distance to the closest patch edge
|
||||
Ang longitudeDistanceToClosestPatchEdge = centerToPointLon.abs() - Ang::fromRadians(_halfSize.lon);
|
||||
|
||||
// get clamp bounds
|
||||
Geodetic2 max = northEastCorner();
|
||||
Geodetic2 min = southWestCorner();
|
||||
|
||||
// If the longitude distance to the closest patch edge is larger than 90 deg
|
||||
// the latitude will have to be clamped to its closest corner, as explained in
|
||||
// the example above.
|
||||
Scalar clampedLat = longitudeDistanceToClosestPatchEdge > Ang::QUARTER ?
|
||||
clampedLat = glm::clamp((Ang::HALF - pointLat).normalizeAround(centerLat).asRadians(), min.lat, max.lat) :
|
||||
clampedLat = glm::clamp(pointLat.asRadians(), min.lat, max.lat);
|
||||
|
||||
// Longitude is just clamped normally
|
||||
Scalar clampedLon = glm::clamp(pointLon.asRadians(), min.lon, max.lon);
|
||||
void GeodeticPatch::setCenter(const Geodetic2& center) {
|
||||
_center = center;
|
||||
}
|
||||
|
||||
return Geodetic2(clampedLat, clampedLon);
|
||||
}
|
||||
void GeodeticPatch::setHalfSize(const Geodetic2& halfSize) {
|
||||
_halfSize = halfSize;
|
||||
}
|
||||
|
||||
Scalar GeodeticPatch::minimalBoundingRadius(const Ellipsoid& ellipsoid) const {
|
||||
// TODO: THIS FUNCTION IS CURRENTLY ERROR PRONE SINCE THE PATCH IS NOW COVERING
|
||||
// A PART OF AN ELLIPSOID AND NOT A SPHERE!MUST CHECK IF THIS FUNCTION IS STILL
|
||||
// VALID.
|
||||
const Geodetic2& cornerNearEquator = _center.lat > 0 ? southWestCorner() : northWestCorner();
|
||||
return glm::length(ellipsoid.geodetic2ToCartesian(_center) - ellipsoid.geodetic2ToCartesian(cornerNearEquator));
|
||||
}
|
||||
/*
|
||||
Scalar GeodeticPatch::unitArea() const {
|
||||
Scalar deltaTheta = 2 * _halfSize.lon;
|
||||
Scalar phiMin = _center.lat - _halfSize.lat;
|
||||
Scalar phiMax = _center.lat + _halfSize.lat;
|
||||
return deltaTheta * (sin(phiMax) - sin(phiMin));
|
||||
}
|
||||
*/
|
||||
const Geodetic2& GeodeticPatch::center() const {
|
||||
return _center;
|
||||
}
|
||||
|
||||
const Geodetic2& GeodeticPatch::halfSize() const {
|
||||
return _halfSize;
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::size() const {
|
||||
return Geodetic2(2 * _halfSize.lat, 2 * _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::northWestCorner() const{
|
||||
return Geodetic2(_center.lat + _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::northEastCorner() const{
|
||||
return Geodetic2(_center.lat + _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::southWestCorner() const{
|
||||
return Geodetic2(_center.lat - _halfSize.lat, _center.lon - _halfSize.lon);
|
||||
}
|
||||
|
||||
Geodetic2 GeodeticPatch::southEastCorner() const{
|
||||
return Geodetic2(_center.lat - _halfSize.lat, _center.lon + _halfSize.lon);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::clamp(const Geodetic2& p) const {
|
||||
using Ang = Angle<Scalar>;
|
||||
|
||||
// Convert to Angles for normalization
|
||||
Ang centerLat = Ang::fromRadians(_center.lat);
|
||||
Ang centerLon = Ang::fromRadians(_center.lon);
|
||||
Ang pointLat = Ang::fromRadians(p.lat);
|
||||
Ang pointLon = Ang::fromRadians(p.lon);
|
||||
|
||||
// Normalize w.r.t. the center in order for the clamping to done correctly
|
||||
//
|
||||
// Example:
|
||||
// centerLat = 0 deg, halfSize.lat = 10 deg, pointLat = 330 deg
|
||||
// --> Just clamping pointLat would be clamp(330, -10, 10) = 10 // WRONG!
|
||||
// Instead, if we first normalize 330 deg around 0, we get -30 deg
|
||||
// --> clamp(-30, -10, 10) = -10 // CORRECT!
|
||||
pointLat.normalizeAround(centerLat);
|
||||
pointLon.normalizeAround(centerLon);
|
||||
|
||||
// get clamp bounds
|
||||
Geodetic2 max = northEastCorner();
|
||||
Geodetic2 min = southWestCorner();
|
||||
|
||||
return Geodetic2(
|
||||
glm::clamp(pointLat.asRadians(), min.lat, max.lat),
|
||||
glm::clamp(pointLon.asRadians(), min.lon, max.lon)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::closestCorner(const Geodetic2& p) const {
|
||||
using Ang = Angle<Scalar>;
|
||||
|
||||
// LatLon vector from patch center to the point
|
||||
Geodetic2 centerToPoint = p - _center;
|
||||
|
||||
// Normalize the difference angles to be centered around 0.
|
||||
Ang latDiff = Ang::fromRadians(centerToPoint.lat).normalizeAround(Ang::ZERO);
|
||||
Ang lonDiff = Ang::fromRadians(centerToPoint.lon).normalizeAround(Ang::ZERO);
|
||||
|
||||
// If latDiff > 0
|
||||
// --> point p is north of the patch center
|
||||
// --> the closest corner to the point must be a northern one
|
||||
// --> set the corner's latitude coordinate to center.lat + halfSize.lat
|
||||
// else
|
||||
// --> set corner's latidude coordinate to center.lat - halfSize.lat
|
||||
Scalar cornerLat = _center.lat + _halfSize.lat * (latDiff > Ang::ZERO ? 1 : -1);
|
||||
|
||||
// We then assigned the corner's longitude coordinate in a similar fashion
|
||||
Scalar cornerLon = _center.lon + _halfSize.lon * (lonDiff > Ang::ZERO ? 1 : -1);
|
||||
|
||||
return Geodetic2(cornerLat, cornerLon);
|
||||
}
|
||||
|
||||
|
||||
Geodetic2 GeodeticPatch::closestPoint(const Geodetic2& p) const {
|
||||
// This method finds the closest point on the patch, to the provided
|
||||
// point p. As we are deali ng with latitude-longitude patches, distance in this
|
||||
// context refers to great-circle distance.
|
||||
// (https://en.wikipedia.org/wiki/Great-circle_distance)
|
||||
//
|
||||
// This uses a simple clamping approach to find the closest point on the
|
||||
// patch. A naive castesian clamp is not sufficient for this purpose,
|
||||
// as illustrated with an example below.
|
||||
|
||||
// Example: (degrees are used for latidude, longitude)
|
||||
// patchCenter = (0,0), patchHalfSize = (45,45), point = (5, 170)
|
||||
// Note, the point and the patch are on opposite sides of the sphere
|
||||
//
|
||||
// cartesian clamp:
|
||||
// --> clampedPointLat = clamp(5, -45, 45) = 5
|
||||
// --> clampedPointLon = clamp(170, -45, 45) = 45
|
||||
// --> result: (5, 45)
|
||||
// --> closest point is actually (45, 45)
|
||||
// --> The error is significant
|
||||
//
|
||||
// This method simply adds an extra clamp on the latitude in these cases. In the
|
||||
// above example, that would be the following:
|
||||
// --> clampedPointLat = clamp(180 - 5, -45, 45) = 45
|
||||
//
|
||||
// Just doing this actually makes points returned from this methods being the
|
||||
// true closest point, great-circle distance-wise.
|
||||
|
||||
|
||||
using Ang = Angle<Scalar>;
|
||||
|
||||
// Convert to Angles for normalization
|
||||
Ang centerLat = Ang::fromRadians(_center.lat);
|
||||
Ang centerLon = Ang::fromRadians(_center.lon);
|
||||
Ang pointLat = Ang::fromRadians(p.lat);
|
||||
Ang pointLon = Ang::fromRadians(p.lon);
|
||||
|
||||
// Normalize point with respect to center. This is done because the point
|
||||
// will later be clamped. See LatLonPatch::clamp(const LatLon&) for explanation
|
||||
pointLat.normalizeAround(centerLat);
|
||||
pointLon.normalizeAround(centerLon);
|
||||
|
||||
// Calculate the longitud difference between center and point. We normalize around
|
||||
// zero because we want the "shortest distance" difference, i.e the difference
|
||||
// should be in the interval [-180 deg, 180 deg]
|
||||
Ang centerToPointLon = (centerLon - pointLon).normalizeAround(Ang::ZERO);
|
||||
|
||||
// Calculate the longitudinal distance to the closest patch edge
|
||||
Ang longitudeDistanceToClosestPatchEdge = centerToPointLon.abs() - Ang::fromRadians(_halfSize.lon);
|
||||
|
||||
// get clamp bounds
|
||||
Geodetic2 max = northEastCorner();
|
||||
Geodetic2 min = southWestCorner();
|
||||
|
||||
// If the longitude distance to the closest patch edge is larger than 90 deg
|
||||
// the latitude will have to be clamped to its closest corner, as explained in
|
||||
// the example above.
|
||||
Scalar clampedLat = longitudeDistanceToClosestPatchEdge > Ang::QUARTER ?
|
||||
clampedLat = glm::clamp((Ang::HALF - pointLat).normalizeAround(centerLat).asRadians(), min.lat, max.lat) :
|
||||
clampedLat = glm::clamp(pointLat.asRadians(), min.lat, max.lat);
|
||||
|
||||
// Longitude is just clamped normally
|
||||
Scalar clampedLon = glm::clamp(pointLon.asRadians(), min.lon, max.lon);
|
||||
|
||||
return Geodetic2(clampedLat, clampedLon);
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <ostream>
|
||||
|
||||
|
||||
|
||||
// Using double precision
|
||||
typedef double Scalar;
|
||||
typedef glm::dvec2 Vec2;
|
||||
@@ -38,98 +39,135 @@ typedef glm::dvec3 Vec3;
|
||||
|
||||
namespace openspace {
|
||||
|
||||
// Forward declaration
|
||||
class Ellipsoid;
|
||||
// Forward declaration
|
||||
class Ellipsoid;
|
||||
|
||||
struct Geodetic2 {
|
||||
Geodetic2();
|
||||
Geodetic2(Scalar latitude, Scalar longitude);
|
||||
Geodetic2(const Geodetic2& src);
|
||||
|
||||
/*
|
||||
static Geodetic2 fromCartesian(const Vec3& v);
|
||||
Vec3 asUnitCartesian() const;
|
||||
*/
|
||||
Geodetic2();
|
||||
Geodetic2(Scalar latitude, Scalar longitude);
|
||||
Geodetic2(const Geodetic2& src);
|
||||
|
||||
Vec2 toLonLatVec2() const;
|
||||
|
||||
/*
|
||||
static Geodetic2 fromCartesian(const Vec3& v);
|
||||
Vec3 asUnitCartesian() const;
|
||||
*/
|
||||
|
||||
inline bool operator==(const Geodetic2& other) const;
|
||||
inline bool operator!=(const Geodetic2& other) const { return !(*this == (other)); }
|
||||
|
||||
inline Geodetic2 operator+(const Geodetic2& other) const;
|
||||
inline Geodetic2 operator-(const Geodetic2& other) const;
|
||||
inline Geodetic2 operator*(Scalar scalar) const;
|
||||
inline Geodetic2 operator/(Scalar scalar) const;
|
||||
|
||||
Scalar lat;
|
||||
Scalar lon;
|
||||
Vec2 toLonLatVec2() const;
|
||||
|
||||
bool operator==(const Geodetic2& other) const;
|
||||
bool operator!=(const Geodetic2& other) const { return !(*this == (other)); }
|
||||
|
||||
Geodetic2 operator+(const Geodetic2& other) const;
|
||||
Geodetic2 operator-(const Geodetic2& other) const;
|
||||
Geodetic2 operator*(Scalar scalar) const;
|
||||
Geodetic2 operator/(Scalar scalar) const;
|
||||
|
||||
Scalar lat;
|
||||
Scalar lon;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Geodetic3 {
|
||||
Geodetic2 geodetic2;
|
||||
Scalar height;
|
||||
Geodetic2 geodetic2;
|
||||
Scalar height;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// TILE INDEX //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
using HashKey = unsigned long;
|
||||
|
||||
|
||||
struct GeodeticTileIndex {
|
||||
|
||||
|
||||
int x, y, level;
|
||||
|
||||
HashKey hashKey() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// GEODETICPATCH //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
class GeodeticPatch {
|
||||
public:
|
||||
GeodeticPatch(
|
||||
Scalar centerLat,
|
||||
Scalar centerLon,
|
||||
Scalar halfSizeLat,
|
||||
Scalar halfSizeLon);
|
||||
GeodeticPatch(
|
||||
const Geodetic2& center,
|
||||
const Geodetic2& halfSize);
|
||||
GeodeticPatch(const GeodeticPatch& patch);
|
||||
GeodeticPatch(
|
||||
Scalar centerLat,
|
||||
Scalar centerLon,
|
||||
Scalar halfSizeLat,
|
||||
Scalar halfSizeLon);
|
||||
|
||||
void setCenter(const Geodetic2&);
|
||||
void setHalfSize(const Geodetic2&);
|
||||
GeodeticPatch(
|
||||
const Geodetic2& center,
|
||||
const Geodetic2& halfSize);
|
||||
|
||||
/**
|
||||
Returns the minimal bounding radius that together with the LatLonPatch's
|
||||
center point represents a sphere in which the patch is completely contained.
|
||||
|
||||
TODO : THIS FUNCTION IS CURRENTLY ERROR PRONE SINCE THE PATCH IS NOW COVERING
|
||||
A PART OF AN ELLIPSOID AND NOT A SPHERE! MUST CHECK IF THIS FUNCTION IS STILL
|
||||
VALID.
|
||||
*/
|
||||
Scalar minimalBoundingRadius(const Ellipsoid& ellipsoid) const;
|
||||
GeodeticPatch(const GeodeticPatch& patch);
|
||||
|
||||
/**
|
||||
Returns the area of the patch with unit radius
|
||||
*/
|
||||
// Scalar unitArea() const;
|
||||
GeodeticPatch::GeodeticPatch(const GeodeticTileIndex& tileIndex);
|
||||
|
||||
|
||||
Geodetic2 northWestCorner() const;
|
||||
Geodetic2 northEastCorner() const;
|
||||
Geodetic2 southWestCorner() const;
|
||||
Geodetic2 southEastCorner() const;
|
||||
void setCenter(const Geodetic2&);
|
||||
void setHalfSize(const Geodetic2&);
|
||||
|
||||
/**
|
||||
* Clamps a point to the patch region
|
||||
*/
|
||||
Geodetic2 clamp(const Geodetic2& p) const;
|
||||
/**
|
||||
Returns the minimal bounding radius that together with the LatLonPatch's
|
||||
center point represents a sphere in which the patch is completely contained.
|
||||
|
||||
TODO : THIS FUNCTION IS CURRENTLY ERROR PRONE SINCE THE PATCH IS NOW COVERING
|
||||
A PART OF AN ELLIPSOID AND NOT A SPHERE! MUST CHECK IF THIS FUNCTION IS STILL
|
||||
VALID.
|
||||
*/
|
||||
Scalar minimalBoundingRadius(const Ellipsoid& ellipsoid) const;
|
||||
|
||||
/**
|
||||
* Returns the corner of the patch that is closest to the given point p
|
||||
*/
|
||||
Geodetic2 closestCorner(const Geodetic2& p) const;
|
||||
/**
|
||||
Returns the area of the patch with unit radius
|
||||
*/
|
||||
// Scalar unitArea() const;
|
||||
|
||||
/**
|
||||
* Returns a point on the patch that minimizes the great-circle distance to
|
||||
* the given point p.
|
||||
*/
|
||||
Geodetic2 closestPoint(const Geodetic2& p) const;
|
||||
|
||||
|
||||
const Geodetic2& center() const;
|
||||
const Geodetic2& halfSize() const;
|
||||
Geodetic2 size() const;
|
||||
Geodetic2 northWestCorner() const;
|
||||
Geodetic2 northEastCorner() const;
|
||||
Geodetic2 southWestCorner() const;
|
||||
Geodetic2 southEastCorner() const;
|
||||
|
||||
/**
|
||||
* Clamps a point to the patch region
|
||||
*/
|
||||
Geodetic2 clamp(const Geodetic2& p) const;
|
||||
|
||||
/**
|
||||
* Returns the corner of the patch that is closest to the given point p
|
||||
*/
|
||||
Geodetic2 closestCorner(const Geodetic2& p) const;
|
||||
|
||||
/**
|
||||
* Returns a point on the patch that minimizes the great-circle distance to
|
||||
* the given point p.
|
||||
*/
|
||||
Geodetic2 closestPoint(const Geodetic2& p) const;
|
||||
|
||||
|
||||
const Geodetic2& center() const;
|
||||
const Geodetic2& halfSize() const;
|
||||
Geodetic2 size() const;
|
||||
|
||||
private:
|
||||
Geodetic2 _center;
|
||||
Geodetic2 _halfSize;
|
||||
Geodetic2 _center;
|
||||
Geodetic2 _halfSize;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -125,6 +125,7 @@ void ChunkNode::internalRender(const RenderData& data, ChunkIndex& traverseData)
|
||||
LatLonPatchRenderer& patchRenderer = _owner.getPatchRenderer();
|
||||
|
||||
patchRenderer.renderPatch(_patch, data, _owner.ellipsoid(), ti);
|
||||
//patchRenderer.renderPatch(_patch, data, _owner.ellipsoid());
|
||||
ChunkNode::renderedPatches++;
|
||||
}
|
||||
}
|
||||
@@ -208,15 +209,17 @@ void ChunkNode::split(int depth) {
|
||||
Geodetic2 qs = Geodetic2(0.5 * hs.lat, 0.5 * hs.lon);
|
||||
|
||||
// Subdivide bounds
|
||||
|
||||
GeodeticPatch nwBounds = GeodeticPatch(Geodetic2(c.lat + qs.lat, c.lon - qs.lon), qs);
|
||||
GeodeticPatch neBounds = GeodeticPatch(Geodetic2(c.lat - qs.lat, c.lon - qs.lon), qs);
|
||||
GeodeticPatch swBounds = GeodeticPatch(Geodetic2(c.lat + qs.lat, c.lon + qs.lon), qs);
|
||||
GeodeticPatch neBounds = GeodeticPatch(Geodetic2(c.lat + qs.lat, c.lon + qs.lon), qs);
|
||||
GeodeticPatch swBounds = GeodeticPatch(Geodetic2(c.lat - qs.lat, c.lon - qs.lon), qs);
|
||||
GeodeticPatch seBounds = GeodeticPatch(Geodetic2(c.lat - qs.lat, c.lon + qs.lon), qs);
|
||||
|
||||
// Create new chunk nodes
|
||||
|
||||
_children[Quad::NORTH_WEST] = std::unique_ptr<ChunkNode>(new ChunkNode(_owner, nwBounds, this));
|
||||
_children[Quad::NORTH_EAST] = std::unique_ptr<ChunkNode>(new ChunkNode(_owner, neBounds, this));
|
||||
_children[Quad::SOUTH_WEST] = std::unique_ptr<ChunkNode>(new ChunkNode(_owner, swBounds, this));
|
||||
_children[Quad::NORTH_EAST] = std::unique_ptr<ChunkNode>(new ChunkNode(_owner, neBounds, this));
|
||||
_children[Quad::SOUTH_EAST] = std::unique_ptr<ChunkNode>(new ChunkNode(_owner, seBounds, this));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ enum Quad {
|
||||
struct ChunkIndex {
|
||||
int x, y, level;
|
||||
|
||||
|
||||
// TODO : NOT SURE WHAT IS RIGHT BUT I SWITCHED THE TWO MIDDLE INDICES FOR IT TO WORK
|
||||
|
||||
std::vector<ChunkIndex> childIndices() const {
|
||||
return {
|
||||
{ 2 * x + 0, 2 * y + 0, level + 1 },
|
||||
@@ -89,28 +92,28 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void internalRender(const RenderData& data, ChunkIndex&);
|
||||
bool internalUpdateChunkTree(const RenderData& data, ChunkIndex& traverseData);
|
||||
void internalRender(const RenderData& data, ChunkIndex&);
|
||||
bool internalUpdateChunkTree(const RenderData& data, ChunkIndex& traverseData);
|
||||
|
||||
/**
|
||||
Uses horizon culling, frustum culling and distance to camera to determine a
|
||||
desired level.
|
||||
In the current implementation of the horizon culling and the distance to the
|
||||
camera, the closer the ellipsoid is to a
|
||||
sphere, the better this will make the splitting. Using the minimum radius to
|
||||
be safe. This means that if the ellipsoid has high difference between radii,
|
||||
splitting might accur even though it is not needed.
|
||||
*/
|
||||
int calculateDesiredLevelAndUpdateIsVisible(
|
||||
const RenderData& data,
|
||||
const ChunkIndex& traverseData);
|
||||
|
||||
|
||||
ChunkNode* _parent;
|
||||
std::unique_ptr<ChunkNode> _children[4];
|
||||
/**
|
||||
Uses horizon culling, frustum culling and distance to camera to determine a
|
||||
desired level.
|
||||
In the current implementation of the horizon culling and the distance to the
|
||||
camera, the closer the ellipsoid is to a
|
||||
sphere, the better this will make the splitting. Using the minimum radius to
|
||||
be safe. This means that if the ellipsoid has high difference between radii,
|
||||
splitting might accur even though it is not needed.
|
||||
*/
|
||||
int calculateDesiredLevelAndUpdateIsVisible(
|
||||
const RenderData& data,
|
||||
const ChunkIndex& traverseData);
|
||||
|
||||
|
||||
ChunkNode* _parent;
|
||||
std::unique_ptr<ChunkNode> _children[4];
|
||||
ChunkLodGlobe& _owner;
|
||||
GeodeticPatch _patch;
|
||||
bool _isVisible;
|
||||
bool _isVisible;
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -24,116 +24,159 @@
|
||||
|
||||
|
||||
#include <modules/globebrowsing/other/gdaldataconverter.h>
|
||||
#include <modules/globebrowsing/other/tileprovider.h>
|
||||
#include <modules/globebrowsing/geodetics/angle.h>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "GdalDataConverter";
|
||||
const std::string _loggerCat = "GdalDataConverter";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
GdalDataConverter::GdalDataConverter()
|
||||
{
|
||||
GdalDataConverter::GdalDataConverter()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GdalDataConverter::~GdalDataConverter()
|
||||
{
|
||||
GdalDataConverter::~GdalDataConverter()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> GdalDataConverter::convertToOpenGLTexture(
|
||||
GDALDataset* dataSet,
|
||||
const GeodeticTileIndex& tileIndex,
|
||||
int GLType)
|
||||
{
|
||||
int nRasters = dataSet->GetRasterCount();
|
||||
std::shared_ptr<Texture> GdalDataConverter::convertToOpenGLTexture(
|
||||
GDALDataset* dataSet,
|
||||
const GeodeticTileIndex& tileIndex,
|
||||
int GLType)
|
||||
{
|
||||
int nRasters = dataSet->GetRasterCount();
|
||||
|
||||
ghoul_assert(nRasters > 0, "Bad dataset. Contains no rasterband.");
|
||||
ghoul_assert(nRasters > 0, "Bad dataset. Contains no rasterband.");
|
||||
|
||||
GDALRasterBand* firstBand = dataSet->GetRasterBand(1);
|
||||
GDALRasterBand* firstBand = dataSet->GetRasterBand(1);
|
||||
|
||||
// Level = overviewCount - overview
|
||||
int overviewCount = firstBand->GetOverviewCount();
|
||||
int overview = overviewCount - tileIndex.level - 1;
|
||||
// Level = overviewCount - overview
|
||||
int overviewCount = firstBand->GetOverviewCount();
|
||||
int overview = overviewCount - tileIndex.level - 1;
|
||||
|
||||
// The output texture will have this size
|
||||
int xSizelevel0 = firstBand->GetOverview(overviewCount - 1)->GetXSize();
|
||||
int ySizelevel0 = firstBand->GetOverview(overviewCount - 1)->GetYSize();
|
||||
// The output texture will have this size
|
||||
int xSizelevel0 = firstBand->GetOverview(overviewCount - 1)->GetXSize();
|
||||
int ySizelevel0 = firstBand->GetOverview(overviewCount - 1)->GetYSize();
|
||||
|
||||
// Create all the raster bands (Commonly one for each channel: red, green, blue)
|
||||
std::vector<GDALRasterBand*> rasterBands;
|
||||
for (size_t i = 0; i < nRasters; i++)
|
||||
{
|
||||
rasterBands.push_back(dataSet->GetRasterBand(i + 1)->GetOverview(overview));
|
||||
}
|
||||
// The data that the texture should read
|
||||
GLubyte* imageData = new GLubyte[xSizelevel0 * ySizelevel0 * nRasters];
|
||||
|
||||
// Read the data
|
||||
for (size_t i = 0; i < nRasters; i++)
|
||||
{
|
||||
|
||||
int xBeginRead = tileIndex.x * pow(2, tileIndex.level) * xSizelevel0;
|
||||
int yBeginRead = tileIndex.y * pow(2, tileIndex.level) * ySizelevel0;
|
||||
rasterBands[i]->RasterIO(
|
||||
GF_Read,
|
||||
xBeginRead, // Begin read x
|
||||
yBeginRead, // Begin read y
|
||||
xSizelevel0, // width to read x
|
||||
ySizelevel0, // width to read y
|
||||
imageData + i, // Where to put data
|
||||
xSizelevel0, // width to read x in destination
|
||||
ySizelevel0, // width to read y in destination
|
||||
GDT_Byte, // Type
|
||||
sizeof(GLubyte) * nRasters, // Pixel spacing
|
||||
0); // Line spacing
|
||||
}
|
||||
|
||||
GdalDataConverter::TextureFormat textrureFormat =
|
||||
getTextureFormatFromRasterCount(nRasters);
|
||||
// The data that the texture should read
|
||||
GLubyte* imageData = new GLubyte[xSizelevel0 * ySizelevel0 * nRasters];
|
||||
|
||||
// The texture should take ownership of the data
|
||||
std::shared_ptr<Texture> texture = std::shared_ptr<Texture>(new Texture(
|
||||
static_cast<void*>(imageData),
|
||||
glm::uvec3(xSizelevel0, ySizelevel0, 1),
|
||||
textrureFormat.ghoulFormat,
|
||||
textrureFormat.glFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
Texture::FilterMode::Linear,
|
||||
Texture::WrappingMode::Repeat));
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < nRasters; i++)
|
||||
{
|
||||
GDALRasterBand* rasterBand = dataSet->GetRasterBand(i + 1)->GetOverview(overview);
|
||||
|
||||
int xBeginRead = tileIndex.x * pow(2, tileIndex.level) * xSizelevel0;
|
||||
int yBeginRead = tileIndex.y * pow(2, tileIndex.level) * ySizelevel0;
|
||||
rasterBand->RasterIO(
|
||||
GF_Read,
|
||||
xBeginRead, // Begin read x
|
||||
yBeginRead, // Begin read y
|
||||
xSizelevel0, // width to read x
|
||||
ySizelevel0, // width to read y
|
||||
imageData + i, // Where to put data
|
||||
xSizelevel0, // width to read x in destination
|
||||
ySizelevel0, // width to read y in destination
|
||||
GDT_Byte, // Type
|
||||
sizeof(GLubyte) * nRasters, // Pixel spacing
|
||||
0); // Line spacing
|
||||
}
|
||||
|
||||
// Do not free imageData since the texture now has ownership of it
|
||||
return texture;
|
||||
}
|
||||
GdalDataConverter::TextureFormat textrureFormat =
|
||||
getTextureFormatFromRasterCount(nRasters);
|
||||
|
||||
GdalDataConverter::TextureFormat GdalDataConverter::getTextureFormatFromRasterCount(
|
||||
int rasterCount)
|
||||
{
|
||||
TextureFormat format;
|
||||
|
||||
switch (rasterCount)
|
||||
{
|
||||
case 1:
|
||||
format.ghoulFormat = Texture::Format::Red;
|
||||
format.glFormat = GL_RED;
|
||||
break;
|
||||
case 2:
|
||||
format.ghoulFormat = Texture::Format::RG;
|
||||
format.glFormat = GL_RG;
|
||||
break;
|
||||
case 3:
|
||||
format.ghoulFormat = Texture::Format::RGB;
|
||||
format.glFormat = GL_RGB;
|
||||
break;
|
||||
case 4:
|
||||
format.ghoulFormat = Texture::Format::RGBA;
|
||||
format.glFormat = GL_RGBA;
|
||||
break;
|
||||
default:
|
||||
Texture* tex = new Texture(
|
||||
static_cast<void*>(imageData),
|
||||
glm::uvec3(xSizelevel0, ySizelevel0, 1),
|
||||
textrureFormat.ghoulFormat,
|
||||
textrureFormat.glFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
Texture::FilterMode::Linear,
|
||||
Texture::WrappingMode::Repeat);
|
||||
|
||||
break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
// The texture should take ownership of the data
|
||||
std::shared_ptr<Texture> texture = std::shared_ptr<Texture>(tex);
|
||||
|
||||
|
||||
// Do not free imageData since the texture now has ownership of it
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
glm::uvec2 GdalDataConverter::geodeticToPixel(GDALDataset* dataSet, const Geodetic2& geo) {
|
||||
double padfTransform[6];
|
||||
CPLErr err = dataSet->GetGeoTransform(padfTransform);
|
||||
|
||||
ghoul_assert(err != CE_Failure, "Failed to get transform");
|
||||
|
||||
Scalar Y = Angle<Scalar>::fromRadians(geo.lat).asDegrees();
|
||||
Scalar X = Angle<Scalar>::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];
|
||||
|
||||
return glm::uvec2(P, L);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GdalDataConverter::TextureFormat GdalDataConverter::getTextureFormatFromRasterCount(
|
||||
int rasterCount)
|
||||
{
|
||||
TextureFormat format;
|
||||
|
||||
switch (rasterCount)
|
||||
{
|
||||
case 1:
|
||||
format.ghoulFormat = Texture::Format::Red;
|
||||
format.glFormat = GL_RED;
|
||||
break;
|
||||
case 2:
|
||||
format.ghoulFormat = Texture::Format::RG;
|
||||
format.glFormat = GL_RG;
|
||||
break;
|
||||
case 3:
|
||||
format.ghoulFormat = Texture::Format::RGB;
|
||||
format.glFormat = GL_RGB;
|
||||
break;
|
||||
case 4:
|
||||
format.ghoulFormat = Texture::Format::RGBA;
|
||||
format.glFormat = GL_RGBA;
|
||||
break;
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -25,38 +25,46 @@
|
||||
#ifndef __GDALDATACONVERTER_H__
|
||||
#define __GDALDATACONVERTER_H__
|
||||
|
||||
#include <modules/globebrowsing/other/tileprovider.h>
|
||||
//#include <modules/globebrowsing/other/tileprovider.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/opengl/texture.h>
|
||||
|
||||
#include <modules/globebrowsing/geodetics/geodetic2.h>
|
||||
|
||||
#include "gdal_priv.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace openspace {
|
||||
|
||||
using namespace ghoul::opengl;
|
||||
using namespace ghoul::opengl;
|
||||
|
||||
class GdalDataConverter
|
||||
{
|
||||
public:
|
||||
GdalDataConverter();
|
||||
~GdalDataConverter();
|
||||
// forward declaration
|
||||
class GeodeticTileIndex;
|
||||
//class Geodetic2;
|
||||
|
||||
std::shared_ptr<Texture> convertToOpenGLTexture(
|
||||
GDALDataset* dataSet,
|
||||
const GeodeticTileIndex& tileIndex,
|
||||
int GLType);
|
||||
class GdalDataConverter
|
||||
{
|
||||
public:
|
||||
GdalDataConverter();
|
||||
~GdalDataConverter();
|
||||
|
||||
private:
|
||||
struct TextureFormat
|
||||
{
|
||||
Texture::Format ghoulFormat;
|
||||
GLuint glFormat;
|
||||
};
|
||||
TextureFormat getTextureFormatFromRasterCount(int rasterCount);
|
||||
};
|
||||
std::shared_ptr<Texture> convertToOpenGLTexture(
|
||||
GDALDataset* dataSet,
|
||||
const GeodeticTileIndex& tileIndex,
|
||||
int GLType);
|
||||
|
||||
struct TextureFormat
|
||||
{
|
||||
Texture::Format ghoulFormat;
|
||||
GLuint glFormat;
|
||||
};
|
||||
|
||||
TextureFormat getTextureFormatFromRasterCount(int rasterCount);
|
||||
|
||||
glm::uvec2 geodeticToPixel(GDALDataset* dataSet, const Geodetic2& geo);
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
|
||||
@@ -39,144 +39,146 @@
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "TextureTileSet";
|
||||
const std::string _loggerCat = "TextureTileSet";
|
||||
}
|
||||
|
||||
namespace openspace {
|
||||
|
||||
TextureTileSet::TextureTileSet(
|
||||
Geodetic2 sizeLevel0,
|
||||
Geodetic2 offsetLevel0,
|
||||
int depth)
|
||||
: _sizeLevel0(sizeLevel0)
|
||||
, _offsetLevel0(offsetLevel0)
|
||||
, _depth(depth)
|
||||
{
|
||||
|
||||
TextureTileSet::TextureTileSet(
|
||||
Geodetic2 sizeLevel0,
|
||||
Geodetic2 offsetLevel0,
|
||||
int depth)
|
||||
: _sizeLevel0(sizeLevel0)
|
||||
, _offsetLevel0(offsetLevel0)
|
||||
, _depth(depth)
|
||||
{
|
||||
|
||||
|
||||
// Read using GDAL
|
||||
// Read using GDAL
|
||||
|
||||
std::string testFile = absPath("map_service_configs/TERRA_CR_B143_2016-04-12.wms");
|
||||
//std::string testFile = absPath("map_service_configs/TERRA_CR_B143_2016-04-12.wms");
|
||||
//std::string testFile = absPath("map_service_configs/TERRAIN.wms");
|
||||
std::string testFile = absPath("textures/earth_bluemarble.jpg");
|
||||
|
||||
|
||||
GDALDataset *poDataset;
|
||||
GDALAllRegister();
|
||||
poDataset = (GDALDataset *)GDALOpen(testFile.c_str(), GA_ReadOnly);
|
||||
assert(poDataset != NULL);
|
||||
GdalDataConverter conv;
|
||||
|
||||
GDALDataset *poDataset;
|
||||
GDALAllRegister();
|
||||
poDataset = (GDALDataset *)GDALOpen(testFile.c_str(), GA_ReadOnly);
|
||||
assert(poDataset != nullptr);
|
||||
GdalDataConverter conv;
|
||||
|
||||
GeodeticTileIndex ti;
|
||||
ti.x = 0;
|
||||
ti.y = 0;
|
||||
ti.level = 0;
|
||||
_testTexture = conv.convertToOpenGLTexture(poDataset, ti, GL_UNSIGNED_BYTE);
|
||||
GeodeticTileIndex ti;
|
||||
ti.x = 0;
|
||||
ti.y = 0;
|
||||
ti.level = 0;
|
||||
_testTexture = conv.convertToOpenGLTexture(poDataset, ti, GL_UNSIGNED_BYTE);
|
||||
|
||||
_testTexture->uploadTexture();
|
||||
_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
|
||||
/*
|
||||
|
||||
// Set e texture to test
|
||||
std::string fileName = "textures/earth_bluemarble.jpg";
|
||||
//std::string fileName = "../../../build/tiles/tile5_8_12.png";
|
||||
//std::string fileName = "tile5_8_12.png";
|
||||
_testTexture = std::move(ghoul::io::TextureReader::ref().loadTexture(absPath(fileName)));
|
||||
|
||||
if (_testTexture) {
|
||||
LDEBUG("Loaded texture from '" << "textures/earth_bluemarble.jpg" << "'");
|
||||
_testTexture->uploadTexture();
|
||||
_testTexture->uploadTexture();
|
||||
_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
|
||||
/*
|
||||
|
||||
// Set e texture to test
|
||||
std::string fileName = "textures/earth_bluemarble.jpg";
|
||||
//std::string fileName = "../../../build/tiles/tile5_8_12.png";
|
||||
//std::string fileName = "tile5_8_12.png";
|
||||
_testTexture = std::move(ghoul::io::TextureReader::ref().loadTexture(absPath(fileName)));
|
||||
|
||||
if (_testTexture) {
|
||||
LDEBUG("Loaded texture from '" << "textures/earth_bluemarble.jpg" << "'");
|
||||
_testTexture->uploadTexture();
|
||||
|
||||
// Textures of planets looks much smoother with AnisotropicMipMap rather than linear
|
||||
// TODO: AnisotropicMipMap crashes on ATI cards ---abock
|
||||
//_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
|
||||
_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
}
|
||||
*/
|
||||
}
|
||||
// Textures of planets looks much smoother with AnisotropicMipMap rather than linear
|
||||
// TODO: AnisotropicMipMap crashes on ATI cards ---abock
|
||||
//_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::AnisotropicMipMap);
|
||||
_testTexture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
TextureTileSet::~TextureTileSet(){
|
||||
TextureTileSet::~TextureTileSet(){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GeodeticTileIndex TextureTileSet::getTileIndex(const GeodeticPatch& patch) {
|
||||
// Calculate the level of the index depanding on the size of the incoming patch.
|
||||
// The level is as big as possible (as far down as possible) but it can't be
|
||||
// too big since at maximum four tiles should be used to cover a patch
|
||||
int level = log2(static_cast<int>(glm::max(
|
||||
_sizeLevel0.lat / (patch.size().lat),
|
||||
_sizeLevel0.lon / (patch.size().lon))));
|
||||
|
||||
// If the depth is not big enough, the level must be clamped.
|
||||
level = glm::min(level, _depth);
|
||||
|
||||
// Calculate the index in x y where the tile should be positioned
|
||||
Vec2 tileSize = _sizeLevel0.toLonLatVec2() / pow(2, level);
|
||||
Vec2 nw = patch.northWestCorner().toLonLatVec2();
|
||||
Vec2 offset = _offsetLevel0.toLonLatVec2();
|
||||
glm::ivec2 tileIndexXY = (nw - offset) / tileSize;
|
||||
GeodeticTileIndex TextureTileSet::getTileIndex(const GeodeticPatch& patch) {
|
||||
// Calculate the level of the index depanding on the size of the incoming patch.
|
||||
// The level is as big as possible (as far down as possible) but it can't be
|
||||
// too big since at maximum four tiles should be used to cover a patch
|
||||
int level = log2(static_cast<int>(glm::max(
|
||||
_sizeLevel0.lat / (patch.size().lat),
|
||||
_sizeLevel0.lon / (patch.size().lon))));
|
||||
|
||||
// If the depth is not big enough, the level must be clamped.
|
||||
level = glm::min(level, _depth);
|
||||
|
||||
// Calculate the index in x y where the tile should be positioned
|
||||
Vec2 tileSize = _sizeLevel0.toLonLatVec2() / pow(2, level);
|
||||
Vec2 nw = patch.northWestCorner().toLonLatVec2();
|
||||
Vec2 offset = _offsetLevel0.toLonLatVec2();
|
||||
glm::ivec2 tileIndexXY = (nw - offset) / tileSize;
|
||||
|
||||
// Flip y since indices increase from top to bottom
|
||||
tileIndexXY.y *= -1;
|
||||
// Flip y since indices increase from top to bottom
|
||||
tileIndexXY.y *= -1;
|
||||
|
||||
// Create the tileindex
|
||||
GeodeticTileIndex tileIndex = { tileIndexXY.x, tileIndexXY.y, level };
|
||||
return tileIndex;
|
||||
}
|
||||
// Create the tileindex
|
||||
GeodeticTileIndex tileIndex = { tileIndexXY.x, tileIndexXY.y, level };
|
||||
return tileIndex;
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> TextureTileSet::getTile(const GeodeticPatch& patch) {
|
||||
return getTile(getTileIndex(patch));
|
||||
}
|
||||
std::shared_ptr<Texture> TextureTileSet::getTile(const GeodeticPatch& patch) {
|
||||
return getTile(getTileIndex(patch));
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> TextureTileSet::getTile(const GeodeticTileIndex& tileIndex) {
|
||||
return _testTexture;
|
||||
}
|
||||
std::shared_ptr<Texture> TextureTileSet::getTile(const GeodeticTileIndex& tileIndex) {
|
||||
return _testTexture;
|
||||
}
|
||||
|
||||
GeodeticPatch TextureTileSet::getTilePositionAndScale(
|
||||
const GeodeticTileIndex& tileIndex) {
|
||||
Geodetic2 tileSize = Geodetic2(
|
||||
_sizeLevel0.lat / pow(2, tileIndex.level),
|
||||
_sizeLevel0.lon / pow(2, tileIndex.level));
|
||||
Geodetic2 northWest = Geodetic2(
|
||||
_offsetLevel0.lat + tileIndex.y * tileSize.lat,
|
||||
_offsetLevel0.lon + tileIndex.x * tileSize.lon);
|
||||
|
||||
return GeodeticPatch(
|
||||
Geodetic2(northWest.lat - tileSize.lat / 2, northWest.lon + tileSize.lon / 2),
|
||||
Geodetic2(tileSize.lat / 2, tileSize.lon / 2));
|
||||
}
|
||||
GeodeticPatch TextureTileSet::getTilePositionAndScale(
|
||||
const GeodeticTileIndex& tileIndex) {
|
||||
Geodetic2 tileSize = Geodetic2(
|
||||
_sizeLevel0.lat / pow(2, tileIndex.level),
|
||||
_sizeLevel0.lon / pow(2, tileIndex.level));
|
||||
Geodetic2 northWest = Geodetic2(
|
||||
_offsetLevel0.lat + tileIndex.y * tileSize.lat,
|
||||
_offsetLevel0.lon + tileIndex.x * tileSize.lon);
|
||||
|
||||
return GeodeticPatch(
|
||||
Geodetic2(northWest.lat - tileSize.lat / 2, northWest.lon + tileSize.lon / 2),
|
||||
Geodetic2(tileSize.lat / 2, tileSize.lon / 2));
|
||||
}
|
||||
|
||||
glm::mat3 TextureTileSet::getUvTransformationPatchToTile(
|
||||
GeodeticPatch patch,
|
||||
const GeodeticTileIndex& tileIndex)
|
||||
{
|
||||
GeodeticPatch tile = getTilePositionAndScale(tileIndex);
|
||||
return getUvTransformationPatchToTile(patch, tile);
|
||||
}
|
||||
glm::mat3 TextureTileSet::getUvTransformationPatchToTile(
|
||||
GeodeticPatch patch,
|
||||
const GeodeticTileIndex& tileIndex)
|
||||
{
|
||||
GeodeticPatch tile = getTilePositionAndScale(tileIndex);
|
||||
return getUvTransformationPatchToTile(patch, tile);
|
||||
}
|
||||
|
||||
glm::mat3 TextureTileSet::getUvTransformationPatchToTile(
|
||||
GeodeticPatch patch,
|
||||
GeodeticPatch tile)
|
||||
{
|
||||
Vec2 posDiff =
|
||||
patch.southWestCorner().toLonLatVec2() -
|
||||
tile.southWestCorner().toLonLatVec2();
|
||||
glm::mat3 TextureTileSet::getUvTransformationPatchToTile(
|
||||
GeodeticPatch patch,
|
||||
GeodeticPatch tile)
|
||||
{
|
||||
Vec2 posDiff =
|
||||
patch.southWestCorner().toLonLatVec2() -
|
||||
tile.southWestCorner().toLonLatVec2();
|
||||
|
||||
glm::mat3 invTileScale = glm::mat3(
|
||||
{ 1 / (tile.halfSize().lon * 2), 0, 0,
|
||||
0, 1 / (tile.halfSize().lat * 2), 0,
|
||||
0, 0, 1 });
|
||||
glm::mat3 invTileScale = glm::mat3(
|
||||
{ 1 / (tile.size().lon), 0, 0,
|
||||
0, 1 / (tile.size().lat), 0,
|
||||
0, 0, 1 });
|
||||
|
||||
glm::mat3 globalTranslation = glm::mat3(
|
||||
{ 1, 0, 0,
|
||||
0, 1, 0,
|
||||
posDiff.x, posDiff.y, 1 });
|
||||
glm::mat3 globalTranslation = glm::mat3(
|
||||
{ 1, 0, 0,
|
||||
0, 1, 0,
|
||||
posDiff.x, posDiff.y, 1 });
|
||||
|
||||
glm::mat3 patchScale = glm::mat3(
|
||||
{ (patch.halfSize().lon * 2), 0, 0,
|
||||
0, (patch.halfSize().lat * 2), 0,
|
||||
0, 0, 1 });
|
||||
glm::mat3 patchScale = glm::mat3(
|
||||
{ (patch.halfSize().lon * 2), 0, 0,
|
||||
0, (patch.halfSize().lat * 2), 0,
|
||||
0, 0, 1 });
|
||||
|
||||
return invTileScale * globalTranslation * patchScale;
|
||||
}
|
||||
return invTileScale * globalTranslation * patchScale;
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#include <modules/globebrowsing/geodetics/geodetic2.h>
|
||||
#include <modules/globebrowsing/other/tileprovider.h>
|
||||
|
||||
#include <openspace/engine/downloadmanager.h>
|
||||
@@ -37,69 +37,43 @@
|
||||
|
||||
|
||||
namespace {
|
||||
const std::string _loggerCat = "TwmsTileProvider";
|
||||
const std::string _loggerCat = "TileProvider";
|
||||
}
|
||||
|
||||
|
||||
namespace openspace {
|
||||
|
||||
bool TileProvider::hasInitializedGDAL = false;
|
||||
|
||||
TileProvider::TileProvider(int tileCacheSize)
|
||||
: _tileCache(tileCacheSize) // setting cache size
|
||||
TileProvider::TileProvider(const std::string& filePath, int tileCacheSize)
|
||||
: _filePath(filePath)
|
||||
, _tileCache(tileCacheSize) // setting cache size
|
||||
{
|
||||
int downloadApplicationVersion = 1;
|
||||
if (!DownloadManager::isInitialized()) {
|
||||
DownloadManager::initialize("../tmp_openspace_downloads/", downloadApplicationVersion);
|
||||
}
|
||||
|
||||
if (!hasInitializedGDAL) {
|
||||
GDALAllRegister();
|
||||
hasInitializedGDAL = true;
|
||||
}
|
||||
|
||||
std::string absFilePath = absPath(filePath);
|
||||
_gdalDataSet = (GDALDataset *)GDALOpen(absFilePath.c_str(), GA_ReadOnly);
|
||||
//auto desc = _gdalDataSet->GetDescription();
|
||||
|
||||
ghoul_assert(_gdalDataSet != nullptr, "Failed to load dataset: " << filePath);
|
||||
|
||||
}
|
||||
|
||||
TileProvider::~TileProvider(){
|
||||
|
||||
delete _gdalDataSet;
|
||||
}
|
||||
|
||||
|
||||
void TileProvider::prerender() {
|
||||
|
||||
// Remove filefutures that are inactive
|
||||
auto it = _fileFutureMap.begin();
|
||||
auto end = _fileFutureMap.end();
|
||||
for (; it != end;){
|
||||
|
||||
if (it->second->isAborted ||
|
||||
it->second->secondsRemaining > 20 ||
|
||||
it->second->errorMessage.compare("") != 0 ||
|
||||
it->second->isFinished)
|
||||
{
|
||||
it = _fileFutureMap.erase(it);
|
||||
//LDEBUG("removnig filefuture");
|
||||
}
|
||||
else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move finished texture tiles to cache
|
||||
/*
|
||||
while (_concurrentJobManager.numFinishedJobs() > 0) {
|
||||
auto finishedJob = _concurrentJobManager.popFinishedJob();
|
||||
|
||||
TextureLoadJob* finishedTextureLoadJob = reinterpret_cast<TextureLoadJob*>(finishedJob.get());
|
||||
|
||||
ghoul_assert(finishedTextureLoadJob != nullptr, "unable to reinterpret cast to TextureLoadJob*");
|
||||
|
||||
auto texture = finishedTextureLoadJob->product();
|
||||
// upload to gpu
|
||||
texture->uploadTexture();
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
texture->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToEdge);
|
||||
|
||||
//LDEBUG("uploaded texture " << finishedTextureLoadJob->hashKey());
|
||||
HashKey hashkey = finishedTextureLoadJob->hashKey();
|
||||
_tileCache.put(hashkey, texture);
|
||||
_fileFutureMap.erase(hashkey);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -109,77 +83,112 @@ namespace openspace {
|
||||
if (_tileCache.exist(hashkey)) {
|
||||
return _tileCache.get(hashkey);
|
||||
}
|
||||
else if (_fileFutureMap.find(hashkey) != _fileFutureMap.end()) {
|
||||
if (_fileFutureMap.find(hashkey)->second->isFinished) {
|
||||
|
||||
std::string fileName = _fileFutureMap.find(hashkey)->second->filePath;
|
||||
std::string filePath = "tiles/" + fileName;
|
||||
|
||||
std::shared_ptr<Texture> texture = loadAndInitTextureDisk(filePath);
|
||||
_tileCache.put(hashkey, texture);
|
||||
_fileFutureMap.erase(hashkey);
|
||||
else {
|
||||
//GeodeticTileIndex ti0 = { 0, 0, 0 };
|
||||
//auto texture = _converter.convertToOpenGLTexture(_gdalDataSet, tileIndex, GL_UNSIGNED_BYTE);
|
||||
auto texture = getTileInternal(tileIndex, GL_UNSIGNED_BYTE);
|
||||
texture->uploadTexture();
|
||||
texture->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToEdge);
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
_tileCache.put(hashkey, texture);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
//LDEBUG("Downloaded " << fileName);
|
||||
|
||||
|
||||
//auto job = std::shared_ptr<TextureLoadJob>(new TextureLoadJob(filePath, hashkey));
|
||||
//_concurrentJobManager.enqueueJob(job);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<Texture> TileProvider::getTileInternal(const GeodeticTileIndex& tileIndex, int GLType) {
|
||||
|
||||
|
||||
|
||||
int nRasters = _gdalDataSet->GetRasterCount();
|
||||
|
||||
ghoul_assert(nRasters > 0, "Bad dataset. Contains no rasterband.");
|
||||
|
||||
GDALRasterBand* firstBand = _gdalDataSet->GetRasterBand(1);
|
||||
|
||||
// Level = overviewCount - overview
|
||||
int numOverviews = firstBand->GetOverviewCount();
|
||||
|
||||
|
||||
int xSize0 = firstBand->GetXSize();
|
||||
int ySize0 = firstBand->GetYSize();
|
||||
|
||||
GeodeticPatch patch = GeodeticPatch(tileIndex);
|
||||
glm::uvec2 pixelStart0 = _converter.geodeticToPixel(_gdalDataSet, patch.northWestCorner());
|
||||
glm::uvec2 pixelEnd0 = _converter.geodeticToPixel(_gdalDataSet, patch.southEastCorner());
|
||||
glm::uvec2 numberOfPixels0 = pixelEnd0 - pixelStart0;
|
||||
|
||||
|
||||
int minNumPixels0 = glm::min(numberOfPixels0.x, numberOfPixels0.y);
|
||||
|
||||
int minNumPixelsRequired = 256;
|
||||
int ov = log2(minNumPixels0) - log2(minNumPixelsRequired);
|
||||
|
||||
ov = glm::clamp(ov, 0, numOverviews-1);
|
||||
|
||||
glm::uvec2 pixelStart(pixelStart0.x >> (ov+1), pixelStart0.y >> (ov + 1));
|
||||
glm::uvec2 numberOfPixels(numberOfPixels0.x >> (ov + 1), numberOfPixels0.y >> (ov + 1));
|
||||
|
||||
// For testing
|
||||
/*pixelStart = glm::uvec2(0, 0);
|
||||
numberOfPixels = glm::uvec2(512, 256);
|
||||
ov = 15;
|
||||
*/
|
||||
// The data that the texture should read
|
||||
GLubyte* imageData = new GLubyte[numberOfPixels.x * numberOfPixels.y * nRasters];
|
||||
|
||||
// Read the data (each rasterband is a separate channel)
|
||||
for (size_t i = 0; i < nRasters; i++) {
|
||||
GDALRasterBand* rasterBand = _gdalDataSet->GetRasterBand(i + 1)->GetOverview(ov);
|
||||
|
||||
int xSize = rasterBand->GetXSize();
|
||||
int ySize = rasterBand->GetYSize();
|
||||
|
||||
rasterBand->RasterIO(
|
||||
GF_Read,
|
||||
pixelStart.x, // Begin read x
|
||||
pixelStart.y, // Begin read y
|
||||
numberOfPixels.x, // width to read x
|
||||
numberOfPixels.y, // width to read y
|
||||
imageData + i, // Where to put data
|
||||
numberOfPixels.x, // width to write x in destination
|
||||
numberOfPixels.y, // width to write y in destination
|
||||
GDT_Byte, // Type
|
||||
sizeof(GLubyte) * nRasters, // Pixel spacing
|
||||
0); // Line spacing
|
||||
}
|
||||
|
||||
GLubyte* imageDataYflipped = new GLubyte[numberOfPixels.x * numberOfPixels.y * nRasters];
|
||||
for (size_t y = 0; y < numberOfPixels.y; y++) {
|
||||
for (size_t x = 0; x < numberOfPixels.x; x++) {
|
||||
imageDataYflipped[x + y * numberOfPixels.x] = imageData[x + (numberOfPixels.y - y) * numberOfPixels.x];
|
||||
}
|
||||
}
|
||||
else if(_fileFutureMap.size() < 100){
|
||||
|
||||
std::shared_ptr<DownloadManager::FileFuture> fileFuture = requestTile(tileIndex);
|
||||
_fileFutureMap.insert_or_assign(hashkey, fileFuture);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> TileProvider::loadAndInitTextureDisk(std::string filePath) {
|
||||
auto textureReader = ghoul::io::TextureReader::ref();
|
||||
std::shared_ptr<Texture> texture = std::move(textureReader.loadTexture(absPath(filePath)));
|
||||
GdalDataConverter::TextureFormat textrureFormat = _converter.getTextureFormatFromRasterCount(nRasters);
|
||||
|
||||
// upload to gpu
|
||||
texture->uploadTexture();
|
||||
texture->setFilter(ghoul::opengl::Texture::FilterMode::Linear);
|
||||
texture->setWrapping(ghoul::opengl::Texture::WrappingMode::ClampToEdge);
|
||||
|
||||
Texture* tex = new Texture(
|
||||
static_cast<void*>(imageDataYflipped),
|
||||
glm::uvec3(numberOfPixels.x, numberOfPixels.y, 1),
|
||||
textrureFormat.ghoulFormat,
|
||||
textrureFormat.glFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
Texture::FilterMode::Linear,
|
||||
Texture::WrappingMode::Repeat);
|
||||
|
||||
// The texture should take ownership of the data
|
||||
std::shared_ptr<Texture> texture = std::shared_ptr<Texture>(tex);
|
||||
|
||||
delete[] imageData;
|
||||
|
||||
// Do not free imageData since the texture now has ownership of it
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<DownloadManager::FileFuture> TileProvider::requestTile(const GeodeticTileIndex& tileIndex) {
|
||||
// download tile
|
||||
std::stringstream ss;
|
||||
//std::string baseUrl = "https://map1c.vis.earthdata.nasa.gov/wmts-geo/wmts.cgi?TIME=2016-04-17&layer=MODIS_Terra_CorrectedReflectance_TrueColor&tilematrixset=EPSG4326_250m&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fjpeg";
|
||||
//ss << baseUrl;
|
||||
//ss << "&TileMatrix=" << tileIndex.level;
|
||||
//ss << "&TileCol=" << tileIndex.x;
|
||||
//ss << "&TileRow=" << tileIndex.y;
|
||||
// https://map1c.vis.earthdata.nasa.gov/wmts-geo/wmts.cgi?TIME=2016-04-17&layer=MODIS_Terra_CorrectedReflectance_TrueColor&tilematrixset=EPSG4326_250m&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fjpeg&TileMatrix=0&TileCol=0&TileRow=0
|
||||
|
||||
std::string baseUrl = "http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-900913";
|
||||
ss << baseUrl;
|
||||
ss << "/" << tileIndex.level;
|
||||
ss << "/" << tileIndex.x;
|
||||
ss << "/" << tileIndex.y;
|
||||
ss << ".png?1461277159335";
|
||||
// http://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-900913/5/8/12.png?
|
||||
std::string twmsRequestUrl = ss.str();
|
||||
|
||||
ss = std::stringstream();
|
||||
ss << tileIndex.level;
|
||||
ss << "_" << tileIndex.x;
|
||||
ss << "_" << tileIndex.y;
|
||||
std::string filePath = "tiles/tile" + ss.str() + ".png";
|
||||
|
||||
using ghoul::filesystem::File;
|
||||
File localTileFile(filePath);
|
||||
bool overrideFile = true;
|
||||
|
||||
std::shared_ptr<DownloadManager::FileFuture> fileFuture = DownloadManager::ref().downloadFile(twmsRequestUrl, localTileFile, overrideFile);
|
||||
return fileFuture;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -22,39 +22,30 @@
|
||||
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
****************************************************************************************/
|
||||
|
||||
#ifndef __TWMS_TILE_PROVIDER_H__
|
||||
#define __TWMS_TILE_PROVIDER_H__
|
||||
#ifndef __TILE_PROVIDER_H__
|
||||
#define __TILE_PROVIDER_H__
|
||||
|
||||
#include <modules/globebrowsing/other/lrucache.h>
|
||||
#include <modules/globebrowsing/other/concurrentjobmanager.h>
|
||||
#include "gdal_priv.h"
|
||||
|
||||
#include <openspace/engine/downloadmanager.h>
|
||||
|
||||
#include <ghoul/logging/logmanager.h>
|
||||
#include <ghoul/filesystem/filesystem.h> // absPath
|
||||
#include <ghoul/opengl/texture.h>
|
||||
#include <ghoul/io/texture/texturereader.h>
|
||||
|
||||
#include <openspace/engine/downloadmanager.h>
|
||||
#include <modules/globebrowsing/geodetics/geodetic2.h>
|
||||
#include <modules/globebrowsing/other/lrucache.h>
|
||||
#include <modules/globebrowsing/other/concurrentjobmanager.h>
|
||||
#include <modules/globebrowsing/other/gdaldataconverter.h>
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// TILE INDEX //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace openspace {
|
||||
using HashKey = unsigned long;
|
||||
|
||||
struct GeodeticTileIndex {
|
||||
int x, y, level;
|
||||
|
||||
HashKey hashKey() const {
|
||||
return x ^ (y << 16) ^ (level << 21);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
namespace openspace {
|
||||
|
||||
using namespace ghoul::opengl;
|
||||
@@ -87,13 +78,11 @@ namespace openspace {
|
||||
HashKey _hashkey;
|
||||
std::shared_ptr<Texture> _texture;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TWMS TILE PROVIDER //
|
||||
// TILE PROVIDER //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -102,7 +91,7 @@ namespace openspace {
|
||||
|
||||
class TileProvider {
|
||||
public:
|
||||
TileProvider(int tileCacheSize);
|
||||
TileProvider(const std::string& fileName, int tileCacheSize);
|
||||
~TileProvider();
|
||||
|
||||
std::shared_ptr<Texture> getTile(const GeodeticTileIndex& tileIndex);
|
||||
@@ -111,17 +100,23 @@ namespace openspace {
|
||||
|
||||
|
||||
private:
|
||||
std::shared_ptr<DownloadManager::FileFuture> requestTile(const GeodeticTileIndex&);
|
||||
std::shared_ptr<Texture> loadAndInitTextureDisk(std::string filePath);
|
||||
|
||||
std::shared_ptr<Texture> getTileInternal(const GeodeticTileIndex& tileIndex, int GLType);
|
||||
|
||||
|
||||
LRUCache<HashKey, std::shared_ptr<Texture>> _tileCache;
|
||||
std::unordered_map<HashKey, std::shared_ptr<DownloadManager::FileFuture>> _fileFutureMap;
|
||||
|
||||
//LRUCache<HashKey, std::shared_ptr<DownloadManager::FileFuture>> _fileFutureCache;
|
||||
|
||||
//ConcurrentJobManager<Texture> _concurrentJobManager;
|
||||
const std::string _filePath;
|
||||
|
||||
static bool hasInitializedGDAL;
|
||||
GDALDataset* _gdalDataSet;
|
||||
GdalDataConverter _converter;
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
#endif // __TWMS_TILE_PROVIDER_H__
|
||||
#endif // __TILE_PROVIDER_H__
|
||||
@@ -77,7 +77,9 @@ namespace openspace {
|
||||
LatLonPatchRenderer::LatLonPatchRenderer(shared_ptr<Grid> grid)
|
||||
: PatchRenderer()
|
||||
, _grid(grid)
|
||||
, tileProvider(5000)
|
||||
, tileProvider("map_service_configs/TERRAIN.wms" , 5000)
|
||||
//, tileProvider("map_service_configs/frmt_wms_virtualearth.xml", 5000)
|
||||
//, tileProvider("textures/earth_bluemarble.jpg", 5000)
|
||||
{
|
||||
_programObject = OsEng.renderEngine().buildRenderProgram(
|
||||
"LatLonSphereMappingProgram",
|
||||
@@ -99,7 +101,6 @@ namespace openspace {
|
||||
|
||||
// Get the textures that should be used for rendering
|
||||
GeodeticTileIndex ti = _tileSet.getTileIndex(patch);
|
||||
|
||||
renderPatch(patch, data, ellipsoid, ti);
|
||||
}
|
||||
|
||||
@@ -112,8 +113,11 @@ namespace openspace {
|
||||
|
||||
using namespace glm;
|
||||
|
||||
// PATCH DID NOT MATCH THE TILEINDEX SO I CREATED A NEW PATCH FROM THE INDEX
|
||||
|
||||
GeodeticPatch newPatch = patch;//GeodeticPatch(tileIndex);
|
||||
|
||||
//GeodeticTileIndex tmpTileIndex = _tileSet.getTileIndex(patch);
|
||||
|
||||
|
||||
// TODO : Model transform should be fetched as a matrix directly.
|
||||
@@ -129,18 +133,14 @@ namespace openspace {
|
||||
// Get the textures that should be used for rendering
|
||||
std::shared_ptr<ghoul::opengl::Texture> tile00;
|
||||
bool usingTile = true;
|
||||
GeodeticTileIndex ti;
|
||||
ti.level =tileIndex.level;
|
||||
ti.x = tileIndex.y;
|
||||
ti.y = tileIndex.x;
|
||||
tile00 = tileProvider.getTile(ti);
|
||||
tile00 = tileProvider.getTile(tileIndex);
|
||||
|
||||
if (tile00 == nullptr) {
|
||||
tile00 = _tileSet.getTile(tileIndex);
|
||||
usingTile = false;
|
||||
}
|
||||
|
||||
glm::mat3 uvTransform = usingTile ? glm::mat3(1) : _tileSet.getUvTransformationPatchToTile(patch, tileIndex);
|
||||
glm::mat3 uvTransform = usingTile ? glm::mat3(1) : _tileSet.getUvTransformationPatchToTile(newPatch, tileIndex);
|
||||
|
||||
// Bind and use the texture
|
||||
ghoul::opengl::TextureUnit texUnit;
|
||||
@@ -149,11 +149,11 @@ namespace openspace {
|
||||
_programObject->setUniform("textureSampler", texUnit);
|
||||
_programObject->setUniform("uvTransformPatchToTile", uvTransform);
|
||||
|
||||
Geodetic2 swCorner = patch.southWestCorner();
|
||||
Geodetic2 swCorner = newPatch.southWestCorner();
|
||||
_programObject->setUniform("segmentsPerPatch", _grid->xSegments());
|
||||
_programObject->setUniform("modelViewProjectionTransform", modelViewProjectionTransform);
|
||||
_programObject->setUniform("minLatLon", vec2(swCorner.toLonLatVec2()));
|
||||
_programObject->setUniform("lonLatScalingFactor", vec2(patch.size().toLonLatVec2()));
|
||||
_programObject->setUniform("lonLatScalingFactor", vec2(newPatch.size().toLonLatVec2()));
|
||||
_programObject->setUniform("radiiSquared", vec3(ellipsoid.radiiSquared()));
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@@ -186,6 +186,12 @@ namespace openspace {
|
||||
_programObject->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);
|
||||
}
|
||||
|
||||
void ClipMapPatchRenderer::update() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClipMapPatchRenderer::renderPatch(
|
||||
const Geodetic2& patchSize,
|
||||
const RenderData& data,
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace openspace {
|
||||
PatchRenderer();
|
||||
~PatchRenderer();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
unique_ptr<ProgramObject> _programObject;
|
||||
@@ -102,10 +103,14 @@ namespace openspace {
|
||||
public:
|
||||
ClipMapPatchRenderer(shared_ptr<ClipMapGrid> grid);
|
||||
|
||||
void update();
|
||||
|
||||
void renderPatch(
|
||||
const Geodetic2& patchSize,
|
||||
const RenderData& data,
|
||||
const Ellipsoid& ellipsoid);
|
||||
|
||||
|
||||
private:
|
||||
shared_ptr<ClipMapGrid> _grid;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user