初始提交: UE5.3项目基础框架

This commit is contained in:
2025-10-14 11:14:54 +08:00
commit 721d9fd98e
5334 changed files with 316782 additions and 0 deletions

View File

@ -0,0 +1,156 @@
#pragma once
#include "Ellipsoid.h"
#include "GlobeRectangle.h"
#include "Library.h"
#include <CesiumGeometry/CullingResult.h>
#include <CesiumGeometry/OrientedBoundingBox.h>
#include <CesiumGeospatial/Ellipsoid.h>
namespace CesiumGeometry {
class Plane;
}
namespace CesiumGeospatial {
class Cartographic;
/**
* @brief A bounding volume specified as a longitude/latitude bounding box and a
* minimum and maximum height.
*/
class CESIUMGEOSPATIAL_API BoundingRegion final {
public:
/**
* @brief Constructs a new bounding region.
*
* @param rectangle The bounding rectangle of the region.
* @param minimumHeight The minimum height in meters.
* @param maximumHeight The maximum height in meters.
* @param ellipsoid The ellipsoid on which this region is defined.
*/
BoundingRegion(
const GlobeRectangle& rectangle,
double minimumHeight,
double maximumHeight,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Gets the bounding rectangle of the region.
*/
const GlobeRectangle& getRectangle() const noexcept {
return this->_rectangle;
}
/**
* @brief Gets the minimum height of the region.
*/
double getMinimumHeight() const noexcept { return this->_minimumHeight; }
/**
* @brief Gets the maximum height of the region.
*/
double getMaximumHeight() const noexcept { return this->_maximumHeight; }
/**
* @brief Gets an oriented bounding box containing this region.
*/
const CesiumGeometry::OrientedBoundingBox& getBoundingBox() const noexcept {
return this->_boundingBox;
}
/**
* @brief Determines on which side of a plane the bounding region is located.
*
* @param plane The plane to test against.
* @return The {@link CesiumGeometry::CullingResult}
* * `Inside` if the entire region is on the side of the plane the normal is
* pointing.
* * `Outside` if the entire region is on the opposite side.
* * `Intersecting` if the region intersects the plane.
*/
CesiumGeometry::CullingResult
intersectPlane(const CesiumGeometry::Plane& plane) const noexcept;
/**
* @brief Computes the distance-squared from a position in ellipsoid-centered
* Cartesian coordinates to the closest point in this bounding region.
*
* If the position cannot be converted into cartograpic coordinates for the
* given ellipsoid (because it is close to the center of the ellipsoid),
* then this function will return the squared distance between the given
* position and the closest point of the bounding box that is enclosed in
* this region.
*
* @param position The position.
* @param ellipsoid The ellipsoid on which this region is defined.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeDistanceSquaredToPosition(
const glm::dvec3& position,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) const noexcept;
/**
* @brief Computes the distance-squared from a longitude-latitude-height
* position to the closest point in this bounding region.
*
* @param position The position.
* @param ellipsoid The ellipsoid on which this region is defined.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeDistanceSquaredToPosition(
const Cartographic& position,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) const noexcept;
/**
* @brief Computes the distance-squared from a position to the closest point
* in this bounding region, when the longitude-latitude-height and
* ellipsoid-centered Cartesian coordinates of the position are both already
* known.
*
* @param cartographicPosition The position as a longitude-latitude-height.
* @param cartesianPosition The position as ellipsoid-centered Cartesian
* coordinates.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeDistanceSquaredToPosition(
const Cartographic& cartographicPosition,
const glm::dvec3& cartesianPosition) const noexcept;
/**
* @brief Computes the union of this bounding region with another.
*
* @param other The other bounding region.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
* @return The union.
*/
BoundingRegion computeUnion(
const BoundingRegion& other,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) const noexcept;
private:
static CesiumGeometry::OrientedBoundingBox _computeBoundingBox(
const GlobeRectangle& rectangle,
double minimumHeight,
double maximumHeight,
const Ellipsoid& ellipsoid);
GlobeRectangle _rectangle;
double _minimumHeight;
double _maximumHeight;
CesiumGeometry::OrientedBoundingBox _boundingBox;
glm::dvec3 _southwestCornerCartesian;
glm::dvec3 _northeastCornerCartesian;
glm::dvec3 _westNormal;
glm::dvec3 _eastNormal;
glm::dvec3 _southNormal;
glm::dvec3 _northNormal;
bool _planesAreInvalid;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,101 @@
#pragma once
#include "BoundingRegion.h"
#include "GlobeRectangle.h"
#include "Library.h"
namespace CesiumGeospatial {
/**
* @brief Helper class for creating a \ref BoundingRegion or \ref GlobeRectangle
* from a set of points.
*/
class CESIUMGEOSPATIAL_API BoundingRegionBuilder {
public:
/**
* @brief Constructs a new instance, starting with an empty bounding region.
*/
BoundingRegionBuilder() noexcept;
/**
* @brief Gets the final region from this builder.
*
* If no positions are added to this builder, the returned region's rectangle
* will be {@link GlobeRectangle::EMPTY}, its minimum height will be 1.0, and
* its maximum height will be -1.0 (the minimum will be greater than the
* maximum).
*/
BoundingRegion
toRegion(const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) const;
/**
* @brief Gets the final globe rectangle from this builder.
*
* If no positions are added to this builder, the returned region's rectangle
* will be {@link GlobeRectangle::EMPTY}.
*/
GlobeRectangle toGlobeRectangle() const;
/**
* @brief Sets the distance from the North or South pole, in radians, that is
* considered "too close" to rely on the longitude value.
*
* When a position given to {@link expandToIncludePosition} has a latitude closer than this value,
* the region will be updated to include the position's _latitude_, but the
* position's longitude will be ignored.
*
* @param tolerance The new tolerance.
*/
void setPoleTolerance(double tolerance) noexcept;
/**
* @brief Gets the distance from the North or South pole, in radians, that is
* considered "too close" to rely on the longitude value.
*
* When a position given to {@link expandToIncludePosition} has a latitude closer than this value,
* the region will be updated to include the position's _latitude_, but the
* position's longitude will be ignored.
*
* @return The tolerance.
*/
double getPoleTolerance() const noexcept;
/**
* @brief Expands the bounding region to include the given position.
*
* The region will be kept as small as possible.
*
* @param position The position to be included in the region.
* @returns True if the region was modified, or false if the region already
* contained the position.
*/
bool expandToIncludePosition(const Cartographic& position);
private:
/**
* @brief When a position's latitude is within this distance in radians from
* the North or South pole, its longitude should be considered unreliable and
* not used to expand the bounding region in that direction.
*/
double _poleTolerance;
GlobeRectangle _rectangle;
double _minimumHeight;
double _maximumHeight;
/**
* @brief True if the region is empty (covers no space) in the longitude
* direction.
*
* We know the rectangle is empty in the latitude direction when South
* is greater than North. But due to wrapping at the anti-meridian, the West
* longitude may very well be greater than the East longitude. So this field
* explicitly tracks whether the region is empty in the longitude direction.
*
* It's possible for the longitude range to be empty while the latitude range
* is not when all of the points used to expand the region so far have been
* too close to the poles to reliably use to compute longitude.
*/
bool _longitudeRangeIsEmpty;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,110 @@
#pragma once
#include "BoundingRegion.h"
#include "Library.h"
namespace CesiumGeospatial {
/**
* @brief A {@link BoundingRegion} whose heights might be very inaccurate and so
* distances should be estimated conservatively for level-of-detail
* computations.
*
* An instance of this class serves as a marker of the imprecision of the
* heights in a {@link BoundingRegion}, and also has a
* {@link BoundingRegionWithLooseFittingHeights::computeConservativeDistanceSquaredToPosition}
* method to compute the conservative distance metric.
*/
class CESIUMGEOSPATIAL_API BoundingRegionWithLooseFittingHeights final {
public:
/**
* @brief Constructs a new bounding region.
*
* @param boundingRegion The bounding region that has imprecise heights.
*/
BoundingRegionWithLooseFittingHeights(
const BoundingRegion& boundingRegion) noexcept;
/**
* @brief Gets the bounding region that has imprecise heights.
*/
const BoundingRegion& getBoundingRegion() const noexcept {
return this->_region;
}
/**
* @brief Computes the conservative distance-squared from a position in
* ellipsoid-centered Cartesian coordinates to the closest point in this
* bounding region.
*
* It is conservative in that the distance is computed using whichever
* is _farther away_ of this bounding region's imprecise minimum and maximum
* heights, so the returned distance may be greater than what the distance to
* the bounding region would be if the heights were precise. When used for
* level-of-detail selection, this ensures that imprecise selection caused by
* the imprecise heights will cause _too little_ detail to be loaded rather
* than too much detail. This is important because overestimating the required
* level-of-detail can require an excessive number of tiles to be loaded.
*
* @param position The position.
* @param ellipsoid The ellipsoid on which this region is defined.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeConservativeDistanceSquaredToPosition(
const glm::dvec3& position,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) const noexcept;
/**
* @brief Computes the conservative distance-squared from a
* longitude-latitude-height position to the closest point in this bounding
* region.
*
* It is conservative in that the distance is computed using whichever
* is _farther away_ of this bounding region's imprecise minimum and maximum
* heights, so the returned distance may be greater than what the distance to
* the bounding region would be if the heights were precise. When used for
* level-of-detail selection, this ensures that imprecise selection caused by
* the imprecise heights will cause _too little_ detail to be loaded rather
* than too much detail. This is important because overestimating the required
* level-of-detail can require an excessive number of tiles to be loaded.
*
* @param position The position.
* @param ellipsoid The ellipsoid on which this region is defined.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeConservativeDistanceSquaredToPosition(
const Cartographic& position,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) const noexcept;
/**
* @brief Computes the conservative distance-squared from a position to the
* closest point in this bounding region, when the longitude-latitude-height
* and ellipsoid-centered Cartesian coordinates of the position are both
* already known.
*
* It is conservative in that the distance is computed using whichever
* is _farther away_ of this bounding region's imprecise minimum and maximum
* heights, so the returned distance may be greater than what the distance to
* the bounding region would be if the heights were precise. When used for
* level-of-detail selection, this ensures that imprecise selection caused by
* the imprecise heights will cause _too little_ detail to be loaded rather
* than too much detail. This is important because overestimating the required
* level-of-detail can require an excessive number of tiles to be loaded.
*
* @param cartographicPosition The position as a longitude-latitude-height.
* @param cartesianPosition The position as ellipsoid-centered Cartesian
* coordinates.
* @return The distance-squared from the position to the closest point in the
* bounding region.
*/
double computeConservativeDistanceSquaredToPosition(
const Cartographic& cartographicPosition,
const glm::dvec3& cartesianPosition) const noexcept;
private:
BoundingRegion _region;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,72 @@
#pragma once
#include "Library.h"
#include <CesiumUtility/Math.h>
namespace CesiumGeospatial {
/**
* @brief A position defined by longitude, latitude, and height.
*/
class CESIUMGEOSPATIAL_API Cartographic final {
public:
/**
* @brief Creates a new instance.
*
* @param longitudeRadians The longitude, in radians.
* @param latitudeRadians The latitude, in radians.
* @param heightMeters The height, in meters. Default value: 0.0.
*/
constexpr Cartographic(
double longitudeRadians,
double latitudeRadians,
double heightMeters = 0.0) noexcept
: longitude(longitudeRadians),
latitude(latitudeRadians),
height(heightMeters) {}
/**
* @brief Creates a new instance from a longitude and latitude specified in
* degrees, and a height given in meters.
*
* The values in the resulting object will be in radians.
*
* @param longitudeDegrees The longitude, in degrees.
* @param latitudeDegrees The latitude, in degrees.
* @param heightMeters The height, in meters. Default value: 0.0.
*/
static constexpr Cartographic fromDegrees(
double longitudeDegrees,
double latitudeDegrees,
double heightMeters = 0.0) noexcept {
return Cartographic(
CesiumUtility::Math::degreesToRadians(longitudeDegrees),
CesiumUtility::Math::degreesToRadians(latitudeDegrees),
heightMeters);
}
/**
* @brief Returns `true` if two cartographics are equal.
*/
constexpr bool operator==(const Cartographic& rhs) const noexcept {
return this->longitude == rhs.longitude && this->latitude == rhs.latitude &&
this->height == rhs.height;
};
/**
* @brief The longitude, in radians.
*/
double longitude;
/**
* @brief The latitude, in radians.
*/
double latitude;
/**
* @brief The height, in meters.
*/
double height;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,101 @@
#pragma once
#include "GlobeRectangle.h"
#include "Library.h"
#include <glm/vec2.hpp>
#include <optional>
#include <string>
#include <vector>
namespace CesiumGeospatial {
/**
* @brief A 2D polygon expressed as a list of longitude/latitude coordinates in
* radians.
*
* The {@link Ellipsoid} associated with the coordinates is not specified
* directly by this instance, but it is assumed that the longitude values range
* from -PI to PI radians and the latitude values range from -PI/2 to PI/2
* radians. Longitude values outside this range are wrapped to be inside the
* range. Latitude values are clamped to the range.
*/
class CESIUMGEOSPATIAL_API CartographicPolygon final {
public:
/**
* @brief Constructs a 2D polygon that can be rasterized onto {@link Cesium3DTilesSelection::Tileset}
* objects.
*
* @param polygon An array of longitude-latitude points in radians defining
* the perimeter of the 2D polygon.
*/
CartographicPolygon(const std::vector<glm::dvec2>& polygon);
/**
* @brief Returns the longitude-latitude vertices that define the
* perimeter of the selected polygon.
*
* @return The perimeter vertices in longitude-latitude radians.
*/
constexpr const std::vector<glm::dvec2>& getVertices() const {
return this->_vertices;
}
/**
* @brief Returns the triangulated indices representing a triangle
* decomposition of the polygon. The indices are in reference to the
* polygon's perimeter vertices.
*
* @return The indices for the polygon's triangle decomposition.
*/
constexpr const std::vector<uint32_t>& getIndices() const {
return this->_indices;
}
/**
* @brief Returns a {@link GlobeRectangle} that represents the bounding
* rectangle of the polygon.
*
* @return The polygon's global bounding rectangle.
*/
constexpr const std::optional<CesiumGeospatial::GlobeRectangle>&
getBoundingRectangle() const {
return this->_boundingRectangle;
}
/**
* @brief Determines whether a globe rectangle is completely inside any of the
* polygons in a list.
*
* @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile.
* @param cartographicPolygons The list of polygons to check.
* @return True if the rectangle is completely inside a polygon; otherwise,
* false.
*/
static bool rectangleIsWithinPolygons(
const CesiumGeospatial::GlobeRectangle& rectangle,
const std::vector<CesiumGeospatial::CartographicPolygon>&
cartographicPolygons) noexcept;
/**
* @brief Determines whether a globe rectangle is completely outside all the
* polygons in a list.
*
* @param rectangle The {@link CesiumGeospatial::GlobeRectangle} of the tile.
* @param cartographicPolygons The list of polygons to check.
* @return True if the rectangle is completely outside all the polygons;
* otherwise, false.
*/
static bool rectangleIsOutsidePolygons(
const CesiumGeospatial::GlobeRectangle& rectangle,
const std::vector<CesiumGeospatial::CartographicPolygon>&
cartographicPolygons) noexcept;
private:
std::vector<glm::dvec2> _vertices;
std::vector<uint32_t> _indices;
std::optional<CesiumGeospatial::GlobeRectangle> _boundingRectangle;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,60 @@
#pragma once
#include "Library.h"
#include <cstdint>
#include <optional>
#include <span>
#include <string>
#include <vector>
namespace CesiumGeospatial {
class Cartographic;
/**
* @brief Loads and queries heights from an Earth Gravitational Model 1996
* (EGM96) grid.
*
* EGM96 is a standard geopotential model of the earth's surface, which can be
* used to obtain an approximation of the mean sea level (MSL) height at any
* location on Earth.
*/
class CESIUMGEOSPATIAL_API EarthGravitationalModel1996Grid final {
public:
/**
* @brief Attempts to create a {@link EarthGravitationalModel1996Grid} from the given buffer.
*
* This method expects the buffer to contain the contents of the WW15MGH.DAC
* 15-arcminute grid. It must be at least 721 * 1440 * 2 = 2,076,480 bytes.
* Any additional bytes at the end of the buffer are ignored.
*
* @returns The instance created from the buffer, or `std::nullopt` if the
* buffer cannot be interpreted as an EGM96 grid.
*/
static std::optional<EarthGravitationalModel1996Grid>
fromBuffer(const std::span<const std::byte>& buffer);
/**
* @brief Samples the height at the given position.
*
* @param position The position to sample. The `height` is ignored.
* @returns The height (in meters) of the EGM96 surface above the WGS84
* ellipsoid. A positive value indicates that EGM96 is above the ellipsoid's
* surface, while a negative value indicates that EGM96 is below the
* ellipsoid's surface.
*/
double sampleHeight(const Cartographic& position) const;
private:
EarthGravitationalModel1996Grid(std::vector<int16_t>&& gridValues);
/**
* Returns the height of the given grid value, in meters.
*/
double getHeightForIndices(size_t vertical, size_t horizontal) const;
std::vector<int16_t> _gridValues;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,193 @@
#pragma once
#include "Cartographic.h"
#include "Library.h"
#include <CesiumUtility/Math.h>
#include <glm/vec3.hpp>
#include <optional>
// The comments are copied here so that the doc comment always shows up in
// Intellisense whether the default is toggled or not.
#ifndef CESIUM_DISABLE_DEFAULT_ELLIPSOID
// To keep from breaking our API, a lot of things now need default Ellipsoid
// parameters. However, we shouldn't rely on these defaults internally, so
// disabling them is a good way to get a compile-time check that they're not
// being used. This macro allows us to toggle this check.
#define CESIUM_DEFAULT_ELLIPSOID = CesiumGeospatial::Ellipsoid::WGS84
#else
// To keep from breaking our API, a lot of things now need default Ellipsoid
// parameters. However, we shouldn't rely on these defaults internally, so
// disabling them is a good way to get a compile-time check that they're not
// being used. This macro allows us to toggle this check.
#define CESIUM_DEFAULT_ELLIPSOID
#endif
namespace CesiumGeospatial {
/**
* @brief A quadratic surface defined in Cartesian coordinates.
*
* The surface is defined by the equation `(x / a)^2 + (y / b)^2 + (z / c)^2 =
* 1`. This is primarily used by Cesium to represent the shape of planetary
* bodies. Rather than constructing this object directly, one of the provided
* constants is normally used.
*/
class CESIUMGEOSPATIAL_API Ellipsoid final {
public:
/**
* @brief An Ellipsoid instance initialized to the WGS84 standard.
*
* The ellipsoid is initialized to the World Geodetic System (WGS84)
* standard, as defined in
* https://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf.
*/
static /*constexpr*/ const Ellipsoid WGS84;
/**
* @brief An Ellipsoid with all three radii set to one meter.
*/
static const Ellipsoid UNIT_SPHERE;
/**
* @brief Creates a new instance.
*
* @param x The radius in x-direction.
* @param y The radius in y-direction.
* @param z The radius in z-direction.
*/
constexpr Ellipsoid(double x, double y, double z) noexcept
: Ellipsoid(glm::dvec3(x, y, z)) {}
/**
* @brief Creates a new instance.
*
* @param radii The radii in x-, y-, and z-direction.
*/
constexpr Ellipsoid(const glm::dvec3& radii) noexcept
: _radii(radii),
_radiiSquared(radii.x * radii.x, radii.y * radii.y, radii.z * radii.z),
_oneOverRadii(1.0 / radii.x, 1.0 / radii.y, 1.0 / radii.z),
_oneOverRadiiSquared(
1.0 / (radii.x * radii.x),
1.0 / (radii.y * radii.y),
1.0 / (radii.z * radii.z)),
_centerToleranceSquared(CesiumUtility::Math::Epsilon1) {}
/**
* @brief Returns the radii in x-, y-, and z-direction.
*/
constexpr const glm::dvec3& getRadii() const noexcept { return this->_radii; }
/**
* @brief Computes the normal of the plane tangent to the surface of the
* ellipsoid at the provided position.
*
* @param position The cartesian position for which to to determine the
* surface normal.
* @return The normal.
*/
glm::dvec3 geodeticSurfaceNormal(const glm::dvec3& position) const noexcept;
/**
* @brief Computes the normal of the plane tangent to the surface of the
* ellipsoid at the provided position.
*
* @param cartographic The {@link Cartographic} position for which to to
* determine the surface normal.
* @return The normal.
*/
glm::dvec3
geodeticSurfaceNormal(const Cartographic& cartographic) const noexcept;
/**
* @brief Converts the provided {@link Cartographic} to cartesian
* representation.
*
* @param cartographic The {@link Cartographic} position.
* @return The cartesian representation.
*/
glm::dvec3
cartographicToCartesian(const Cartographic& cartographic) const noexcept;
/**
* @brief Converts the provided cartesian to a {@link Cartographic}
* representation.
*
* The result will be the empty optional if the given cartesian is at the
* center of this ellipsoid.
*
* @param cartesian The cartesian position.
* @return The {@link Cartographic} representation, or the empty optional if
* the cartesian is at the center of this ellipsoid.
*/
std::optional<Cartographic>
cartesianToCartographic(const glm::dvec3& cartesian) const noexcept;
/**
* @brief Scales the given cartesian position along the geodetic surface
* normal so that it is on the surface of this ellipsoid.
*
* The result will be the empty optional if the position is at the center of
* this ellipsoid.
*
* @param cartesian The cartesian position.
* @return The scaled position, or the empty optional if
* the cartesian is at the center of this ellipsoid.
*/
std::optional<glm::dvec3>
scaleToGeodeticSurface(const glm::dvec3& cartesian) const noexcept;
/**
* @brief Scales the provided cartesian position along the geocentric
* surface normal so that it is on the surface of this ellipsoid.
*
* @param cartesian The cartesian position to scale.
* @returns The scaled position, or the empty optional if the cartesian is at
* the center of this ellipsoid.
*/
std::optional<glm::dvec3>
scaleToGeocentricSurface(const glm::dvec3& cartesian) const noexcept;
/**
* @brief The maximum radius in any dimension.
*
* @return The maximum radius.
*/
constexpr double getMaximumRadius() const noexcept {
return glm::max(this->_radii.x, glm::max(this->_radii.y, this->_radii.z));
}
/**
* @brief The minimum radius in any dimension.
*
* @return The minimum radius.
*/
constexpr double getMinimumRadius() const noexcept {
return glm::min(this->_radii.x, glm::min(this->_radii.y, this->_radii.z));
}
/**
* @brief Returns `true` if two elliposids are equal.
*/
constexpr bool operator==(const Ellipsoid& rhs) const noexcept {
return this->_radii == rhs._radii;
};
/**
* @brief Returns `true` if two elliposids are *not* equal.
*/
constexpr bool operator!=(const Ellipsoid& rhs) const noexcept {
return !(*this == rhs);
};
private:
glm::dvec3 _radii;
glm::dvec3 _radiiSquared;
glm::dvec3 _oneOverRadii;
glm::dvec3 _oneOverRadiiSquared;
double _centerToleranceSquared;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,112 @@
#pragma once
#include "Ellipsoid.h"
#include "Library.h"
#include <CesiumGeometry/Plane.h>
#include <glm/fwd.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace CesiumGeospatial {
/**
* @brief A plane tangent to an {@link Ellipsoid} at a certain origin position.
*
* If the origin is not on the surface of the ellipsoid, its surface projection
* will be used.
*/
class CESIUMGEOSPATIAL_API EllipsoidTangentPlane final {
public:
/**
* @brief Creates a new instance.
*
* @param origin The origin, in cartesian coordinates.
* @param ellipsoid The ellipsoid. Default value: {@link Ellipsoid::WGS84}.
* @throws An `std::invalid_argument` if the given origin is at the
* center of the ellipsoid.
*/
EllipsoidTangentPlane(
const glm::dvec3& origin,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Creates a new instance.
*
* @param eastNorthUpToFixedFrame A transform that was computed with
* {@link GlobeTransforms::eastNorthUpToFixedFrame}.
* @param ellipsoid The ellipsoid. Default value: {@link Ellipsoid::WGS84}.
*/
EllipsoidTangentPlane(
const glm::dmat4& eastNorthUpToFixedFrame,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Returns the {@link Ellipsoid}.
*/
const Ellipsoid& getEllipsoid() const noexcept { return this->_ellipsoid; }
/**
* @brief Returns the origin, in cartesian coordinates.
*/
const glm::dvec3& getOrigin() const noexcept { return this->_origin; }
/**
* @brief Returns the x-axis of this plane.
*/
const glm::dvec3& getXAxis() const noexcept { return this->_xAxis; }
/**
* @brief Returns the y-axis of this plane.
*/
const glm::dvec3& getYAxis() const noexcept { return this->_yAxis; }
/**
* @brief Returns the z-axis (i.e. the normal) of this plane.
*/
const glm::dvec3& getZAxis() const noexcept {
return this->_plane.getNormal();
}
/**
* @brief Returns a {@link CesiumGeometry::Plane} representation of this
* plane.
*/
const CesiumGeometry::Plane& getPlane() const noexcept {
return this->_plane;
}
/**
* @brief Computes the position of the projection of the given point on this
* plane.
*
* Projects the given point on this plane, along the normal. The result will
* be a 2D point, referring to the local coordinate system of the plane that
* is given by the x- and y-axis.
*
* @param cartesian The point in cartesian coordinates.
* @return The 2D representation of the point on the plane that is closest to
* the given position.
*/
glm::dvec2
projectPointToNearestOnPlane(const glm::dvec3& cartesian) const noexcept;
private:
/**
* Computes the matrix that is used for the constructor (if the origin
* and ellipsoid are given), but throws an `std::invalid_argument` if
* the origin is at the center of the ellipsoid.
*/
static glm::dmat4 computeEastNorthUpToFixedFrame(
const glm::dvec3& origin,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
Ellipsoid _ellipsoid;
glm::dvec3 _origin;
glm::dvec3 _xAxis;
glm::dvec3 _yAxis;
CesiumGeometry::Plane _plane;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,155 @@
#pragma once
#include "Ellipsoid.h"
#include "GlobeRectangle.h"
#include "Library.h"
#include <CesiumUtility/Math.h>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace CesiumGeospatial {
class Cartographic;
/**
* @brief A map projection where longitude and latitude are mapped using an
* {@link Ellipsoid}.
*
* The longitude and latitude are linearly mapped to X and Y by multiplying them
* (in radians) by the {@link Ellipsoid::getMaximumRadius()}. This projection is
* commonly known as geographic, equirectangular, equidistant cylindrical, or
* plate carrée. It is also known as EPSG:4326.
*
* @see WebMercatorProjection
*/
class CESIUMGEOSPATIAL_API GeographicProjection final {
public:
/**
* @brief The maximum bounding rectangle of the geographic projection,
* ranging from -PI to PI radians longitude and
* from -PI/2 to +PI/2 radians latitude.
*/
static constexpr GlobeRectangle MAXIMUM_GLOBE_RECTANGLE = GlobeRectangle(
-CesiumUtility::Math::OnePi,
-CesiumUtility::Math::PiOverTwo,
CesiumUtility::Math::OnePi,
CesiumUtility::Math::PiOverTwo);
/**
* @brief Computes the maximum rectangle that can be covered with this
* projection
*
* @param ellipsoid The {@link Ellipsoid}. Default value:
* {@link Ellipsoid::WGS84}.
* @return The rectangle
*/
static constexpr CesiumGeometry::Rectangle computeMaximumProjectedRectangle(
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) noexcept {
const double longitudeValue =
ellipsoid.getMaximumRadius() * CesiumUtility::Math::OnePi;
const double latitudeValue =
ellipsoid.getMaximumRadius() * CesiumUtility::Math::PiOverTwo;
return CesiumGeometry::Rectangle(
-longitudeValue,
-latitudeValue,
longitudeValue,
latitudeValue);
}
/**
* @brief Constructs a new instance.
*
* @param ellipsoid The {@link Ellipsoid}.
*/
GeographicProjection(
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) noexcept;
/**
* @brief Gets the {@link Ellipsoid}.
*/
const Ellipsoid& getEllipsoid() const noexcept { return this->_ellipsoid; }
/**
* @brief Converts geodedic ellipsoid coordinates to geographic coordinates.
*
* Converts geodetic ellipsoid coordinates, in radians, to the equivalent
* geographic X, Y, Z coordinates expressed in meters. The height is copied
* unmodified to the `z` coordinate.
*
* @param cartographic The geodetic coordinates in radians.
* @returns The equivalent geographic X, Y, Z coordinates, in meters.
*/
glm::dvec3 project(const Cartographic& cartographic) const noexcept;
/**
* @brief Projects a globe rectangle to geographic coordinates.
*
* This is done by projecting the southwest and northeast corners.
*
* @param rectangle The globe rectangle to project.
* @return The projected rectangle.
*/
CesiumGeometry::Rectangle
project(const CesiumGeospatial::GlobeRectangle& rectangle) const noexcept;
/**
* @brief Converts geographic coordinates to geodetic ellipsoid coordinates.
*
* Converts geographic X and Y coordinates, expressed in meters, to a
* {@link Cartographic} containing geodetic ellipsoid coordinates.
* The height is set to 0.0.
*
* @param projectedCoordinates The geographic projected coordinates to
* unproject.
* @returns The equivalent cartographic coordinates.
*/
Cartographic unproject(const glm::dvec2& projectedCoordinates) const noexcept;
/**
* @brief Converts geographic coordinates to geodetic ellipsoid coordinates.
*
* Converts geographic X, Y coordinates, expressed in meters, to a
* {@link Cartographic} containing geodetic ellipsoid coordinates.
* The Z coordinate is copied unmodified to the height.
*
* @param projectedCoordinates The geographic projected coordinates to
* unproject, with height (z) in meters.
* @returns The equivalent cartographic coordinates.
*/
Cartographic unproject(const glm::dvec3& projectedCoordinates) const noexcept;
/**
* @brief Unprojects a geographic rectangle to the globe.
*
* This is done by unprojecting the southwest and northeast corners.
*
* @param rectangle The rectangle to unproject.
* @returns The unprojected rectangle.
*/
CesiumGeospatial::GlobeRectangle
unproject(const CesiumGeometry::Rectangle& rectangle) const noexcept;
/**
* @brief Returns `true` if two projections (i.e. their ellipsoids) are equal.
*/
bool operator==(const GeographicProjection& rhs) const noexcept {
return this->_ellipsoid == rhs._ellipsoid;
};
/**
* @brief Returns `true` if two projections (i.e. their ellipsoids) are *not*
* equal.
*/
bool operator!=(const GeographicProjection& rhs) const noexcept {
return !(*this == rhs);
};
private:
Ellipsoid _ellipsoid;
double _semimajorAxis;
double _oneOverSemimajorAxis;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,137 @@
#pragma once
#include "Library.h"
#include <CesiumGeospatial/Ellipsoid.h>
#include <glm/mat4x4.hpp>
#include <optional>
namespace CesiumGeospatial {
class LocalHorizontalCoordinateSystem;
/**
* @brief Anchors an object to the globe by defining a transformation from the
* object's coordinate to the globe-fixed coordinate system (usually
* \ref glossary-ecef).
*
* This class allows the anchored coordinate system to be realized in any
* {@link LocalHorizontalCoordinateSystem}. When the object is moved, either by
* specifying a new globe-fixed transform or a new local transform, the
* orientation may optionally be updated to keep the object upright at its new
* location on the globe.
*/
class CESIUMGEOSPATIAL_API GlobeAnchor final {
public:
/**
* @brief Creates a new instance from a transformation to a local coordinate
* system.
*
* For example, in a game engine, the transformation may be the game
* object's initial transformation to the game engine's world coordinate
* system.
*
* @param localCoordinateSystem The local coordinate system that is the target
* of the transformation.
* @param anchorToLocal The matrix transforming from this object's
* coordinate system to the local coordinate system.
*/
static GlobeAnchor fromAnchorToLocalTransform(
const LocalHorizontalCoordinateSystem& localCoordinateSystem,
const glm::dmat4& anchorToLocal);
/**
* @brief Creates a new instance from a transformation to the globe-fixed
* coordinate system.
*
* @param anchorToFixed The matrix transforming from this object's
* coordinate system to the globe-fixed coordinate system.
*/
static GlobeAnchor
fromAnchorToFixedTransform(const glm::dmat4& anchorToFixed);
/**
* @brief Constructs a new instance with a given transformation to the
* globe-fixed coordinate system.
*
* @param anchorToFixed The matrix transforming from this object's
* coordinate system to the globe-fixed coordinate system.
*/
explicit GlobeAnchor(const glm::dmat4& anchorToFixed);
/**
* @brief Gets the transformation from the anchor's coordinate system to the
* globe-fixed coordinate system.
*/
const glm::dmat4& getAnchorToFixedTransform() const;
/**
* @brief Sets the new transformation from the anchor's coordinate system
* to globe-fixed coordinates.
*
* @param newAnchorToFixed The new matrix transforming from this object's
* coordinate system to the globe-fixed coordinate system.
* @param adjustOrientation Whether to adjust the anchor's orientation based
* on globe curvature as the anchor moves. The Earth is not flat, so as we
* move across its surface, the direction of "up" changes. If we ignore this
* fact and leave an object's orientation unchanged as it moves over the globe
* surface, the object will become increasingly tilted and eventually be
* completely upside-down when we arrive at the opposite side of the globe.
* When this parameter is true, this method will automatically apply a
* rotation to the anchor to account for globe curvature when the position on
* the globe changes. This parameter should usually be true, but it may be
* useful to set it to false it when the caller already accounts for globe
* curvature itself, because in that case anchor would be over-rotated.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
void setAnchorToFixedTransform(
const glm::dmat4& newAnchorToFixed,
bool adjustOrientation,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Gets the transformation from the anchor's coordinate system to the
* given local-horizontal coordinate system.
*/
glm::dmat4 getAnchorToLocalTransform(
const LocalHorizontalCoordinateSystem& localCoordinateSystem) const;
/**
* @brief Sets the globe-fixed transformation based on a new transformation
* from anchor coordinates to a local-horizontal coordinate system.
*
* For example, in a game engine, the new transformation may be the game
* object's normal transformation to the game engine's world coordinate
* system. It may need to be updated because the object was just moved by the
* physics engine.
*
* @param localCoordinateSystem The local coordinate system that is the target
* of the transformation.
* @param newAnchorToLocal The new matrix transforming from this object's
* coordinate system to the local coordinate system.
* @param adjustOrientation Whether to adjust the anchor's orientation based
* on globe curvature as the anchor moves. The Earth is not flat, so as we
* move across its surface, the direction of "up" changes. If we ignore this
* fact and leave an object's orientation unchanged as it moves over the globe
* surface, the object will become increasingly tilted and eventually be
* completely upside-down when we arrive at the opposite side of the globe.
* When this parameter is true, this method will automatically apply a
* rotation to the anchor to account for globe curvature when the position on
* the globe changes. This parameter should usually be true, but it may be
* useful to set it to false it when the caller already accounts for globe
* curvature itself, because in that case anchor would be over-rotated.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
*/
void setAnchorToLocalTransform(
const LocalHorizontalCoordinateSystem& localCoordinateSystem,
const glm::dmat4& newAnchorToLocal,
bool adjustOrientation,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
private:
glm::dmat4 _anchorToFixed;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,288 @@
#pragma once
#include "Cartographic.h"
#include "Library.h"
#include <CesiumGeometry/Rectangle.h>
#include <CesiumUtility/Math.h>
#include <optional>
namespace CesiumGeospatial {
/**
* @brief A two-dimensional, rectangular region on a globe, specified using
* longitude and latitude coordinates. The region is rectangular in terms of
* longitude-latitude coordinates, but may be far from rectangular on the actual
* globe surface.
*
* The eastern coordinate may be less than the western coordinate, which
* indicates that the rectangle crosses the anti-meridian.
*
* @see CesiumGeometry::Rectangle
*/
class CESIUMGEOSPATIAL_API GlobeRectangle final {
public:
/**
* @brief An empty rectangle.
*
* The rectangle has the following values:
* * `west`: Pi
* * `south`: Pi/2
* * `east`: -Pi
* * `north`: -Pi/2
*/
static const GlobeRectangle EMPTY;
/**
* @brief The maximum rectangle.
*
* The rectangle has the following values:
* * `west`: -Pi
* * `south`: -Pi/2
* * `east`: Pi
* * `north`: Pi/2
*/
static const GlobeRectangle MAXIMUM;
/**
* @brief Constructs a new instance.
*
* @param west The westernmost longitude, in radians, in the range [-Pi, Pi].
* @param south The southernmost latitude, in radians, in the range [-Pi/2,
* Pi/2].
* @param east The easternmost longitude, in radians, in the range [-Pi, Pi].
* @param north The northernmost latitude, in radians, in the range [-Pi/2,
* Pi/2].
*/
constexpr GlobeRectangle(
double west,
double south,
double east,
double north) noexcept
: _west(west), _south(south), _east(east), _north(north) {}
/**
* Creates a rectangle given the boundary longitude and latitude in degrees.
* The angles are converted to radians.
*
* @param westDegrees The westernmost longitude in degrees in the range
* [-180.0, 180.0].
* @param southDegrees The southernmost latitude in degrees in the range
* [-90.0, 90.0].
* @param eastDegrees The easternmost longitude in degrees in the range
* [-180.0, 180.0].
* @param northDegrees The northernmost latitude in degrees in the range
* [-90.0, 90.0].
* @returns The rectangle.
*
* @snippet TestGlobeRectangle.cpp fromDegrees
*/
static constexpr GlobeRectangle fromDegrees(
double westDegrees,
double southDegrees,
double eastDegrees,
double northDegrees) noexcept {
return GlobeRectangle(
CesiumUtility::Math::degreesToRadians(westDegrees),
CesiumUtility::Math::degreesToRadians(southDegrees),
CesiumUtility::Math::degreesToRadians(eastDegrees),
CesiumUtility::Math::degreesToRadians(northDegrees));
}
/**
* @brief Returns the westernmost longitude, in radians.
*/
constexpr double getWest() const noexcept { return this->_west; }
/**
* @brief Sets the westernmost longitude, in radians.
*/
void setWest(double value) noexcept { this->_west = value; }
/**
* @brief Returns the southernmost latitude, in radians.
*/
constexpr double getSouth() const noexcept { return this->_south; }
/**
* @brief Sets the southernmost latitude, in radians.
*/
void setSouth(double value) noexcept { this->_south = value; }
/**
* @brief Returns the easternmost longitude, in radians.
*/
constexpr double getEast() const noexcept { return this->_east; }
/**
* @brief Sets the easternmost longitude, in radians.
*/
void setEast(double value) noexcept { this->_east = value; }
/**
* @brief Returns the northernmost latitude, in radians.
*/
constexpr double getNorth() const noexcept { return this->_north; }
/**
* @brief Sets the northernmost latitude, in radians.
*/
void setNorth(double value) noexcept { this->_north = value; }
/**
* @brief Returns the {@link Cartographic} position of the south-west corner.
*/
constexpr Cartographic getSouthwest() const noexcept {
return Cartographic(this->_west, this->_south);
}
/**
* @brief Returns the {@link Cartographic} position of the south-east corner.
*/
constexpr Cartographic getSoutheast() const noexcept {
return Cartographic(this->_east, this->_south);
}
/**
* @brief Returns the {@link Cartographic} position of the north-west corner.
*/
constexpr Cartographic getNorthwest() const noexcept {
return Cartographic(this->_west, this->_north);
}
/**
* @brief Returns the {@link Cartographic} position of the north-east corner.
*/
constexpr Cartographic getNortheast() const noexcept {
return Cartographic(this->_east, this->_north);
}
/**
* @brief Returns this rectangle as a {@link CesiumGeometry::Rectangle}.
*/
constexpr CesiumGeometry::Rectangle toSimpleRectangle() const noexcept {
return CesiumGeometry::Rectangle(
this->getWest(),
this->getSouth(),
this->getEast(),
this->getNorth());
}
/**
* @brief Computes the width of this rectangle.
*
* The result will be in radians, in the range [0, Pi*2].
*/
constexpr double computeWidth() const noexcept {
double east = this->_east;
const double west = this->_west;
if (east < west) {
east += CesiumUtility::Math::TwoPi;
}
return east - west;
}
/**
* @brief Computes the height of this rectangle.
*
* The result will be in radians, in the range [0, Pi*2].
*/
constexpr double computeHeight() const noexcept {
return this->_north - this->_south;
}
/**
* @brief Computes the {@link Cartographic} center position of this rectangle.
*/
Cartographic computeCenter() const noexcept;
/**
* @brief Returns `true` if this rectangle contains the given point.
*
* The provided cartographic position must be within the longitude range [-Pi,
* Pi] and the latitude range [-Pi/2, Pi/2].
*
* This will take into account the wrapping of the longitude at the
* anti-meridian.
*/
bool contains(const Cartographic& cartographic) const noexcept;
/**
* @brief Determines if this rectangle is empty.
*
* An empty rectangle bounds no part of the globe, not even a single point.
*/
bool isEmpty() const noexcept;
/**
* @brief Computes the intersection of two rectangles.
*
* This function assumes that the rectangle's coordinates are latitude and
* longitude in radians and produces a correct intersection, taking into
* account the fact that the same angle can be represented with multiple
* values as well as the wrapping of longitude at the anti-meridian. For a
* simple intersection that ignores these factors and can be used with
* projected coordinates, see
* {@link CesiumGeometry::Rectangle::computeIntersection}.
*
* @param other The other rectangle to intersect with this one.
* @returns The intersection rectangle, or `std::nullopt` if there is no
* intersection.
*/
std::optional<GlobeRectangle>
computeIntersection(const GlobeRectangle& other) const noexcept;
/**
* @brief Computes the union of this globe rectangle with another.
*
* @param other The other globe rectangle.
* @return The union.
*/
GlobeRectangle computeUnion(const GlobeRectangle& other) const noexcept;
/**
* @brief Splits this rectangle at the anti-meridian (180 degrees longitude),
* if necessary.
*
* If the rectangle does not cross the anti-meridian, the entire rectangle is
* returned in the `first` field of the pair and the `second` is std::nullopt.
* If it does cross the anti-meridian, this function returns two rectangles
* that touch but do not cross it. The larger of the two rectangles is
* returned in `first` and the smaller one is returned in `second`.
*/
std::pair<GlobeRectangle, std::optional<GlobeRectangle>>
splitAtAntiMeridian() const noexcept;
/**
* @brief Checks whether two globe rectangles are exactly equal.
*
* @param left The first rectangle.
* @param right The second rectangle.
* @return Whether the rectangles are equal
*/
static bool
equals(const GlobeRectangle& left, const GlobeRectangle& right) noexcept;
/**
* @brief Checks whether two globe rectangles are equal up to a given relative
* epsilon.
*
* @param left The first rectangle.
* @param right The second rectangle.
* @param relativeEpsilon The relative epsilon.
* @return Whether the rectangles are epsilon-equal
*/
static bool equalsEpsilon(
const GlobeRectangle& left,
const GlobeRectangle& right,
double relativeEpsilon) noexcept;
private:
double _west;
double _south;
double _east;
double _north;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,39 @@
#pragma once
#include "Ellipsoid.h"
#include "Library.h"
#include <glm/fwd.hpp>
#include <glm/vec3.hpp>
namespace CesiumGeospatial {
/**
* @brief Transforms between globe-related coordinate systems.
*/
class CESIUMGEOSPATIAL_API GlobeTransforms final {
public:
/**
* @brief Computes a transformation from east-north-up axes to an
* ellipsoid-fixed reference frame.
*
* Computes a 4x4 transformation matrix from a reference frame with an
* east-north-up axes centered at the provided origin to the provided
* ellipsoid's fixed reference frame. The local axes are defined as: <ul>
* <li>The `x` axis points in the local east direction.</li>
* <li>The `y` axis points in the local north direction.</li>
* <li>The `z` axis points in the direction of the ellipsoid surface normal
* which passes through the position.</li>
* </ul>
*
* @param origin The center point of the local reference frame.
* @param ellipsoid The {@link Ellipsoid} whose fixed frame is used in the
* transformation. Default value: {@link Ellipsoid::WGS84}.
* @return The transformation matrix
*/
static glm::dmat4x4 eastNorthUpToFixedFrame(
const glm::dvec3& origin,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) noexcept;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,18 @@
#pragma once
/**
* @brief Classes for geospatial computations in Cesium
*
* @mermaid-interactive{dependencies/CesiumGeospatial}
*/
namespace CesiumGeospatial {}
#if defined(_WIN32) && defined(CESIUM_SHARED)
#ifdef CESIUMGEOSPATIAL_BUILDING
#define CESIUMGEOSPATIAL_API __declspec(dllexport)
#else
#define CESIUMGEOSPATIAL_API __declspec(dllimport)
#endif
#else
#define CESIUMGEOSPATIAL_API
#endif

View File

@ -0,0 +1,205 @@
#pragma once
#include "Ellipsoid.h"
#include "Library.h"
#include <glm/mat4x4.hpp>
#include <glm/vec3.hpp>
namespace CesiumGeospatial {
class Cartographic;
}
namespace CesiumGeospatial {
/**
* @brief A local direction, consisting of the four cardinal directions (North,
* South, East, West) combined with Up and Down.
*/
enum class LocalDirection { East, North, West, South, Up, Down };
/**
* @brief A coordinate system created from a local horizontal plane at a
* particular origin point on the globe.
*
* Each principal axis in the new coordinate system can be specified to point in
* a particular compass direction, or it can point up or down. In this way, a
* right-handed or left-handed coordinate system can be created.
*/
class CESIUMGEOSPATIAL_API LocalHorizontalCoordinateSystem {
public:
/**
* @brief Create a new coordinate system centered at a given longitude,
* latitude, and ellipsoidal height.
*
* @param origin The origin of the coordinate system.
* @param xAxisDirection The local direction in which the X axis points at the
* origin.
* @param yAxisDirection The local direction in which the Y axis points at the
* origin.
* @param zAxisDirection The local direction in which the Z axis points at the
* origin.
* @param scaleToMeters Local units are converted to meters by multiplying
* them by this factor. For example, if the local units are centimeters, this
* parameter should be 1.0 / 100.0.
* @param ellipsoid The ellipsoid on which the coordinate system is based.
*/
LocalHorizontalCoordinateSystem(
const Cartographic& origin,
LocalDirection xAxisDirection = LocalDirection::East,
LocalDirection yAxisDirection = LocalDirection::North,
LocalDirection zAxisDirection = LocalDirection::Up,
double scaleToMeters = 1.0,
// Can't use CESIUM_DEFAULT_ELLIPSOID here because of the other default
// parameters
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84);
/**
* @brief Create a new coordinate system centered at a \ref glossary-ecef
* "Earth-Centered, Earth-Fixed" position.
*
* @param originEcef The origin of the coordinate system.
* @param xAxisDirection The local direction in which the X axis points at the
* origin.
* @param yAxisDirection The local direction in which the Y axis points at the
* origin.
* @param zAxisDirection The local direction in which the Z axis points at the
* origin.
* @param scaleToMeters Local units are converted to meters by multiplying
* them by this factor. For example, if the local units are centimeters, this
* parameter should be 1.0 / 100.0.
* @param ellipsoid The ellipsoid on which the coordinate system is based.
*/
LocalHorizontalCoordinateSystem(
const glm::dvec3& originEcef,
LocalDirection xAxisDirection = LocalDirection::East,
LocalDirection yAxisDirection = LocalDirection::North,
LocalDirection zAxisDirection = LocalDirection::Up,
double scaleToMeters = 1.0,
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84);
/**
* @brief Create a new coordinate system with a specified transformation to
* the \ref glossary-ecef "Earth-Centered, Earth-Fixed" frame. This is an
* advanced constructor and should be avoided in most cases.
*
* This constructor can be used to save/restore the state of an instance. It
* can also be used to create unusual coordinate systems that can't be created
* by the other constructors, such as coordinate systems where the axes aren't
* aligned with compass directions.
*
* @param localToEcef The transformation matrix from this coordinate system to
* the ECEF frame.
*/
explicit LocalHorizontalCoordinateSystem(const glm::dmat4& localToEcef);
/**
* @brief Create a new coordinate system with the specified transformations
* between the local frame and the
* \ref glossary-ecef "Earth-Centered, Earth-Fixed" frame. This is an advanced
* constructor and should be avoided in most cases.
*
* This constructor can be used to save/restore the state of an instance. It
* can also be used to create unusual coordinate systems that can't be created
* by the other constructors, such as coordinate systems where the axes aren't
* aligned with compass directions.
*
* @param localToEcef The transformation matrix from this coordinate system to
* the ECEF frame. This must be the inverse of `ecefToLocal` or behavior is
* undefined, but this is not enforced.
* @param ecefToLocal The transformation matrix from the ECEF frame to this
* coordinate system. This must be the inverse of `localToEcef` or behavior is
* undefined, but this is not enforced.
*/
LocalHorizontalCoordinateSystem(
const glm::dmat4& localToEcef,
const glm::dmat4& ecefToLocal);
/**
* @brief Gets the transformation matrix from the local horizontal coordinate
* system managed by this instance to the \ref glossary-ecef.
*
* @return The transformation.
*/
const glm::dmat4& getLocalToEcefTransformation() const noexcept {
return this->_localToEcef;
}
/**
* @brief Gets the transformation matrix from \ref glossary-ecef to the
* local horizontal coordinate system managed by this instance.
*
* @return The transformation.
*/
const glm::dmat4& getEcefToLocalTransformation() const noexcept {
return this->_ecefToLocal;
};
/**
* @brief Converts a position in the local horizontal coordinate system
* managed by this instance to
* \ref glossary-ecef "Earth-Centered, Earth-Fixed (ECEF)".
*
* @param localPosition The position in the local coordinate system.
* @return The equivalent position in the ECEF coordinate system.
*/
glm::dvec3
localPositionToEcef(const glm::dvec3& localPosition) const noexcept;
/**
* @brief Converts a position in the
* \ref glossary-ecef "Earth-Centered, Earth-Fixed (ECEF)" coordinate system
* to the local horizontal coordinate system managed by this instance.
*
* @param ecefPosition The position in the ECEF coordinate system.
* @return The equivalent position in the local coordinate system.
*/
glm::dvec3 ecefPositionToLocal(const glm::dvec3& ecefPosition) const noexcept;
/**
* @brief Converts a direction in the local horizontal coordinate system
* managed by this instance to
* \ref glossary-ecef "Earth-Centered, Earth-Fixed (ECEF)".
*
* Because the vector is treated as a direction only, the translation portion
* of the transformation is ignored.
*
* @param localDirection The direction in the local coordinate system.
* @return The equivalent direction in the ECEF coordinate system.
*/
glm::dvec3
localDirectionToEcef(const glm::dvec3& localDirection) const noexcept;
/**
* @brief Converts a direction in the
* \ref glossary-ecef "Earth-Centered, Earth-Fixed (ECEF)" coordinate system
* to the local horizontal coordinate system managed by this instance.
*
* Because the vector is treated as a direction only, the translation portion
* of the transformation is ignored.
*
* @param ecefDirection The direction in the ECEF coordinate system.
* @return The equivalent direction in the local coordinate system.
*/
glm::dvec3
ecefDirectionToLocal(const glm::dvec3& ecefDirection) const noexcept;
/**
* @brief Computes the transformation matrix from this local horizontal
* coordinate system to another one. For example, if the returned matrix is
* multiplied with a vector representing a position in this coordinate system,
* the result will be a new vector representing the position in the target
* coordinate system.
*
* @param target The other local horizontal coordinate system.
* @return The transformation.
*/
glm::dmat4 computeTransformationToAnotherLocal(
const LocalHorizontalCoordinateSystem& target) const noexcept;
private:
glm::dmat4 _ecefToLocal;
glm::dmat4 _localToEcef;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,140 @@
#pragma once
#include "BoundingRegion.h"
#include "CesiumGeometry/AxisAlignedBox.h"
#include "CesiumGeometry/Rectangle.h"
#include "Ellipsoid.h"
#include "GeographicProjection.h"
#include "WebMercatorProjection.h"
#include <glm/vec2.hpp>
#include <variant>
namespace CesiumGeospatial {
/**
* @brief A projection.
*
* This is a `std::variant` of different types of map projections that
* can convert between projected map coordinates and Cartographic coordinates.
*
* @see GeographicProjection
* @see WebMercatorProjection
*/
typedef std::variant<GeographicProjection, WebMercatorProjection> Projection;
/**
* @brief Projects a position on the globe using the given {@link Projection}.
*
* @param projection The projection.
* @param position The {@link Cartographic} position.
* @return The coordinates of the projected point, in the coordinate system
* of the given projection.
*/
glm::dvec3
projectPosition(const Projection& projection, const Cartographic& position);
/**
* @brief Unprojects a position from the globe using the given
* {@link Projection}.
*
* @param projection The projection.
* @param position The coordinates of the point, in meters.
* @return The {@link Cartographic} position.
*/
Cartographic
unprojectPosition(const Projection& projection, const glm::dvec3& position);
/**
* @brief Projects a rectangle on the globe by simply projecting its four
* corners.
*
* This is only accurate when the globe rectangle is still a rectangle after
* projecting, which is true for {@link WebMercatorProjection} but not
* necessarily true for other projections.
*
* @param projection The projection.
* @param rectangle The globe rectangle to be projected.
* @return The projected rectangle.
*/
CesiumGeometry::Rectangle projectRectangleSimple(
const Projection& projection,
const GlobeRectangle& rectangle);
/**
* @brief Unprojects a rectangle to the globe by simply unprojecting its four
* corners.
*
* This is only accurate when the rectangle is still a rectangle after
* unprojecting, which is true for {@link WebMercatorProjection} but not
* necessarily true for other projections.
*
* @param projection The projection.
* @param rectangle The rectangle to be unprojected.
* @return The unprojected rectangle.
*/
GlobeRectangle unprojectRectangleSimple(
const Projection& projection,
const CesiumGeometry::Rectangle& rectangle);
/**
* @brief Projects a bounding region on the globe by simply projecting its
* eight corners.
*
* This is only accurate when the globe box is still a box after
* projecting, which is true for {@link WebMercatorProjection} but not
* necessarily true for other projections.
*
* @param projection The projection.
* @param region The bounding region to be projected.
* @return The projected box.
*/
CesiumGeometry::AxisAlignedBox
projectRegionSimple(const Projection& projection, const BoundingRegion& region);
/**
* @brief Unprojects a box to the globe by simply unprojecting its eight
* corners.
*
* This is only accurate when the box is still a box after
* unprojecting, which is true for {@link WebMercatorProjection} but not
* necessarily true for other projections.
*
* @param projection The projection.
* @param box The box to be unprojected.
* @param ellipsoid The {@link CesiumGeospatial::Ellipsoid}.
* @return The unprojected bounding region.
*/
BoundingRegion unprojectRegionSimple(
const Projection& projection,
const CesiumGeometry::AxisAlignedBox& box,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Computes the approximate real-world size, in meters, of a given
* projected rectangle.
*
* The returned X component corresponds to the size in the projected X
* direction, while the returned Y component corresponds to the size in the
* projected Y direction.
*
* @param projection The projection.
* @param rectangle The projected rectangle to measure.
* @param maxHeight The maximum height of the geometry inside the rectangle.
* @param ellipsoid The ellipsoid used to convert longitude and latitude to
* ellipsoid-centered coordinates.
* @return The approximate size.
*/
glm::dvec2 computeProjectedRectangleSize(
const Projection& projection,
const CesiumGeometry::Rectangle& rectangle,
double maxHeight,
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Obtains the ellipsoid used by a Projection variant.
*/
const Ellipsoid& getProjectionEllipsoid(const Projection& projection);
} // namespace CesiumGeospatial

View File

@ -0,0 +1,118 @@
#pragma once
#include "BoundingRegion.h"
#include "Ellipsoid.h"
#include "S2CellID.h"
#include <CesiumGeometry/CullingResult.h>
#include <CesiumGeometry/Plane.h>
#include <glm/vec3.hpp>
#include <array>
#include <span>
#include <string_view>
namespace CesiumGeospatial {
/**
* A tile bounding volume specified as an S2 cell token with minimum and maximum
* heights. The bounding volume is a k DOP. A k-DOP is the Boolean intersection
* of extents along k directions.
*/
class CESIUMGEOSPATIAL_API S2CellBoundingVolume final {
public:
/** @brief Creates a new \ref S2CellBoundingVolume.
*
* @param cellID The S2 cell ID.
* @param minimumHeight The minimum height of the bounding volume.
* @param maximumHeight The maximum height of the bounding volume.
* @param ellipsoid The ellipsoid.
*/
S2CellBoundingVolume(
const S2CellID& cellID,
double minimumHeight,
double maximumHeight,
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);
/**
* @brief Gets this bounding volume's cell ID.
*/
const S2CellID& getCellID() const { return this->_cellID; }
/**
* @brief Gets the minimum height of the cell.
*/
double getMinimumHeight() const noexcept { return this->_minimumHeight; }
/**
* @brief Gets the maximum height of the cell.
*/
double getMaximumHeight() const noexcept { return this->_maximumHeight; }
/**
* @brief Gets the center of this bounding volume in ellipsoid-fixed (ECEF)
* coordinates.
*/
glm::dvec3 getCenter() const noexcept;
/**
* @brief Gets the either corners of the bounding volume, in ellipsoid-fixed
* (ECEF) coordinates.
*
* @return An array of positions with a `size()` of 8.
*/
std::span<const glm::dvec3> getVertices() const noexcept;
/**
* @brief Determines on which side of a plane the bounding volume is located.
*
* @param plane The plane to test against.
* @return The {@link CesiumGeometry::CullingResult}
* * `Inside` if the entire region is on the side of the plane the normal is
* pointing.
* * `Outside` if the entire region is on the opposite side.
* * `Intersecting` if the region intersects the plane.
*/
CesiumGeometry::CullingResult
intersectPlane(const CesiumGeometry::Plane& plane) const noexcept;
/**
* @brief Computes the distance squared from a given position to the closest
* point on this bounding volume. The position must be expressed in
* ellipsoid-centered (ECEF) coordinates.
*
* @param position The position
* @return The estimated distance squared from the bounding box to the point.
*
* @snippet TestOrientedBoundingBox.cpp distanceSquaredTo
*/
double
computeDistanceSquaredToPosition(const glm::dvec3& position) const noexcept;
/**
* @brief Gets the six planes that bound the volume.
*
* @return An array of planes with a `size()` of 6.
*/
std::span<const CesiumGeometry::Plane> getBoundingPlanes() const noexcept;
/**
* @brief Computes the bounding begion that best fits this S2 cell volume.
*
* @return The bounding region.
*/
BoundingRegion
computeBoundingRegion(const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) const noexcept;
private:
S2CellID _cellID;
double _minimumHeight;
double _maximumHeight;
glm::dvec3 _center;
std::array<CesiumGeometry::Plane, 6> _boundingPlanes;
std::array<glm::dvec3, 8> _vertices;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,173 @@
#pragma once
#include "Cartographic.h"
#include "GlobeRectangle.h"
#include "Library.h"
#include <array>
#include <cstdint>
#include <string>
#include <string_view>
namespace CesiumGeometry {
struct QuadtreeTileID;
}
namespace CesiumGeospatial {
/**
* @brief A 64-bit unsigned integer that uniquely identifies a
* cell in the S2 cell decomposition.
*
* It has the following format:
*
* id = [face][face_pos]
*
* face: a 3-bit number (range 0..5) encoding the cube face.
*
* face_pos: a 61-bit number encoding the position of the center of this
* cell along the Hilbert curve over this face (see the Wiki
* pages for details).
*
* Sequentially increasing cell ids follow a continuous space-filling curve
* over the entire sphere. They have the following properties:
*
* - The id of a cell at level k consists of a 3-bit face number followed
* by k bit pairs that recursively select one of the four children of
* each cell. The next bit is always 1, and all other bits are 0.
* Therefore, the level of a cell is determined by the position of its
* lowest-numbered bit that is turned on (for a cell at level k, this
* position is 2 * (kMaxLevel - k).)
*
* - The id of a parent cell is at the midpoint of the range of ids spanned
* by its children (or by its descendants at any level).
*
* This class is adapted from S2CellId in https://github.com/google/s2geometry.
*/
class CESIUMGEOSPATIAL_API S2CellID {
public:
/**
* Creates a new S2Cell from a token. A token is a hexadecimal representation
* of the 64-bit S2CellID.
*
* @param token The token for the S2 Cell.
* @returns A new S2CellID.
*/
static S2CellID fromToken(const std::string_view& token);
/**
* @brief Creates a cell given its face (range 0..5), level, and Hilbert curve
* cell index within that face and level.
*
* @param face The face index.
* @param level The level within the face.
* @param position The Hilbert-order index of the cell within the face and
* level.
* @return The cell.
*/
static S2CellID
fromFaceLevelPosition(uint8_t face, uint32_t level, uint64_t position);
/**
* @brief Create an S2 id from a face and a quadtree tile id.
*
* @param face The S2 face (0...5) that this tile is on.
* @param quadtreeTileID The quadtree tile id for this tile, within the given
* face.
*/
static S2CellID fromQuadtreeTileID(
uint8_t face,
const CesiumGeometry::QuadtreeTileID& quadtreeTileID);
/**
* @brief Constructs a new S2 cell ID.
*
* The cell ID value is not validated. Use {@link isValid} to check the
* validity after constructions.
*
* @param id The 64-bit cell ID value.
*/
S2CellID(uint64_t id);
/**
* @brief Determines if this cell ID is valid.
*
* @return true if the the cell ID refers to a valid cell; otherwise, false.
*/
bool isValid() const;
/**
* @brief Gets the ID of the cell.
*
* @return The ID.
*/
uint64_t getID() const { return this->_id; }
/**
* @brief Converts the cell ID to a hexadecimal token.
*/
std::string toToken() const;
/**
* @brief Gets the level of the cell from the cell ID.
*
* @return The cell ID, where 0 is the root.
*/
int32_t getLevel() const;
/**
* @brief Gets the face id (0...5) for this S2 cell.
*/
uint8_t getFace() const;
/**
* @brief Gets the longitude/latitude position at the center of this cell.
*
* The height is always 0.0.
*
* @return The center.
*/
Cartographic getCenter() const;
/**
* @brief Gets the vertices at the corners of the cell.
*
* The height values are always 0.0.
*
* Note that all positions inside the S2 Cell are _not_ guaranteed to fall
* inside the rectangle formed by these vertices.
*
* @return Four vertices specifying the corners of this cell.
*/
std::array<Cartographic, 4> getVertices() const;
/**
* @brief Gets the parent cell of this cell.
*
* If this is a root cell, the behavior is unspecified.
*/
S2CellID getParent() const;
/**
* @brief Gets a child cell of this cell.
*
* If the index is less than 0 or greater than 3, or if this is a leaf cell,
* the behavior is unspecified.
*
* @param index The index in the range 0 to 3.
* @return The child cell.
*/
S2CellID getChild(size_t index) const;
/**
* @brief Computes the globe rectangle that bounds this cell.
*
* @return The globe rectangle.
*/
GlobeRectangle computeBoundingRectangle() const;
private:
uint64_t _id;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,102 @@
#pragma once
#include "Library.h"
#include <CesiumGeospatial/Ellipsoid.h>
#include <glm/vec3.hpp>
#include <optional>
namespace CesiumGeospatial {
/**
* @brief Produces points on an ellipse that lies on a plane that intersects the
* center of the earth and each of the input coordinates. The height above the
* surface at each point along the curve will be a linear interpolation between
* the source and destination heights.
*/
class CESIUMGEOSPATIAL_API SimplePlanarEllipsoidCurve final {
public:
/**
* @brief Creates a new instance of {@link SimplePlanarEllipsoidCurve} from a
* source and destination specified in Earth-Centered, Earth-Fixed
* coordinates.
*
* @param ellipsoid The ellipsoid that the source and destination positions
* are relative to.
* @param sourceEcef The position that the path will begin at in ECEF
* coordinates.
* @param destinationEcef The position that the path will end at in ECEF
* coordinates.
*
* @returns An optional type containing a {@link SimplePlanarEllipsoidCurve}
* object representing the generated path, if possible. If it wasn't possible
* to scale the input coordinates to geodetic surface coordinates on a WGS84
* ellipsoid, this will return `std::nullopt` instead.
*/
static std::optional<SimplePlanarEllipsoidCurve>
fromEarthCenteredEarthFixedCoordinates(
const Ellipsoid& ellipsoid,
const glm::dvec3& sourceEcef,
const glm::dvec3& destinationEcef);
/**
* @brief Creates a new instance of {@link SimplePlanarEllipsoidCurve} from a
* source and destination specified in cartographic coordinates (Longitude,
* Latitude, and Height).
*
* @param ellipsoid The ellipsoid that these cartographic coordinates are
* from.
* @param source The position that the path will begin at in Longitude,
* Latitude, and Height.
* @param destination The position that the path will end at in Longitude,
* Latitude, and Height.
*
* @returns An optional type containing a {@link SimplePlanarEllipsoidCurve}
* object representing the generated path, if possible. If it wasn't possible
* to scale the input coordinates to geodetic surface coordinates on a WGS84
* ellipsoid, this will return std::nullopt instead.
*/
static std::optional<SimplePlanarEllipsoidCurve> fromLongitudeLatitudeHeight(
const Ellipsoid& ellipsoid,
const Cartographic& source,
const Cartographic& destination);
/**
* @brief Samples the curve at the given percentage of its length.
*
* @param percentage The percentage of the curve's length to sample at,
* where 0 is the beginning and 1 is the end. This value will be clamped to
* the range [0..1].
* @param additionalHeight The height above the earth at this position will be
* calculated by interpolating between the height at the beginning and end of
* the curve based on the value of \p percentage. This parameter specifies an
* additional offset to add to the height.
*
* @returns The position of the given point on this curve in Earth-Centered
* Earth-Fixed coordinates.
*/
glm::dvec3
getPosition(double percentage, double additionalHeight = 0.0) const;
private:
SimplePlanarEllipsoidCurve(
const Ellipsoid& ellipsoid,
const glm::dvec3& scaledSourceEcef,
const glm::dvec3& scaledDestinationEcef,
const glm::dvec3& originalSourceEcef,
const glm::dvec3& originalDestinationEcef);
double _totalAngle;
double _sourceHeight;
double _destinationHeight;
Ellipsoid _ellipsoid;
glm::dvec3 _sourceDirection;
glm::dvec3 _rotationAxis;
glm::dvec3 _sourceEcef;
glm::dvec3 _destinationEcef;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,176 @@
#pragma once
#include "Ellipsoid.h"
#include "GlobeRectangle.h"
#include "Library.h"
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace CesiumGeospatial {
class Cartographic;
/**
* @brief The map projection used by Google Maps, Bing Maps, and most of ArcGIS
* Online, EPSG:3857.
*
* This projection uses geodetic longitude and latitude expressed with WGS84 and
* transforms them to Mercator using the spherical (rather than ellipsoidal)
* equations.
*
* @see GeographicProjection
*/
class CESIUMGEOSPATIAL_API WebMercatorProjection final {
public:
/**
* @brief The maximum latitude (both North and South) supported by a Web
* Mercator (EPSG:3857) projection.
*
* Technically, the Mercator projection is defined for any latitude
* up to (but not including) 90 degrees, but it makes sense
* to cut it off sooner because it grows exponentially with increasing
* latitude. The logic behind this particular cutoff value, which is the one
* used by Google Maps, Bing Maps, and Esri, is that it makes the projection
* square. That is, the rectangle is equal in the X and Y directions.
*
* The constant value is computed by calling:
* `CesiumGeospatial::WebMercatorProjection::mercatorAngleToGeodeticLatitude(CesiumUtility::Math::OnePi)`
*/
static const double MAXIMUM_LATITUDE;
/**
* @brief The maximum bounding rectangle of the Web Mercator projection,
* ranging from -PI to PI radians longitude and
* from -MAXIMUM_LATITUDE to +MAXIMUM_LATITUDE.
*/
static const GlobeRectangle MAXIMUM_GLOBE_RECTANGLE;
/**
* @brief Computes the maximum rectangle that can be covered with this
* projection
*
* @param ellipsoid The {@link Ellipsoid}. Default value:
* {@link Ellipsoid::WGS84}.
* @return The rectangle
*/
static constexpr CesiumGeometry::Rectangle computeMaximumProjectedRectangle(
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) noexcept {
const double value =
ellipsoid.getMaximumRadius() * CesiumUtility::Math::OnePi;
return CesiumGeometry::Rectangle(-value, -value, value, value);
}
/**
* @brief Constructs a new instance.
*
* @param ellipsoid The {@link Ellipsoid}.
*/
WebMercatorProjection(
const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID) noexcept;
/**
* @brief Gets the {@link Ellipsoid}.
*/
const Ellipsoid& getEllipsoid() const noexcept { return this->_ellipsoid; }
/**
* @brief Converts geodedic ellipsoid coordinates to Web Mercator coordinates.
*
* Converts geodetic ellipsoid coordinates, in radians, to the equivalent Web
* Mercator X, Y, Z coordinates expressed in meters. The height is copied
* unmodified to the `z` coordinate.
*
* @param cartographic The geodetic coordinates in radians.
* @returns The equivalent web mercator X, Y, Z coordinates, in meters.
*/
glm::dvec3 project(const Cartographic& cartographic) const noexcept;
/**
* @brief Projects a globe rectangle to Web Mercator coordinates.
*
* This is done by projecting the southwest and northeast corners.
*
* @param rectangle The globe rectangle to project.
* @return The projected rectangle.
*/
CesiumGeometry::Rectangle
project(const CesiumGeospatial::GlobeRectangle& rectangle) const noexcept;
/**
* @brief Converts Web Mercator coordinates to geodetic ellipsoid coordinates.
*
* Converts Web Mercator X and Y coordinates, expressed in meters, to a
* {@link Cartographic} containing geodetic ellipsoid coordinates.
* The height is set to 0.0.
*
* @param projectedCoordinates The web mercator projected coordinates to
* unproject.
* @returns The equivalent cartographic coordinates.
*/
Cartographic unproject(const glm::dvec2& projectedCoordinates) const noexcept;
/**
* @brief Converts Web Mercator coordinates to geodetic ellipsoid coordinates.
*
* Converts Web Mercator X, Y coordinates, expressed in meters, to a
* {@link Cartographic} containing geodetic ellipsoid coordinates.
* The Z coordinate is copied unmodified to the height.
*
* @param projectedCoordinates The web mercator projected coordinates to
* unproject, with height (z) in meters.
* @returns The equivalent cartographic coordinates.
*/
Cartographic unproject(const glm::dvec3& projectedCoordinates) const noexcept;
/**
* @brief Unprojects a Web Mercator rectangle to the globe.
*
* This is done by unprojecting the southwest and northeast corners.
*
* @param rectangle The rectangle to unproject.
* @returns The unprojected rectangle.
*/
CesiumGeospatial::GlobeRectangle
unproject(const CesiumGeometry::Rectangle& rectangle) const noexcept;
/**
* @brief Converts a Mercator angle, in the range -PI to PI, to a geodetic
* latitude in the range -PI/2 to PI/2.
*
* @param mercatorAngle The angle to convert.
* @returns The geodetic latitude in radians.
*/
static double mercatorAngleToGeodeticLatitude(double mercatorAngle) noexcept;
/**
* @brief Converts a geodetic latitude in radians, in the range -PI/2 to PI/2,
* to a Mercator angle in the range -PI to PI.
*
* @param latitude The geodetic latitude in radians.
* @returns The Mercator angle.
*/
static double geodeticLatitudeToMercatorAngle(double latitude) noexcept;
/**
* @brief Returns `true` if two projections (i.e. their ellipsoids) are equal.
*/
bool operator==(const WebMercatorProjection& rhs) const noexcept {
return this->_ellipsoid == rhs._ellipsoid;
};
/**
* @brief Returns `true` if two projections (i.e. their ellipsoids) are *not*
* equal.
*/
bool operator!=(const WebMercatorProjection& rhs) const noexcept {
return !(*this == rhs);
};
private:
Ellipsoid _ellipsoid;
double _semimajorAxis;
double _oneOverSemimajorAxis;
};
} // namespace CesiumGeospatial

View File

@ -0,0 +1,24 @@
#pragma once
#include "Ellipsoid.h"
#include <CesiumGeometry/QuadtreeTilingScheme.h>
namespace CesiumGeospatial {
/**
* @brief Computes the maximum geometric error per radian of a quadtree with
* certain assumptions.
*
* The geometric error for a tile can be computed by multiplying the value
* returned by this function by the width of the tile in radians.
*
* This function computes a suitable geometric error for a 65x65 terrain
* heightmap where the vertical error is 25% of the horizontal spacing between
* height samples at the equator.
*
* @param ellipsoid The ellipsoid.
* @return The max geometric error.
*/
double calcQuadtreeMaxGeometricError(
const CesiumGeospatial::Ellipsoid& ellipsoid) noexcept;
} // namespace CesiumGeospatial