Skip to content

Commit

Permalink
Add an ElevationPool::getElevation function that is asynchronous
Browse files Browse the repository at this point in the history
  • Loading branch information
gwaldron committed Mar 8, 2017
1 parent 06d4795 commit 425b534
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 5 deletions.
23 changes: 23 additions & 0 deletions src/osgEarth/ElevationPool
Expand Up @@ -30,10 +30,19 @@

namespace osgEarth
{
using namespace Threading;

// defined at the end of this file
class ElevationEnvelope;
class Map;

//! Result of an elevation query.
struct ElevationSample : public osg::Referenced
{
float elevation;
float resolution;
ElevationSample(float a, float b) : elevation(a), resolution(b) { }
};

/**
* A pool of elevation data that can be used to manage regional elevation
Expand Down Expand Up @@ -72,6 +81,9 @@ namespace osgEarth
/** Creates a query envelope to use for elevation queries in a certain area. */
ElevationEnvelope* createEnvelope(const SpatialReference* srs, unsigned lod);

//! Queries the elevation at a GeoPoint for a given LOD.
Future<ElevationSample> getElevation(const GeoPoint& p, unsigned lod=23);

/** Maximum number of elevation tiles to cache */
void setMaxEntries(unsigned maxEntries) { _maxEntries = maxEntries; }
unsigned getMaxEntries() const { return _maxEntries; }
Expand Down Expand Up @@ -148,6 +160,17 @@ namespace osgEarth
// that a ElevationEnvelope uses for a terrain sampling opteration.
typedef std::set<osg::ref_ptr<Tile>, TileSortHiResToLoRes> QuerySet;

// Asynchronous elevation query operation
struct GetElevationOp : public osg::Operation {
GetElevationOp(ElevationPool*, const GeoPoint&, unsigned lod);
osg::observer_ptr<ElevationPool> _pool;
GeoPoint _point;
unsigned _lod;
Promise<ElevationSample> _promise;
void operator()(osg::Object*);
};
friend struct GetElevationOp;

protected:

/** dtor - prevent stack allocation */
Expand Down
28 changes: 28 additions & 0 deletions src/osgEarth/ElevationPool.cpp
Expand Up @@ -21,6 +21,7 @@
#include <osgEarth/MapFrame>
#include <osgEarth/Map>
#include <osgEarth/Metrics>
#include <osgEarth/Registry>
#include <osg/Shape>

using namespace osgEarth;
Expand Down Expand Up @@ -69,6 +70,33 @@ ElevationPool::setTileSize(unsigned value)
clearImpl();
}

Future<ElevationSample>
ElevationPool::getElevation(const GeoPoint& point, unsigned lod)
{
GetElevationOp* op = new GetElevationOp(this, point, lod);
Future<ElevationSample> result = op->_promise.getFuture();
Registry::instance()->getAsyncOperationQueue()->add(op);
return result;
}

ElevationPool::GetElevationOp::GetElevationOp(ElevationPool* pool, const GeoPoint& point, unsigned lod) :
_pool(pool), _point(point), _lod(lod)
{
//nop
}

void
ElevationPool::GetElevationOp::operator()(osg::Object*)
{
osg::ref_ptr<ElevationPool> pool;
if (!_promise.isAbandoned() && _pool.lock(pool))
{
osg::ref_ptr<ElevationEnvelope> env = pool->createEnvelope(_point.getSRS(), _lod);
std::pair<float, float> r = env->getElevationAndResolution(_point.x(), _point.y());
_promise.resolve(new ElevationSample(r.first, r.second));
}
}

bool
ElevationPool::fetchTileFromMap(const TileKey& key, MapFrame& frame, Tile* tile)
{
Expand Down
34 changes: 29 additions & 5 deletions src/osgEarth/ThreadingUtils
Expand Up @@ -160,6 +160,9 @@ namespace osgEarth { namespace Threading
{
};

/**
* Future is the consumer-side interface to an asynchronous operation.
*/
template<typename T>
class Future
{
Expand All @@ -170,41 +173,62 @@ namespace osgEarth { namespace Threading
};

public:
//! Blank CTOR
Future() {
_ev = new RefEvent();
_objRef = new RefPtrRef();
}

//! Copy CTOR
Future(const Future& rhs) : _ev(rhs._ev), _objRef(rhs._objRef.get()) { }

// Has the promise been fulfilled yet?
bool isFulfilled() const {
//! True is the promise was resolved and a result if available.
bool isAvailable() const {
return _ev->isSet();
}

// Wait for the promise to be fulfilled, then return.
//! The result value; blocks until it is available and then returns it.
T* get() {
_ev->wait();
return _objRef->_obj.release();
return _objRef->_obj.get();
}

//! The result value; blocks until available and returns it; then resets to initial state.
T* release() {
_ev->wait();
T* out = _objRef->_obj.release();
_ev->reset();
return out;
}

private:
osg::ref_ptr<RefEvent> _ev;
osg::ref_ptr<RefPtrRef> _objRef;
template<typename U> friend class Promise;
};


/**
* Future is the producer-side interface to an asynchronous operation.
*/
template<typename T>
class Promise
{
public:
//! This promise's future result.
const Future<T> getFuture() const { return _future; }

//! Resolve (fulfill) the promise with the provided result value.
void resolve(T* value) {
_future._objRef->_obj = value;
_future._ev->set();
}

//! True if the promise is resolved and the Future holds a valid result.
bool isResolved() const {
return _future._ev->isSet();
}

//! True is there are no Future objects waiting on this Promise.
bool isAbandoned() const {
return _future._objRef->referenceCount() == 1;
}
Expand Down

0 comments on commit 425b534

Please sign in to comment.