985 lines
32 KiB
Plaintext
985 lines
32 KiB
Plaintext
|
/* -*-c++-*- */
|
||
|
/* osgEarth - Geospatial SDK for OpenSceneGraph
|
||
|
* Copyright 2020 Pelican Mapping
|
||
|
* http://osgearth.org
|
||
|
*
|
||
|
* osgEarth is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||
|
*/
|
||
|
#pragma once
|
||
|
|
||
|
#include <osgEarth/Common>
|
||
|
#include <osgEarth/GeoCommon>
|
||
|
#include <osgEarth/Bounds>
|
||
|
#include <osgEarth/SpatialReference>
|
||
|
#include <osgEarth/ImageUtils>
|
||
|
#include <osgEarth/Status>
|
||
|
#include <osgEarth/Threading>
|
||
|
|
||
|
#include <osg/Referenced>
|
||
|
#include <osg/Image>
|
||
|
#include <osg/Shape>
|
||
|
|
||
|
#include <unordered_map>
|
||
|
|
||
|
namespace osg
|
||
|
{
|
||
|
class Camera;
|
||
|
class Polytope;
|
||
|
}
|
||
|
|
||
|
namespace osgEarth
|
||
|
{
|
||
|
class TerrainResolver;
|
||
|
class GeoExtent;
|
||
|
|
||
|
/**
|
||
|
* A georeferenced 3D point.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoPoint
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint.
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
double x,
|
||
|
double y,
|
||
|
double z,
|
||
|
const AltitudeMode& mode );
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint with an absolute Z.
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
double x,
|
||
|
double y,
|
||
|
double z );
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint with X and Y coordinates. The Z defaults
|
||
|
* to zero with an ALTMODE_RELATIVE altitude mode (i.e., 0 meters
|
||
|
* above the terrain).
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
double x,
|
||
|
double y );
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint from a vec3.
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
const osg::Vec3d& xyz,
|
||
|
const AltitudeMode& mode );
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint from a vec3 with absolute Z.
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
const osg::Vec3d& xyz);
|
||
|
|
||
|
/**
|
||
|
* Constructs a new GeoPoint by transforming an existing GeoPoint into
|
||
|
* the specified spatial reference.
|
||
|
*/
|
||
|
GeoPoint(
|
||
|
const SpatialReference* srs,
|
||
|
const GeoPoint& rhs );
|
||
|
|
||
|
/**
|
||
|
* Copy constructor
|
||
|
*/
|
||
|
GeoPoint(const GeoPoint& rhs);
|
||
|
|
||
|
/**
|
||
|
* Constructs an empty (and invalid) geopoint.
|
||
|
*/
|
||
|
GeoPoint();
|
||
|
|
||
|
/**
|
||
|
* Constructs a GeoPoint at 0,0,0 absolute.
|
||
|
*/
|
||
|
GeoPoint(const SpatialReference* srs);
|
||
|
|
||
|
/**
|
||
|
* Constructs a geopoint from serialization
|
||
|
*/
|
||
|
GeoPoint( const Config& conf, const SpatialReference* srs =0L );
|
||
|
|
||
|
/** dtor */
|
||
|
virtual ~GeoPoint() { }
|
||
|
|
||
|
/**
|
||
|
* Sets the SRS and coords
|
||
|
*/
|
||
|
void set(
|
||
|
const SpatialReference* srs,
|
||
|
const osg::Vec3d& xyz,
|
||
|
const AltitudeMode& mode );
|
||
|
|
||
|
void set(
|
||
|
const SpatialReference* srs,
|
||
|
double x,
|
||
|
double y,
|
||
|
double z,
|
||
|
const AltitudeMode& mode );
|
||
|
|
||
|
// component getter/setters
|
||
|
|
||
|
double& x() { return _p.x(); }
|
||
|
double x() const { return _p.x(); }
|
||
|
|
||
|
double& y() { return _p.y(); }
|
||
|
double y() const { return _p.y(); }
|
||
|
|
||
|
double& z() { return _p.z(); }
|
||
|
double z() const { return _p.z(); }
|
||
|
|
||
|
double& alt() { return _p.z(); }
|
||
|
double alt() const { return _p.z(); }
|
||
|
|
||
|
osg::Vec3d& vec3d() { return _p; }
|
||
|
const osg::Vec3d& vec3d() const { return _p; }
|
||
|
|
||
|
const SpatialReference* getSRS() const { return _srs.get(); }
|
||
|
|
||
|
/**
|
||
|
* AltitudeMode reflects whether the Z coordinate is absolute with
|
||
|
* respect to MSL or relative to the terrain elevation at that
|
||
|
* point. When using relative points, GeoPoint usually requires
|
||
|
* access to a TerrainProvider in order to resolve the altitude.
|
||
|
*/
|
||
|
AltitudeMode& altitudeMode() { return _altMode; }
|
||
|
const AltitudeMode& altitudeMode() const { return _altMode; }
|
||
|
bool isRelative() const { return _altMode == ALTMODE_RELATIVE; }
|
||
|
bool isAbsolute() const { return _altMode == ALTMODE_ABSOLUTE; }
|
||
|
|
||
|
/**
|
||
|
* Returns a copy of this geopoint transformed into another SRS.
|
||
|
*/
|
||
|
GeoPoint transform(const SpatialReference* outSRS) const;
|
||
|
|
||
|
/**
|
||
|
* Transforms this geopoint into another SRS.
|
||
|
*/
|
||
|
bool transform(const SpatialReference* outSRS, GeoPoint& output) const;
|
||
|
|
||
|
/**
|
||
|
* Transforms this point in place to another SRS.
|
||
|
*/
|
||
|
bool transformInPlace(const SpatialReference* srs);
|
||
|
|
||
|
/**
|
||
|
* Transforms this geopoint's Z coordinate (in place)
|
||
|
*/
|
||
|
bool transformZ(const AltitudeMode& altMode, const TerrainResolver* t );
|
||
|
|
||
|
/**
|
||
|
* Transforms and returns the geopoints Z coordinate.
|
||
|
*/
|
||
|
bool transformZ(const AltitudeMode& altMode, const TerrainResolver* t, double& out_z) const;
|
||
|
|
||
|
//! Transforms a resolution distance to a cartesian value.
|
||
|
Distance transformResolution(const Distance& d, const UnitsType& outUnits) const;
|
||
|
|
||
|
/**
|
||
|
* Transforms this geopoint's Z to be absolute w.r.t. the vertical datum
|
||
|
*/
|
||
|
bool makeAbsolute(const TerrainResolver* t) { return transformZ(ALTMODE_ABSOLUTE, t); }
|
||
|
|
||
|
/**
|
||
|
* Transforms this geopoint's Z to be terrain-relative.
|
||
|
*/
|
||
|
bool makeRelative(const TerrainResolver* t) { return transformZ(ALTMODE_RELATIVE, t); }
|
||
|
|
||
|
/**
|
||
|
* Transforms this GeoPoint to geographic (lat/long) coords in place.
|
||
|
*/
|
||
|
bool makeGeographic();
|
||
|
|
||
|
/**
|
||
|
* Outputs world coordinates corresponding to this point. If the point
|
||
|
* is ALTMODE_RELATIVE, this will fail because there's no way to resolve
|
||
|
* the actual Z coordinate. Use the variant of toWorld that takes a
|
||
|
* Terrain* instead.
|
||
|
*/
|
||
|
bool toWorld( osg::Vec3d& out_world ) const;
|
||
|
|
||
|
/**
|
||
|
* Outputs world coordinates corresponding to this point, passing in a Terrain
|
||
|
* object that will be used if the point needs to be converted to absolute
|
||
|
* altitude
|
||
|
*/
|
||
|
bool toWorld( osg::Vec3d& out_world, const TerrainResolver* terrain ) const;
|
||
|
|
||
|
/**
|
||
|
* Converts world coordinates into a geopoint
|
||
|
*/
|
||
|
bool fromWorld(const SpatialReference* srs, const osg::Vec3d& world);
|
||
|
|
||
|
/**
|
||
|
* geopoint into absolute world coords.
|
||
|
*/
|
||
|
bool createLocalToWorld( osg::Matrixd& out_local2world ) const;
|
||
|
|
||
|
/**
|
||
|
* Outputs a matrix that will transform absolute world coordiantes so they are
|
||
|
* localized into a local tangent place around this geopoint.
|
||
|
*/
|
||
|
bool createWorldToLocal( osg::Matrixd& out_world2local ) const;
|
||
|
|
||
|
/**
|
||
|
* Converts this point to the same point in a local tangent plane.
|
||
|
*/
|
||
|
GeoPoint toLocalTangentPlane() const;
|
||
|
|
||
|
/**
|
||
|
* Outputs an "up" vector corresponding to the given point. The up vector
|
||
|
* is orthogonal to a local tangent plane at that point on the map.
|
||
|
*/
|
||
|
bool createWorldUpVector( osg::Vec3d& out_up ) const;
|
||
|
|
||
|
//! Geodesic distance from this point to another.
|
||
|
//! This is the distance along the real-world ellipsoidal surface
|
||
|
//! of the Earth (or other body), regardless of map projection.
|
||
|
//! It does not account for Z/altitude.
|
||
|
//! @param rhs Other point
|
||
|
//! @return Geodesic distance between the two points
|
||
|
Distance geodesicDistanceTo(const GeoPoint& rhs) const;
|
||
|
|
||
|
/**
|
||
|
* @deprecated - ambiguous, will be removed. Use geodesicDistanceTo() or toWorld()/length instead.
|
||
|
* Calculates the distance in meters from this geopoint to another.
|
||
|
*/
|
||
|
double distanceTo(const GeoPoint& rhs) const;
|
||
|
|
||
|
/**
|
||
|
* Interpolates a point between this point and another point
|
||
|
* using the parameter t [0..1].
|
||
|
*/
|
||
|
GeoPoint interpolate(const GeoPoint& rhs, double t) const;
|
||
|
|
||
|
//! Convert this point to screen coordinates.
|
||
|
osg::Vec2d toScreen(const osg::Camera* camera) const;
|
||
|
|
||
|
//! Convenience function to return xy units
|
||
|
const UnitsType& getXYUnits() const;
|
||
|
|
||
|
bool operator == (const GeoPoint& rhs) const;
|
||
|
bool operator != (const GeoPoint& rhs) const { return !operator==(rhs); }
|
||
|
bool isValid() const { return _srs.valid(); }
|
||
|
|
||
|
Config getConfig() const;
|
||
|
|
||
|
/**
|
||
|
* Represent this point as a string
|
||
|
*/
|
||
|
std::string toString() const;
|
||
|
|
||
|
public:
|
||
|
static GeoPoint INVALID;
|
||
|
|
||
|
protected:
|
||
|
osg::Vec3d _p;
|
||
|
osg::ref_ptr<const SpatialReference> _srs;
|
||
|
AltitudeMode _altMode;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A simple circular bounding area consiting of a GeoPoint and a linear radius.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoCircle
|
||
|
{
|
||
|
public:
|
||
|
/** Construct an INVALID GeoCircle */
|
||
|
GeoCircle();
|
||
|
|
||
|
/** Copy another GoeCircle */
|
||
|
GeoCircle(const GeoCircle& rhs);
|
||
|
|
||
|
/** Construct a new GeoCircle */
|
||
|
GeoCircle(
|
||
|
const GeoPoint& center,
|
||
|
double radius );
|
||
|
|
||
|
virtual ~GeoCircle() { }
|
||
|
|
||
|
/** The center point of the circle */
|
||
|
const GeoPoint& getCenter() const { return _center; }
|
||
|
void setCenter( const GeoPoint& value ) { _center = value; }
|
||
|
|
||
|
/** Circle's radius, in linear map units (or meters for a geographic SRS) */
|
||
|
double getRadius() const { return _radius; }
|
||
|
void setRadius( double value ) { _radius = value; }
|
||
|
|
||
|
/** SRS of the center point */
|
||
|
const SpatialReference* getSRS() const { return _center.getSRS(); }
|
||
|
|
||
|
/** equality test */
|
||
|
bool operator == ( const GeoCircle& rhs ) const;
|
||
|
|
||
|
/** inequality test */
|
||
|
bool operator != ( const GeoCircle& rhs ) const { return !operator==(rhs); }
|
||
|
|
||
|
/** validity test */
|
||
|
bool isValid() const { return _center.isValid() && _radius > 0.0; }
|
||
|
|
||
|
/** transform the GeoCircle to another SRS */
|
||
|
GeoCircle transform( const SpatialReference* srs ) const;
|
||
|
|
||
|
/** transform the GeoCircle to another SRS */
|
||
|
bool transform( const SpatialReference* srs, GeoCircle& out_circle ) const;
|
||
|
|
||
|
/** does this GeoCircle intersect another? */
|
||
|
bool intersects( const GeoCircle& rhs ) const;
|
||
|
|
||
|
public:
|
||
|
|
||
|
static GeoCircle INVALID;
|
||
|
|
||
|
protected:
|
||
|
GeoPoint _center;
|
||
|
double _radius;
|
||
|
};
|
||
|
}
|
||
|
OSGEARTH_SPECIALIZE_CONFIG(osgEarth::GeoPoint);
|
||
|
|
||
|
namespace osgEarth
|
||
|
{
|
||
|
/**
|
||
|
* An axis-aligned geospatial extent. A bounding box that is aligned with a
|
||
|
* spatial reference's coordinate system.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoExtent
|
||
|
{
|
||
|
public:
|
||
|
/** Default ctor creates an "invalid" extent */
|
||
|
GeoExtent();
|
||
|
|
||
|
/** Contructs a valid extent */
|
||
|
GeoExtent(
|
||
|
const SpatialReference* srs,
|
||
|
double west, double south,
|
||
|
double east, double north);
|
||
|
|
||
|
/** Contructs an invalid extent that you can grow with the expandToInclude method */
|
||
|
GeoExtent(const SpatialReference* srs);
|
||
|
|
||
|
/** Copy ctor */
|
||
|
GeoExtent(const GeoExtent& rhs);
|
||
|
|
||
|
/** create from Bounds object */
|
||
|
GeoExtent(const SpatialReference* srs, const Bounds& bounds);
|
||
|
|
||
|
/** dtor */
|
||
|
virtual ~GeoExtent() { }
|
||
|
|
||
|
//! Set from the SW and NE corners.
|
||
|
void set(double west, double south, double east, double north);
|
||
|
|
||
|
bool operator == (const GeoExtent& rhs) const;
|
||
|
bool operator != (const GeoExtent& rhs) const;
|
||
|
|
||
|
/** Gets the spatial reference system underlying this extent. */
|
||
|
const SpatialReference* getSRS() const { return _srs.get(); }
|
||
|
|
||
|
//! Coordinates of the bounding edges, normalized for the lat/long frame if necessary
|
||
|
inline double west() const;
|
||
|
inline double east() const;
|
||
|
inline double south() const;
|
||
|
inline double north() const;
|
||
|
|
||
|
//! Coordinates of the bounds, NOT normalized to the lat/long frame.
|
||
|
inline double xMin() const { return _west; }
|
||
|
inline double xMax() const { return _west + _width; }
|
||
|
inline double yMin() const { return _south; }
|
||
|
inline double yMax() const { return _south + _height; }
|
||
|
|
||
|
//! East-to-west span of the extent
|
||
|
inline double width() const { return _width; }
|
||
|
|
||
|
//! East-to-est span in specified units
|
||
|
double width(const UnitsType& units) const;
|
||
|
|
||
|
//! North-to-south span of the extent
|
||
|
inline double height() const { return _height; }
|
||
|
|
||
|
//! North-to-south span in specified units
|
||
|
double height(const UnitsType& units) const;
|
||
|
|
||
|
//! Gets the centroid of the bounds
|
||
|
GeoPoint getCentroid() const;
|
||
|
|
||
|
//! Legacy @deprecated
|
||
|
bool getCentroid(double& out_x, double& out_y) const;
|
||
|
|
||
|
//! True if the extent is geographic and crosses the 180 degree meridian.
|
||
|
bool crossesAntimeridian() const;
|
||
|
|
||
|
//! Raw bounds of the extent (unnormalized)
|
||
|
void getBounds(double& xmin, double& ymin, double& xmax, double& ymax) const;
|
||
|
|
||
|
/** True if this object defines a real, valid extent with positive area */
|
||
|
inline bool isValid() const
|
||
|
{
|
||
|
return _srs.valid() && _width >= 0.0 && _height >= 0.0;
|
||
|
}
|
||
|
|
||
|
inline bool isInvalid() const { return !isValid(); }
|
||
|
|
||
|
/**
|
||
|
* If this extent crosses the international date line, populates two extents, one for
|
||
|
* each side, and returns true. Otherwise returns false and leaves the reference
|
||
|
* parameters untouched.
|
||
|
*/
|
||
|
bool splitAcrossAntimeridian(GeoExtent& first, GeoExtent& second) const;
|
||
|
|
||
|
/**
|
||
|
* Returns this extent transformed into another spatial reference.
|
||
|
*
|
||
|
* NOTE! It is possible that the target SRS will not be able to accomadate the
|
||
|
* extents of the source SRS. (For example, transforming a full WGS84 extent
|
||
|
* to Mercator will resultin an error since Mercator does not cover the entire
|
||
|
* globe.) Consider using Profile:clampAndTransformExtent() instead of using
|
||
|
* this method directly.
|
||
|
*/
|
||
|
GeoExtent transform(const SpatialReference* to_srs) const;
|
||
|
|
||
|
/**
|
||
|
* Same as transform(srs) but puts the result in the output extent
|
||
|
*/
|
||
|
bool transform(const SpatialReference* to_srs, GeoExtent& output) const;
|
||
|
|
||
|
/**
|
||
|
* Returns true if the specified point falls within the bounds of the extent.
|
||
|
*
|
||
|
* @param x, y
|
||
|
* Coordinates to test
|
||
|
* @param xy_srs
|
||
|
* SRS of input x and y coordinates; if null, the method assumes x and y
|
||
|
* are in the same SRS as this object.
|
||
|
*/
|
||
|
bool contains(double x, double y, const SpatialReference* srs = 0L) const;
|
||
|
|
||
|
template<typename VEC>
|
||
|
bool contains(const VEC& a, const SpatialReference* srs = nullptr) const {
|
||
|
return contains(a.x(), a.y(), srs);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the point falls within this extent.
|
||
|
*/
|
||
|
bool contains(const GeoPoint& rhs) const;
|
||
|
|
||
|
/**
|
||
|
* Returns true if this extent fully contains another extent.
|
||
|
*/
|
||
|
bool contains(const GeoExtent& rhs) const;
|
||
|
|
||
|
/**
|
||
|
* Returns true if this extent fully contains the target bounds.
|
||
|
*/
|
||
|
bool contains(const Bounds& rhs) const;
|
||
|
|
||
|
/**
|
||
|
* Returns TRUE if this extent intersects another extent.
|
||
|
* @param[in ] rhs Extent against which to perform intersect test
|
||
|
* @param[in ] checkSRS If false, assume the extents have the same SRS (don't check).
|
||
|
*/
|
||
|
bool intersects(const GeoExtent& rhs, bool checkSRS = true) const;
|
||
|
|
||
|
/**
|
||
|
* Copy of the anonymous bounding box
|
||
|
*/
|
||
|
Bounds bounds() const;
|
||
|
|
||
|
/**
|
||
|
* Gets a geo circle bounding this extent.
|
||
|
*/
|
||
|
GeoCircle computeBoundingGeoCircle() const;
|
||
|
|
||
|
/**
|
||
|
* Grow this extent to include the specified point (which is assumed to be
|
||
|
* in the extent's SRS.
|
||
|
*/
|
||
|
void expandToInclude(double x, double y);
|
||
|
void expandToInclude(const osg::Vec3d& v) { expandToInclude(v.x(), v.y()); }
|
||
|
//void expandToInclude( const Bounds& bounds );
|
||
|
|
||
|
/**
|
||
|
* Expand this extent to include the bounds of another extent.
|
||
|
*/
|
||
|
bool expandToInclude(const GeoExtent& rhs);
|
||
|
|
||
|
/**
|
||
|
* Intersect this extent with another extent in the same SRS and return the
|
||
|
* result.
|
||
|
*/
|
||
|
GeoExtent intersectionSameSRS(const GeoExtent& rhs) const; //const Bounds& rhs ) const;
|
||
|
|
||
|
/**
|
||
|
* Returns a human-readable string containing the extent data (without the SRS)
|
||
|
*/
|
||
|
std::string toString() const;
|
||
|
|
||
|
/**
|
||
|
*Inflates this GeoExtent by the given ratios
|
||
|
*/
|
||
|
void scale(double x_scale, double y_scale);
|
||
|
|
||
|
/**
|
||
|
* Expands the extent by x and y.
|
||
|
*/
|
||
|
void expand(double x, double y);
|
||
|
|
||
|
/**
|
||
|
* Expands the extent by x and y.
|
||
|
*/
|
||
|
void expand(const Distance& x, const Distance& y);
|
||
|
|
||
|
/**
|
||
|
*Gets the area of this GeoExtent
|
||
|
*/
|
||
|
double area() const;
|
||
|
|
||
|
/**
|
||
|
* Generate a polytope in world coordinates that bounds the extent.
|
||
|
* Return false if the extent it invalid.
|
||
|
*/
|
||
|
bool createPolytope(osg::Polytope& out) const;
|
||
|
|
||
|
/**
|
||
|
* Computes a scale/bias matrix that transforms parametric coordinates [0..1]
|
||
|
* from this extent into the target extent. Return false if the extents are
|
||
|
* incompatible (different SRS, etc.)
|
||
|
*
|
||
|
* For example, if this extent is 100m wide, and the target extent is
|
||
|
* 200m wide, the output matrix will have an x_scale = 0.5.
|
||
|
*
|
||
|
* Note! For the sake of efficiency, this method does NOT check for
|
||
|
* validity nor for SRS equivalence -- so be sure to validate those beforehand.
|
||
|
* It also assumes the output matrix is preset to the identity.
|
||
|
*/
|
||
|
bool createScaleBias(const GeoExtent& target, osg::Matrixd& output) const;
|
||
|
bool createScaleBias(const GeoExtent& target, osg::Matrixf& output) const;
|
||
|
|
||
|
/**
|
||
|
* Generates a BoundingSphere encompassing the extent and a vertical
|
||
|
* volume, in world coordinates.
|
||
|
*/
|
||
|
osg::BoundingSphered createWorldBoundingSphere(double minElev, double maxElev) const;
|
||
|
|
||
|
/**
|
||
|
* Returns true if the extent is the entire Earth, for geographic
|
||
|
* extents. Otherwise returns false.
|
||
|
*/
|
||
|
bool isWholeEarth() const;
|
||
|
|
||
|
public:
|
||
|
static GeoExtent INVALID;
|
||
|
|
||
|
public: // config
|
||
|
Config getConfig() const;
|
||
|
void fromConfig(const Config& conf);
|
||
|
|
||
|
private:
|
||
|
double _west, _width, _south, _height;
|
||
|
osg::ref_ptr<const SpatialReference> _srs;
|
||
|
|
||
|
double normalizeX(double longitude) const;
|
||
|
|
||
|
void clamp();
|
||
|
|
||
|
bool isGeographic() const;
|
||
|
|
||
|
void setOriginAndSize(double west, double south, double width, double height);
|
||
|
};
|
||
|
|
||
|
inline double GeoExtent::west() const { return _west; }
|
||
|
|
||
|
// if east is exactly on the antimeridian, return 180.0
|
||
|
inline double GeoExtent::east() const {
|
||
|
double a = normalizeX(_west + _width);
|
||
|
return (isValid() && _srs->isGeographic() && a == -180.0) ? 180.0 : a;
|
||
|
}
|
||
|
|
||
|
inline double GeoExtent::south() const { return _south; }
|
||
|
|
||
|
inline double GeoExtent::north() const { return _south + _height; }
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A geospatial area with tile data LOD extents
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT DataExtent : public GeoExtent
|
||
|
{
|
||
|
public:
|
||
|
DataExtent();
|
||
|
DataExtent(const GeoExtent& extent);
|
||
|
DataExtent(const GeoExtent& extent, const std::string &description);
|
||
|
DataExtent(const GeoExtent& extent, unsigned minLevel);
|
||
|
DataExtent(const GeoExtent& extent, unsigned minLevel, const std::string &description);
|
||
|
DataExtent(const GeoExtent& extent, unsigned minLevel, unsigned maxLevel);
|
||
|
DataExtent(const GeoExtent& extent, unsigned minLevel, unsigned maxLevel, const std::string &description);
|
||
|
|
||
|
/** dtor */
|
||
|
virtual ~DataExtent() { }
|
||
|
|
||
|
/** The minimum LOD of the extent */
|
||
|
optional<unsigned>& minLevel() { return _minLevel; }
|
||
|
const optional<unsigned>& minLevel() const { return _minLevel; }
|
||
|
|
||
|
/** The maximum LOD of the extent */
|
||
|
optional<unsigned>& maxLevel() { return _maxLevel; }
|
||
|
const optional<unsigned>& maxLevel() const { return _maxLevel; }
|
||
|
|
||
|
/** description for the data extents */
|
||
|
const osgEarth::optional<std::string>& description() const { return _description; }
|
||
|
|
||
|
private:
|
||
|
optional<unsigned> _minLevel;
|
||
|
optional<unsigned> _maxLevel;
|
||
|
optional<std::string> _description;
|
||
|
};
|
||
|
|
||
|
typedef std::vector< DataExtent > DataExtentList;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A georeferenced image; i.e. an osg::Image and an associated GeoExtent with SRS.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoImage
|
||
|
{
|
||
|
public:
|
||
|
using pixel_type = osg::Vec4f;
|
||
|
|
||
|
/** Construct an empty (invalid) geoimage. */
|
||
|
GeoImage();
|
||
|
|
||
|
//! Construct an image with an error status
|
||
|
GeoImage(const Status&);
|
||
|
|
||
|
//! Constructs a new goereferenced image.
|
||
|
GeoImage(const osg::Image* image, const GeoExtent& extent);
|
||
|
|
||
|
//! Constructs a new goereferenced image from a future.
|
||
|
GeoImage(jobs::future<osg::ref_ptr<osg::Image>> image, const GeoExtent& extent);
|
||
|
|
||
|
/** dtor */
|
||
|
virtual ~GeoImage() { }
|
||
|
|
||
|
static GeoImage INVALID;
|
||
|
|
||
|
public:
|
||
|
/**
|
||
|
* True if this is a valid geo image.
|
||
|
*/
|
||
|
bool valid() const;
|
||
|
|
||
|
//! Error status if set
|
||
|
const Status& getStatus() const { return _status; }
|
||
|
|
||
|
/**
|
||
|
* Gets a pointer to the underlying OSG image.
|
||
|
*/
|
||
|
const osg::Image* getImage() const;
|
||
|
|
||
|
/**
|
||
|
* Gets the geospatial extent of the image.
|
||
|
*/
|
||
|
const GeoExtent& getExtent() const;
|
||
|
|
||
|
/**
|
||
|
* Shortcut to get the spatial reference system describing
|
||
|
* the projection of the image.
|
||
|
*/
|
||
|
const SpatialReference* getSRS() const;
|
||
|
|
||
|
/**
|
||
|
* Crops the image to a new geospatial extent.
|
||
|
*
|
||
|
* @param extent
|
||
|
* New extent to which to crop the image.
|
||
|
* @param exact
|
||
|
* If "exact" is true, the output image will have exactly the extents requested;
|
||
|
* this process may require resampling and will therefore be more expensive. If
|
||
|
* "exact" is false, we do a simple crop of the image that is rounded to the nearest
|
||
|
* pixel. The resulting extent will be close but usually not exactly what was
|
||
|
* requested - however, this method is faster.
|
||
|
* @param width, height
|
||
|
* New pixel size for the output image. By default, the method will automatically
|
||
|
* calculate a new pixel size.
|
||
|
*/
|
||
|
GeoImage crop(
|
||
|
const GeoExtent& extent,
|
||
|
bool exact = false,
|
||
|
unsigned int width = 0,
|
||
|
unsigned int height = 0,
|
||
|
bool useBilinearInterpolation = true) const;
|
||
|
|
||
|
/**
|
||
|
* Warps the image into a new spatial reference system.
|
||
|
*
|
||
|
* @param to_srs
|
||
|
* SRS into which to warp the image.
|
||
|
* @param to_extent
|
||
|
* Supply this extent if you wish to warp AND crop the image in one step. This is
|
||
|
* faster than calling reproject() and then crop().
|
||
|
* @param width, height
|
||
|
* New pixel size for the output image. Be default, the method will automatically
|
||
|
* calculate a new pixel size.
|
||
|
*/
|
||
|
GeoImage reproject(
|
||
|
const SpatialReference* to_srs,
|
||
|
const GeoExtent* to_extent = 0,
|
||
|
unsigned int width = 0,
|
||
|
unsigned int height = 0,
|
||
|
bool useBilinearInterpolation = true) const;
|
||
|
|
||
|
/**
|
||
|
* Returns the underlying OSG image and releases the reference pointer.
|
||
|
*/
|
||
|
osg::ref_ptr<osg::Image> takeImage();
|
||
|
|
||
|
/**
|
||
|
* Gets the units per pixel of this geoimage
|
||
|
*/
|
||
|
double getUnitsPerPixel() const;
|
||
|
|
||
|
//! Gets the coordinate at the image's s,t
|
||
|
bool getCoord(int s, int t, double& out_x, double& out_y) const;
|
||
|
|
||
|
//! Sets a token object in this GeoImage. You can use this to
|
||
|
//! keep a weak reference to the object after creating it,
|
||
|
//! for example to detect destruction.
|
||
|
void setTrackingToken(osg::Object* token);
|
||
|
osg::Object* getTrackingToken() const;
|
||
|
|
||
|
|
||
|
unsigned s() const {
|
||
|
return _myimage.valid() ? _myimage->s() : 0;
|
||
|
}
|
||
|
unsigned t() const {
|
||
|
return _myimage.valid() ? _myimage->t() : 0;
|
||
|
}
|
||
|
bool read(pixel_type& output, unsigned s, unsigned t) const {
|
||
|
_read(output, s, t);
|
||
|
return output.r() != NO_DATA_VALUE;
|
||
|
}
|
||
|
bool read(pixel_type& output, double u, double v) const {
|
||
|
_read(output, u, v);
|
||
|
return output.r() != NO_DATA_VALUE;
|
||
|
}
|
||
|
|
||
|
ImageUtils::PixelReader& getReader() { return _read; };
|
||
|
const ImageUtils::PixelReader& getReader() const { return _read; }
|
||
|
|
||
|
//! Read the value of a pixel at a geopoint.
|
||
|
bool read(
|
||
|
pixel_type& output,
|
||
|
const GeoPoint& p) const;
|
||
|
|
||
|
private:
|
||
|
GeoExtent _extent;
|
||
|
Status _status;
|
||
|
osg::ref_ptr<const osg::Image> _myimage;
|
||
|
mutable optional<jobs::future<osg::ref_ptr<osg::Image>>> _future;
|
||
|
osg::ref_ptr<osg::Object> _token;
|
||
|
ImageUtils::PixelReader _read;
|
||
|
};
|
||
|
|
||
|
typedef std::vector<GeoImage> GeoImageVector;
|
||
|
|
||
|
struct GeoImageIterator : public ImageUtils::ImageIteratorWithExtent<GeoExtent>
|
||
|
{
|
||
|
GeoImageIterator(const GeoImage& im) :
|
||
|
ImageUtils::ImageIteratorWithExtent<GeoExtent>(
|
||
|
im.getImage(), im.getExtent()) { }
|
||
|
};
|
||
|
|
||
|
struct GeoImagePixelReader : public ImageUtils::PixelReaderWithExtent<GeoExtent>
|
||
|
{
|
||
|
GeoImagePixelReader(const GeoImage& im) :
|
||
|
ImageUtils::PixelReaderWithExtent<GeoExtent>(
|
||
|
im.getImage(), im.getExtent()) { }
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* A georeferenced heightfield and associated normal/curvature map.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoHeightField
|
||
|
{
|
||
|
public:
|
||
|
/** Constructs an empty (invalid) heightfield. */
|
||
|
GeoHeightField();
|
||
|
|
||
|
//! Construct a heightfield with an error status
|
||
|
GeoHeightField(const Status&);
|
||
|
|
||
|
/**
|
||
|
* Constructs a new georeferenced heightfield.
|
||
|
*/
|
||
|
GeoHeightField(
|
||
|
const osg::HeightField* heightField,
|
||
|
const GeoExtent& extent);
|
||
|
|
||
|
/** dtor */
|
||
|
virtual ~GeoHeightField() { }
|
||
|
|
||
|
static GeoHeightField INVALID;
|
||
|
|
||
|
/**
|
||
|
* True if this is a valid heightfield.
|
||
|
*/
|
||
|
bool valid() const;
|
||
|
|
||
|
//! Status indicator
|
||
|
const Status& getStatus() const { return _status; }
|
||
|
|
||
|
/**
|
||
|
* Gets the elevation value at a specified point.
|
||
|
*
|
||
|
* @param srs
|
||
|
* Spatial reference of the query coordinates. (If you pass in NULL, the method
|
||
|
* will assume that the SRS is equivalent to that of the GeoHeightField. Be sure
|
||
|
* this is case of you will get incorrect results.)
|
||
|
* @param x, y
|
||
|
* Coordinates at which to query the elevation value.
|
||
|
* @param interp
|
||
|
* Interpolation method for the elevation query.
|
||
|
* @param srsWithOutputVerticalDatum
|
||
|
* Transform the output elevation value according to the vertical datum
|
||
|
* associated with this SRS. If the SRS is NULL, assume a geodetic vertical datum
|
||
|
* relative to this object's reference ellipsoid.
|
||
|
* @param out_elevation
|
||
|
* Output: the elevation value
|
||
|
* @return
|
||
|
* True if the elevation query was succesful; false if not (e.g. if the query
|
||
|
* fell outside the geospatial extent of the heightfield)
|
||
|
*/
|
||
|
bool getElevation(
|
||
|
const SpatialReference* inputSRS,
|
||
|
double x,
|
||
|
double y,
|
||
|
RasterInterpolation interp,
|
||
|
const SpatialReference* srsWithOutputVerticalDatum,
|
||
|
float& out_elevation) const;
|
||
|
|
||
|
//! Gets the elevation at a point (must be in the same SRS; bilinear interpolation)
|
||
|
float getElevation(double x, double y, RasterInterpolation interp = INTERP_BILINEAR) const;
|
||
|
|
||
|
/**
|
||
|
* Subsamples the heightfield, returning a new heightfield corresponding to
|
||
|
* the destEx extent. The destEx must be a smaller, inset area of sourceEx.
|
||
|
*/
|
||
|
GeoHeightField createSubSample(const GeoExtent& destEx, unsigned int width, unsigned int height, RasterInterpolation interpolation) const;
|
||
|
|
||
|
/**
|
||
|
* Gets the geospatial extent of the heightfield.
|
||
|
*/
|
||
|
const GeoExtent& getExtent() const;
|
||
|
|
||
|
/**
|
||
|
* The minimum height in the heightfield
|
||
|
*/
|
||
|
float getMinHeight() const { return _minHeight; }
|
||
|
|
||
|
/**
|
||
|
* The maximum height in the heightfield
|
||
|
*/
|
||
|
float getMaxHeight() const { return _maxHeight; }
|
||
|
|
||
|
/**
|
||
|
* Gets a pointer to the underlying OSG heightfield.
|
||
|
*/
|
||
|
const osg::HeightField* getHeightField() const;
|
||
|
|
||
|
/**
|
||
|
* Gets the X interval of this GeoHeightField
|
||
|
*/
|
||
|
double getXInterval() const;
|
||
|
|
||
|
/**
|
||
|
* Gets the Y interval of this GeoHeightField
|
||
|
*/
|
||
|
double getYInterval() const;
|
||
|
|
||
|
|
||
|
//Sort GeoHeightField's by resolution
|
||
|
struct SortByResolutionFunctor
|
||
|
{
|
||
|
inline bool operator() (const GeoHeightField& lhs, const GeoHeightField& rhs) const
|
||
|
{
|
||
|
return lhs.getXInterval() < rhs.getXInterval();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
protected:
|
||
|
GeoExtent _extent;
|
||
|
Status _status;
|
||
|
float _minHeight, _maxHeight;
|
||
|
osg::ref_ptr<const osg::HeightField> _heightField;
|
||
|
|
||
|
void init();
|
||
|
};
|
||
|
|
||
|
typedef std::vector<GeoHeightField> GeoHeightFieldVector;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A georeferenced node.
|
||
|
*/
|
||
|
class OSGEARTH_EXPORT GeoNode
|
||
|
{
|
||
|
public:
|
||
|
//! Construct an empty (invalid) GeoNode
|
||
|
GeoNode();
|
||
|
|
||
|
//! Construct an image with an error status
|
||
|
GeoNode(const Status&);
|
||
|
|
||
|
//! Constructs a new goereferenced node
|
||
|
GeoNode(const osg::Node* node, const GeoExtent& extent);
|
||
|
|
||
|
//! Global invalid node
|
||
|
static GeoNode INVALID;
|
||
|
|
||
|
public:
|
||
|
//! True if this is a valid object
|
||
|
bool valid() const;
|
||
|
|
||
|
//! Error status if set
|
||
|
const Status& getStatus() const { return _status; }
|
||
|
|
||
|
//! The underlying OSG node
|
||
|
const osg::Node* getNode() const { return _node.get(); }
|
||
|
|
||
|
//! The node's extent
|
||
|
const GeoExtent& getExtent() const { return _extent; }
|
||
|
|
||
|
private:
|
||
|
GeoExtent _extent;
|
||
|
Status _status;
|
||
|
osg::ref_ptr<const osg::Node> _node;
|
||
|
};
|
||
|
|
||
|
typedef std::vector<GeoNode> GeoNodeVector;
|
||
|
}
|