DYT/Tool/OpenSceneGraph-3.6.5/include/osgEarth/URI

522 lines
18 KiB
Plaintext
Raw Permalink Normal View History

2024-12-24 23:49:36 +00:00
/* -*-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/>
*/
#ifndef OSGEARTH_URI
#define OSGEARTH_URI 1
#include <osgEarth/Common>
#include <osgEarth/Containers>
#include <osgEarth/IOTypes>
#include <osg/Image>
#include <osg/Node>
#include <osgDB/Options>
#include <osgDB/ReaderWriter>
namespace osgUtil
{
class IncrementalCompileOperation;
}
namespace osgEarth
{
class URI;
class ProgressCallback;
using namespace Threading;
/**
* Context for resolving relative URIs.
*
* This object provides "context" for a relative URI. In other words, it provides
* all of the information the system needs to resolve it to an absolute location from
* which OSG can load data.
*
* The "referrer" is the location of an object that "points" to the object in the
* corresponding URI. The location conveyed by the URI will be relative to the location of
* its referrer. For example, a referrer of "http://server/folder/hello.xml" applied
* to the URI "there.jpg" will resolve to "http://server/folder/there.jpg". NOTE that referrer
* it not itself a location (like a folder); rather it's the object that referred to the URI
* being contextualized.
*/
class OSGEARTH_EXPORT URIContext
{
public:
/** Creates an empty context. */
URIContext();
/** Creates a context that specifies a referring object. */
URIContext(const std::string& referrer);
/** Copy constructor. */
URIContext(const URIContext& rhs);
/** dtor */
virtual ~URIContext() { }
/** Serializes this context to an Options structure. This is useful when passing context information
to an osgDB::ReaderWriter that takes a stream as input -- the stream is anonymous, therefore this
is the preferred way to communicate the context information. */
void store( osgDB::Options* options );
/** Creates a context from the serialized version in an Options structure (see above) */
URIContext( const osgDB::Options* options );
/** Returns the referring object. */
const std::string& referrer() const { return _referrer; }
/** True if the context is empty */
bool empty() const { return _referrer.empty(); }
/** Parents the input context with the current object, placing the subContext's information
under it. Used to re-parent relative locations with a higher-level referrer object. */
URIContext add( const URIContext& subContext ) const;
/** Returns a new context with the sub path appended to the current referrer path. */
URIContext add( const std::string& subPath ) const;
/** creates a string suitable for passing to an osgDB::ReaderWriter implementation */
std::string getOSGPath( const std::string& target ) const;
//! Add a header name/value pair to use when requesting a remote URL
void addHeader(const std::string& name, const std::string& value);
//! Headers in this URIContext
const Headers& getHeaders() const;
//! Headers in this URIContext
Headers& getHeaders();
private:
friend class URI;
std::string _referrer;
Headers _headers;
};
//--------------------------------------------------------------------
/**
* Stream container for reading a URI directly from a stream
*/
class OSGEARTH_EXPORT URIStream
{
public:
URIStream(const URI& uri, std::ios_base::openmode mode =std::ios_base::in);
virtual ~URIStream();
public:
// auto-cast to istream
operator std::istream& ();
protected:
friend class URI;
std::istream* _instream;
};
//--------------------------------------------------------------------
/**
* Represents the location of a resource, providing the raw (original, possibly
* relative) and absolute forms.
*
* URI is serializable and may be used in an earth file, like in the following
* example. Note that in earth files, the URI is actually called "url"; this is
* simply because of an old convention and we wish to avoid breaking backwards
* compatibility.
*
* <url>../path/relative/to/earth/file</url>
*
* Note also that a relative URI will be relative to the location of the
* parent resource (usually the earth file itself).
*
* You can also specify osgDB plugin options; for example:
*
* <url options_string="JPEG_QUALITY 60">../path/to/image.jpg</url>
*
* Of course, options are particular to OSG plugins, so please consult the
* code for your plugin for more information.
*/
class OSGEARTH_EXPORT URI
{
public:
/**
* Constructs an empty (and invalid) URI.
*/
URI();
/**
* Constructs a new URI from a location (typically an absolute url)
*/
URI( const std::string& location );
/**
* Constructs a new URI from a location and an existing context.
*/
URI( const std::string& location, const URIContext& context );
/**
* Constructs a new URI from a string.
*/
URI( const char* location );
/** dtor */
virtual ~URI() { }
public:
/** The base (possibly relative) location string. */
const std::string& base() const { return _baseURI; }
/** The fully qualified location string. */
const std::string& full() const { return _fullURI; }
/** The dereference operator also returns the fully qualified URI, since it's a common operation. */
const std::string& operator * () const { return _fullURI; }
/** Context with which this URI was created. */
const URIContext& context() const { return _context; }
/** Whether the URI is empty */
bool empty() const { return _baseURI.empty(); }
/** Whether the object of the URI is cacheable. */
bool isRemote() const;
/** Returns a copy of this URI with the suffix appended */
URI append( const std::string& suffix ) const;
/** String used for keying the cache */
const std::string& cacheKey() const { return !_cacheKey.empty() ? _cacheKey : _fullURI; }
/** osgDB::Options option string (plugin options) */
optional<std::string>& optionString() { return _optionString; }
const optional<std::string>& optionString() const { return _optionString; }
public:
/** Sets a cache key. By default the cache key is the full URI, but you can override that. */
void setCacheKey( const std::string& key ) { _cacheKey = key; }
public: // read methods return a ReadResult object
ReadResult readObject(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const;
ReadResult readImage(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const;
ReadResult readNode(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const;
ReadResult readString(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const;
ReadResult readConfig(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const;
public: // get methods call the read* methods, then just return the raw data.
osg::Object* getObject(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const { return readObject(dbOptions, progress).releaseObject(); }
osg::Image* getImage(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const { return readImage(dbOptions, progress).releaseImage(); }
osg::Node* getNode(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const { return readNode(dbOptions, progress).releaseNode(); }
std::string getString(
const osgDB::Options* dbOptions =0L,
ProgressCallback* progress =0L ) const { return readString(dbOptions, progress).getString(); }
public:
bool operator < ( const URI& rhs ) const { return _fullURI < rhs._fullURI; }
bool operator == ( const URI& rhs ) const { return _fullURI.compare(rhs._fullURI) == 0; }
bool operator != ( const URI& rhs ) const { return _fullURI.compare(rhs._fullURI) != 0; }
public:
/** Copier */
URI( const URI& rhs );
public: // config methods
Config getConfig() const;
void mergeConfig(const Config& conf);
public: // Static convenience methods
/** Encodes text to URL safe test. Escapes special charaters */
inline static std::string urlEncode(const std::string &value);
protected:
std::string _baseURI;
std::string _fullURI;
std::string _cacheKey;
URIContext _context;
optional<std::string> _optionString;
void ctorCacheKey();
};
}
//------------------------------------------------------------------------
namespace std {
// std::hash specialization for URI
template<> struct hash<osgEarth::URI> {
inline size_t operator()(const osgEarth::URI& value) const {
return hash<string>()(value.full());
}
};
}
//------------------------------------------------------------------------
namespace osgEarth
{
/**
* A lookup table that maps URI references to other URI references. This
* is used as an optional resource mapping table. (See KML's ResourceMap
* for usage example)
*
* WARNING: osgDB::Options will only store a raw pointer to the class, so
* make sure the scope of the osgDB::Options does not exceed the scope of
* the embedded alias map!
*/
class OSGEARTH_EXPORT URIAliasMap
{
public:
/**
* Inserts a key-value pair into the map.
*/
void insert( const std::string& key, const std::string& value );
/**
* Resolves the input address into a URI string.
*/
std::string resolve(const std::string& input, const URIContext& context) const;
/**
* True if there are no mappings
*/
bool empty() const { return _map.empty(); }
/**
* Clears out the map.
*/
void clear() { _map.clear(); }
/**
* Loads an alias map from an Options.
*/
static URIAliasMap* from( const osgDB::Options* options ) {
return options ? const_cast<URIAliasMap*>(static_cast<const URIAliasMap*>(options->getPluginData("osgEarth::URIAliasMap"))) : 0L;
}
/**
* Stores an alias map in an Options
*/
void apply( osgDB::Options* options ) {
if ( options ) options->setPluginData("osgEarth::URIAliasMap", this);
}
protected:
std::map<std::string,std::string> _map;
friend class Config;
};
/**
* A custom read callback (that you can set in an osgDB::Options) that will
* attempt to resolve pathnames using a URI alias map.
*/
class OSGEARTH_EXPORT URIAliasMapReadCallback : public osgDB::ReadFileCallback
{
public:
URIAliasMapReadCallback( const URIAliasMap& aliasMap, const URIContext& context );
virtual osgDB::ReaderWriter::ReadResult openArchive(const std::string& filename, osgDB::ReaderWriter::ArchiveStatus status, unsigned int indexBlockSizeHint, const osgDB::Options* useObjectCache);
virtual osgDB::ReaderWriter::ReadResult readObject(const std::string& filename, const osgDB::Options* options);
virtual osgDB::ReaderWriter::ReadResult readImage(const std::string& filename, const osgDB::Options* options);
virtual osgDB::ReaderWriter::ReadResult readHeightField(const std::string& filename, const osgDB::Options* options);
virtual osgDB::ReaderWriter::ReadResult readNode(const std::string& filename, const osgDB::Options* options);
virtual osgDB::ReaderWriter::ReadResult readShader(const std::string& filename, const osgDB::Options* options);
protected:
const URIAliasMap& _aliasMap;
URIContext _context;
};
//------------------------------------------------------------------------
/**
* A URI result cache that you can embed in an osgDB::Options, and if found,
* URI will attempt to use it.
*
* WARNING: osgDB::Options will only store a raw pointer to the class, so
* make sure the scope of the osgDB::Options does not exceed the scope of
* the embedded cache!
*/
struct /*header-only*/ URIResultCache : public LRUCache<URI, ReadResult>
{
URIResultCache( bool threadsafe =true )
: LRUCache<URI,ReadResult>( threadsafe ) { }
static URIResultCache* from(const osgDB::Options* options) {
return options ? const_cast<URIResultCache*>(static_cast<const URIResultCache*>(options->getPluginData("osgEarth::URIResultCache"))) : 0L;
}
void apply( osgDB::Options* options ) {
if ( options ) options->setPluginData("osgEarth::URIResultCache", this);
}
};
//------------------------------------------------------------------------
/**
* You can install a post-read callback in a osgDB::Options and the URI
* class till invoke it on a ReadResult before returning.
*/
class /*header-only*/ URIPostReadCallback : public osg::Referenced
{
public:
URIPostReadCallback() { }
virtual ~URIPostReadCallback() { }
virtual void operator()( ReadResult& result ) =0;
public:
void apply(osgDB::Options* options) {
if ( options ) options->setPluginData("osgEarth::URIPostReadCallback", this);
}
static URIPostReadCallback* from(const osgDB::Options* options) {
return options ? const_cast<URIPostReadCallback*>(static_cast<const URIPostReadCallback*>(options->getPluginData("osgEarth::URIPostReadCallback"))) : 0L;
}
};
//------------------------------------------------------------------------
// Config specialization for URI:
template<> inline
void Config::set<URI>( const std::string& key, const optional<URI>& opt ) {
if ( opt.isSet() ) {
remove(key);
set( key, opt->getConfig() );
}
}
template<> inline
bool Config::get<URI>( const std::string& key, optional<URI>& output ) const {
if ( hasChild(key) ) {
const Config& uriconf = child(key);
if (!uriconf.value().empty()) {
URI uri(uriconf.value(), uriconf.referrer());
uri.mergeConfig(uriconf);
output = uri;
return true;
}
else return false;
}
else return false;
}
// Config specialization for URIAliasMap
template <> inline
void Config::set<URIAliasMap>( const std::string& key, const optional<URIAliasMap>& map ) {
remove( key );
if ( map.isSet() ) {
Config conf( key );
for( std::map<std::string,std::string>::const_iterator i = map->_map.begin(); i != map->_map.end(); ++i ) {
Config alias( "alias" );
alias.add( "source", i->first );
alias.add( "target", i->second );
conf.add( alias );
}
set(conf);
}
}
template <> inline
bool Config::get<URIAliasMap>( const std::string& key, optional<URIAliasMap>& output ) const {
Config alias = child(key);
if ( !alias.empty() ) {
for( ConfigSet::const_iterator i = alias.children().begin(); i != alias.children().end(); ++i ) {
std::string source = i->value("source");
std::string target = i->value("target");
if ( !source.empty() && !target.empty() )
output.mutable_value().insert( source, target );
}
return true;
}
else {
return false;
}
}
//------------------------------------------------------------------------
// Ref.: https://stackoverflow.com/questions/154536/encode-decode-urls-in-c
std::string URI::urlEncode(const std::string &value)
{
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << std::uppercase;
escaped << '%' << std::setw(2) << int((unsigned char)c);
escaped << std::nouppercase;
}
return escaped.str();
}
}
#endif // OSGEARTH_URI