bool ResourceCache::getOrCreateLineTexture(const URI& uri, osg::ref_ptr<osg::Texture>& output, const osgDB::Options* readOptions) { Threading::ScopedMutexLock lock(_texMutex); TextureCache::Record rec; if (_texCache.get(uri.full(), rec) && rec.value().valid()) { output = rec.value().get(); } else { osg::ref_ptr<osg::Image> image = uri.getImage(readOptions); if (image.valid()) { osg::Texture2D* tex = new osg::Texture2D(image); tex->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE ); tex->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT ); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR); tex->setMaxAnisotropy( 4.0f ); tex->setResizeNonPowerOfTwoHint( false ); output = tex; _texCache.insert(uri.full(), output.get()); } } return output.valid(); }
void initialize( const std::string& referenceURI, const Profile* overrideProfile) { if ( !overrideProfile ) { OE_WARN << LC << "An explicit profile definition is required by the OSG driver." << std::endl; return; } setProfile( overrideProfile ); osg::ref_ptr<osg::Image> image; URI url = _options.url().value(); if ( !url.empty() ) { url = URI( url.full(), referenceURI ); // obselete? HTTPClient::ResultCode code = HTTPClient::readImageFile( url.full(), image ); if ( code != HTTPClient::RESULT_OK ) { OE_WARN << LC << "Failed to load data from \"" << url.full() << "\", because: " << HTTPClient::getResultCodeString(code) << std::endl; } } if ( !image.valid() ) OE_WARN << LC << "Faild to load data from \"" << url.full() << "\"" << std::endl; // calculate and store the maximum LOD for which to return data if ( image.valid() ) { int minSpan = osg::minimum( image->s(), image->t() ); int tileSize = _options.tileSize().value(); _maxDataLevel = (int)LOG2((minSpan/tileSize)+1); //OE_NOTICE << "[osgEarth::OSG driver] minSpan=" << minSpan << ", _tileSize=" << tileSize << ", maxDataLevel = " << _maxDataLevel << std::endl; getDataExtents().push_back( DataExtent(overrideProfile->getExtent(), 0, _maxDataLevel) ); bool computeAlpha = (_options.convertLuminanceToRGBA() == true && image->getPixelFormat() == GL_LUMINANCE) || (_options.addAlpha() == true && !ImageUtils::hasAlphaChannel( image.get() ) ); if ( computeAlpha ) { image = makeRGBAandComputeAlpha( image.get() ); } else if ( ImageUtils::hasAlphaChannel( image.get() )) { image = ImageUtils::convertToRGBA8( image.get() ); } else { image = ImageUtils::convertToRGB8( image.get() ); } _image = GeoImage( image.get(), getProfile()->getExtent() ); } _extension = osgDB::getFileExtension( url.full() ); }
Status initialize(const osgDB::Options* dbOptions) { _dbOptions = Registry::instance()->cloneOrCreateOptions(dbOptions); URI xyzURI = _options.url().value(); if ( xyzURI.empty() ) { return Status::Error( "Fail: driver requires a valid \"url\" property" ); } // driver requires a profile. if ( !getProfile() ) { return Status::Error( "An explicit profile definition is required by the XYZ driver." ); } _template = xyzURI.full(); _rotateStart = _template.find("["); _rotateEnd = _template.find("]"); if ( _rotateStart != std::string::npos && _rotateEnd != std::string::npos && _rotateEnd-_rotateStart > 1 ) { _rotateString = _template.substr(_rotateStart, _rotateEnd-_rotateStart+1); _rotateChoices = _template.substr(_rotateStart+1, _rotateEnd-_rotateStart-1); } _format = _options.format().isSet() ? *_options.format() : osgDB::getLowerCaseFileExtension( xyzURI.base() ); return STATUS_OK; }
URIStream::URIStream( const URI& uri ) : _fileStream( 0L ) { if ( osgDB::containsServerAddress(uri.full()) ) { HTTPResponse res = HTTPClient::get( uri.full() ); if ( res.isOK() ) { std::string buf = res.getPartAsString(0); _bufStream.str(buf); } } else { _fileStream = new std::ifstream( uri.full().c_str() ); } }
URIStream::URIStream(const URI& uri, std::ios_base::openmode mode) : _instream( 0L ) { if ( osgDB::containsServerAddress(uri.full()) ) { HTTPResponse res = HTTPClient::get( uri.full() ); if ( res.isOK() ) { std::string buf = res.getPartAsString(0); _instream = new std::istringstream(buf); } } else { _instream = new std::ifstream(uri.full().c_str(), mode); } }
bool ServiceReader::read( const URI& location, const osgDB::Options* options, RESTResponse& response ) { response.setServiceURL( location.full() ); std::string serviceLocation = location.full() + "?f=json&pretty=true"; ReadResult r = URI(serviceLocation, location.context()).readString(); if ( r.failed() ) { OE_WARN << "Failed to read ArcGIS Services tile map file from " << serviceLocation << std::endl; return false; } // Read tile map into a Config: Config conf; std::stringstream buf( r.getString() ); if (!conf.fromJSON( buf.str() )) { return false; } return read( conf, response ); }
KMZArchive::KMZArchive( const URI& archiveURI ) : _archiveURI( archiveURI ), _buf( 0L ), _bufsize( 1024000 ) { supportsExtension( "kmz", "KMZ" ); // download the KMZ to a local cache - cannot open zip files remotely. URI localURI( archiveURI ); if ( osgDB::containsServerAddress(archiveURI.full()) ) { localURI = downloadToCache( archiveURI ); } _uf = unzOpen( localURI.full().c_str() ); _buf = (void*)new char[_bufsize]; }
bool LandCoverBiome::configure(const ConfigOptions& conf, const osgDB::Options* dbo) { LandCoverBiomeOptions in( conf ); if ( in.biomeClasses().isSet() ) setClasses( in.biomeClasses().get() ); for(SymbolVector::const_iterator i = in.symbols().begin(); i != in.symbols().end(); ++i) { const BillboardSymbol* bs = dynamic_cast<BillboardSymbol*>( i->get() ); if ( bs ) { URI imageURI = bs->url()->evalURI(); osg::Image* image = const_cast<osg::Image*>( bs->getImage() ); if ( !image ) { image = imageURI.getImage(dbo); } if ( image ) { getBillboards().push_back( LandCoverBillboard(image, bs->width().get(), bs->height().get()) ); } else { OE_WARN << LC << "Failed to load billboard image from \"" << imageURI.full() << "\"\n"; } } else { OE_WARN << LC << "Unrecognized symbol in land cover biome\n"; } } if ( getBillboards().size() == 0 ) { OE_WARN << LC << "A biome failed to install any billboards.\n"; return false; } return true; }
bool SubstituteModelFilter::findResource(const URI& uri, const InstanceSymbol* symbol, FilterContext& context, std::set<URI>& missing, osg::ref_ptr<InstanceResource>& output ) { // be careful about refptrs here since _instanceCache is an LRU. InstanceCache::Record rec; if ( _instanceCache.get(uri, rec) ) { // found it in the cache: output = rec.value().get(); } else if ( _resourceLib.valid() ) { // look it up in the resource library: output = _resourceLib->getInstance( uri.base(), context.getDBOptions() ); } else { // create it on the fly: output = symbol->createResource(); output->uri() = uri; _instanceCache.insert( uri, output.get() ); } // failed to find the instance. if ( !output.valid() ) { if ( missing.find(uri) == missing.end() ) { missing.insert(uri); OE_WARN << LC << "Failed to locate resource: " << uri.full() << std::endl; } } return output.valid(); }
/** override */ Status initialize( const osgDB::Options* dbOptions ) { osg::ref_ptr<const Profile> result; char sep = _options.url()->full().find_first_of('?') == std::string::npos? '?' : '&'; URI capUrl = _options.capabilitiesUrl().value(); if ( capUrl.empty() ) { capUrl = URI( _options.url()->full() + sep + std::string("SERVICE=WMS") + std::string("&VERSION=") + _options.wmsVersion().value() + std::string("&REQUEST=GetCapabilities") ); } //Try to read the WMS capabilities osg::ref_ptr<WMSCapabilities> capabilities = WMSCapabilitiesReader::read( capUrl.full(), dbOptions ); if ( !capabilities.valid() ) { return Status::Error( Status::ResourceUnavailable, "Unable to read WMS GetCapabilities." ); } else { OE_INFO << LC << "Got capabilities from " << capUrl.full() << std::endl; } if ( _formatToUse.empty() && capabilities.valid() ) { _formatToUse = capabilities->suggestExtension(); OE_INFO << LC << "No format specified, capabilities suggested extension " << _formatToUse << std::endl; } if ( _formatToUse.empty() ) _formatToUse = "png"; if ( _srsToUse.empty() ) _srsToUse = "EPSG:4326"; std::string wmsFormatToUse = _options.wmsFormat().value(); //Initialize the WMS request prototype std::stringstream buf; // first the mandatory keys: buf << std::fixed << _options.url()->full() << sep << "SERVICE=WMS" << "&VERSION=" << _options.wmsVersion().value() << "&REQUEST=GetMap" << "&LAYERS=" << _options.layers().value() << "&FORMAT=" << ( wmsFormatToUse.empty() ? std::string("image/") + _formatToUse : wmsFormatToUse ) << "&STYLES=" << _options.style().value() << (_options.wmsVersion().value() == "1.3.0" ? "&CRS=" : "&SRS=") << _srsToUse << "&WIDTH="<< getPixelsPerTile() << "&HEIGHT=" << getPixelsPerTile() << "&BBOX=%lf,%lf,%lf,%lf"; // then the optional keys: if ( _options.transparent().isSet() ) buf << "&TRANSPARENT=" << (_options.transparent() == true ? "TRUE" : "FALSE"); _prototype = ""; _prototype = buf.str(); //OE_NOTICE << "Prototype " << _prototype << std::endl; osg::ref_ptr<SpatialReference> wms_srs = SpatialReference::create( _srsToUse ); // check for spherical mercator: if ( wms_srs.valid() && wms_srs->isEquivalentTo( osgEarth::Registry::instance()->getGlobalMercatorProfile()->getSRS() ) ) { result = osgEarth::Registry::instance()->getGlobalMercatorProfile(); } else if (wms_srs.valid() && wms_srs->isEquivalentTo( osgEarth::Registry::instance()->getGlobalGeodeticProfile()->getSRS())) { result = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } // Next, try to glean the extents from the layer list if ( capabilities.valid() ) { //TODO: "layers" mights be a comma-separated list. need to loop through and //combine the extents?? yes WMSLayer* layer = capabilities->getLayerByName( _options.layers().value() ); if ( layer ) { double minx, miny, maxx, maxy; minx = miny = maxx = maxy = 0; //Check to see if the profile is equivalent to global-geodetic if (wms_srs->isGeographic()) { //Try to get the lat lon extents if they are provided layer->getLatLonExtents(minx, miny, maxx, maxy); //If we still don't have any extents, just default to global geodetic. if (!result.valid() && minx == 0 && miny == 0 && maxx == 0 && maxy == 0) { result = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } } if (minx == 0 && miny == 0 && maxx == 0 && maxy == 0) { layer->getExtents(minx, miny, maxx, maxy); } if (!result.valid()) { result = Profile::create( _srsToUse, minx, miny, maxx, maxy ); } //Add the layer extents to the list of valid areas if (minx != 0 || maxx != 0 || miny != 0 || maxy != 0) { GeoExtent extent( result->getSRS(), minx, miny, maxx, maxy); getDataExtents().push_back( DataExtent(extent, 0) ); } } } // Last resort: create a global extent profile (only valid for global maps) if ( !result.valid() && wms_srs->isGeographic()) { result = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } // JPL uses an experimental interface called TileService -- ping to see if that's what // we are trying to read: URI tsUrl = _options.tileServiceUrl().value(); if ( tsUrl.empty() ) { tsUrl = URI(_options.url()->full() + sep + std::string("request=GetTileService") ); } OE_INFO << LC << "Testing for JPL/TileService at " << tsUrl.full() << std::endl; _tileService = TileServiceReader::read(tsUrl.full(), dbOptions); if (_tileService.valid()) { OE_INFO << LC << "Found JPL/TileService spec" << std::endl; TileService::TilePatternList patterns; _tileService->getMatchingPatterns( _options.layers().value(), _formatToUse, _options.style().value(), _srsToUse, getPixelsPerTile(), getPixelsPerTile(), patterns ); if (patterns.size() > 0) { result = _tileService->createProfile( patterns ); _prototype = _options.url()->full() + sep + patterns[0].getPrototype(); } } else { OE_INFO << LC << "No JPL/TileService spec found; assuming standard WMS" << std::endl; } // Use the override profile if one is passed in. if ( getProfile() == 0L ) { setProfile( result.get() ); } if ( getProfile() ) { OE_INFO << LC << "Profile=" << getProfile()->toString() << std::endl; // set up the cache options properly for a TileSource. _dbOptions = Registry::instance()->cloneOrCreateOptions( dbOptions ); return Status::OK(); } else { return Status::Error( "Unable to establish profile" ); } }
/** * Create and return an image for the given TileKey. */ osg::Image* createImage( const TileKey& key, ProgressCallback* progress ) { if (_debugDirect) { //osg::Image* image = new osg::Image; //image->allocateImage(256,256,1, GL_RGB, GL_UNSIGNED_BYTE); //return image; return osgDB::readImageFile( getDirectURI(key) ); //return URI(getDirectURI(key)).getImage(_dbOptions.get(), progress); } // center point of the tile (will be in spherical mercator) double x, y; key.getExtent().getCentroid(x, y); // transform it to lat/long: GeoPoint geo; GeoPoint( getProfile()->getSRS(), x, y ).transform( getProfile()->getSRS()->getGeographicSRS(), geo ); // contact the REST API. Docs are here: // http://msdn.microsoft.com/en-us/library/ff701716.aspx // construct the request URI: std::string request = Stringify() << std::setprecision(12) << _options.imageryMetadataAPI().get() // base REST API << "/" << _options.imagerySet().get() // imagery set to use << "/" << geo.y() << "," << geo.x() // center point in lat/long << "?zl=" << key.getLOD() + 1 // zoom level << "&o=json" // response format << "&key=" << _options.key().get(); // API key // check the URI cache. URI location; TileURICache::Record rec; if ( _tileURICache.get(request, rec) ) { location = URI(rec.value()); //CacheStats stats = _tileURICache.getStats(); //OE_INFO << "Ratio = " << (stats._hitRatio*100) << "%" << std::endl; } else { unsigned c = ++_apiCount; if ( c % 25 == 0 ) OE_INFO << LC << "API calls = " << c << std::endl; // fetch it: ReadResult metadataResult = URI(request).readString(_dbOptions, progress); if ( metadataResult.failed() ) { // check for a REST error: if ( metadataResult.code() == ReadResult::RESULT_SERVER_ERROR ) { OE_WARN << LC << "REST API request error!" << std::endl; Config metadata; std::string content = metadataResult.getString(); metadata.fromJSON( content ); ConfigSet errors = metadata.child("errorDetails").children(); for(ConfigSet::const_iterator i = errors.begin(); i != errors.end(); ++i ) { OE_WARN << LC << "REST API: " << i->value() << std::endl; } return 0L; } else { OE_WARN << LC << "Request error: " << metadataResult.getResultCodeString() << std::endl; } return 0L; } // decode it: Config metadata; if ( !metadata.fromJSON(metadataResult.getString()) ) { OE_WARN << LC << "Error decoding REST API response" << std::endl; return 0L; } // check the vintage field. If it's empty, that means we got a "no data" tile. Config* vintageEnd = metadata.find("vintageEnd"); if ( !vintageEnd || vintageEnd->value().empty() ) { OE_DEBUG << LC << "NO data image encountered." << std::endl; return 0L; } // find the tile URI: Config* locationConf= metadata.find("imageUrl"); if ( !locationConf ) { OE_WARN << LC << "REST API JSON parsing error (imageUrl not found)" << std::endl; return 0L; } location = URI( locationConf->value() ); _tileURICache.insert( request, location.full() ); } // request the actual tile //OE_INFO << "key = " << key.str() << ", URL = " << location->value() << std::endl; //osg::Image* image = location.getImage(_dbOptions.get(), progress); osg::Image* image = osgDB::readImageFile( location.full() ); if ( image && _geom.valid() ) { GeometryRasterizer rasterizer( image->s(), image->t() ); rasterizer.draw( _geom.get(), osg::Vec4(1,1,1,1) ); osg::ref_ptr<osg::Image> overlay = rasterizer.finalize(); ImageUtils::PixelVisitor<AlphaBlend> blend; blend.accept( overlay.get(), image ); } return image; }
// override Status initialize( const osgDB::Options* dbOptions ) { // add the security token to the URL if necessary: URI url = _options.url().value(); OE_DEBUG << LC << "Initial URL: " << url.full() << std::endl; // append token to url in query string is set if (_options.token().isSet()) { std::string token = _options.token().value(); if (!token.empty()) { std::string sep = url.full().find( "?" ) == std::string::npos ? "?" : "&"; url = url.append( sep + std::string("token=") + token ); } } else { OE_DEBUG << LC << "Token not set" << std::endl; } // append layers to url in query string is set .. format is show:1,2,3 if (_options.layers().isSet()) { std::string layers = _options.layers().value(); OE_DEBUG << LC << "_Layers: " << layers << std::endl; if (!layers.empty()) { std::string sep = url.full().find( "?" ) == std::string::npos ? "?" : "&"; url = url.append( sep + std::string("layers=show:") + layers ); } } else { OE_DEBUG << LC << "Layer options not set" << std::endl; } OE_DEBUG << LC << "_map_service URL: " << url.full() << std::endl; // read map service metadata from the server if ( !_map_service.init(url, dbOptions) ) { OE_INFO << LC << "_map_service.init failed: " << _map_service.getError() << std::endl; return Status::Error( Stringify() << "[osgearth] [ArcGIS] map service initialization failed: " << _map_service.getError() ); } _dbOptions = Registry::instance()->cloneOrCreateOptions( dbOptions ); // establish a profile if we don't already have one: if ( !getProfile() ) { const Profile* profile = NULL; if ( _profileConf.isSet() ) { profile = Profile::create( _profileConf.get() ); } else if ( _map_service.getProfile() ) { profile = _map_service.getProfile(); } else { // finally, fall back on lat/long profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } setProfile( profile ); } return STATUS_OK; }
void ModelNode::init() { // reset. this->clearDecoration(); osgEarth::clearChildren( this ); osgEarth::clearChildren( _xform.get() ); this->addChild( _xform.get() ); this->setHorizonCulling(false); osg::ref_ptr<const ModelSymbol> sym = _style.get<ModelSymbol>(); // backwards-compatibility: support for MarkerSymbol (deprecated) if ( !sym.valid() && _style.has<MarkerSymbol>() ) { osg::ref_ptr<InstanceSymbol> temp = _style.get<MarkerSymbol>()->convertToInstanceSymbol(); sym = dynamic_cast<const ModelSymbol*>( temp.get() ); } if ( sym.valid() ) { if ( ( sym->url().isSet() ) || (sym->getModel() != NULL) ) { // Try to get a model from symbol osg::ref_ptr<osg::Node> node = sym->getModel(); // Try to get a model from URI if (node.valid() == false) { URI uri = sym->url()->evalURI(); if ( sym->uriAliasMap()->empty() ) { node = uri.getNode( _dbOptions.get() ); } else { // install an alias map if there's one in the symbology. osg::ref_ptr<osgDB::Options> tempOptions = Registry::instance()->cloneOrCreateOptions(_dbOptions.get()); tempOptions->setReadFileCallback( new URIAliasMapReadCallback(*sym->uriAliasMap(), uri.full()) ); node = uri.getNode( tempOptions.get() ); } if (node.valid() == false) { OE_WARN << LC << "No model and failed to load data from " << uri.full() << std::endl; } } if (node.valid() == true) { if ( Registry::capabilities().supportsGLSL() ) { // generate shader code for the loaded model: Registry::shaderGenerator().run( node.get(), "osgEarth.ModelNode", Registry::stateSetCache() ); } // attach to the transform: _xform->addChild( node ); // insert a clamping agent if necessary: replaceChild( _xform.get(), applyAltitudePolicy(_xform.get(), _style) ); if ( sym->scale().isSet() ) { double s = sym->scale()->eval(); this->setScale( osg::Vec3f(s, s, s) ); } // auto scaling? if ( sym->autoScale() == true ) { this->getOrCreateStateSet()->setRenderBinDetails(0, osgEarth::AUTO_SCALE_BIN ); } // rotational offsets? if (sym && (sym->heading().isSet() || sym->pitch().isSet() || sym->roll().isSet()) ) { osg::Matrix rot; double heading = sym->heading().isSet() ? sym->heading()->eval() : 0.0; double pitch = sym->pitch().isSet() ? sym->pitch()->eval() : 0.0; double roll = sym->roll().isSet() ? sym->roll()->eval() : 0.0; rot.makeRotate( osg::DegreesToRadians(heading), osg::Vec3(0,0,1), osg::DegreesToRadians(pitch), osg::Vec3(1,0,0), osg::DegreesToRadians(roll), osg::Vec3(0,1,0) ); this->setLocalRotation( rot.getRotate() ); } this->applyRenderSymbology( _style ); } else { OE_WARN << LC << "No model" << std::endl; } } else { OE_WARN << LC << "Symbology: no URI or model" << std::endl; } } else { OE_WARN << LC << "Insufficient symbology" << std::endl; } }
// one-time initialization (per source) Status initialize(const osgDB::Options* dbOptions) { _dbOptions = Registry::instance()->cloneOrCreateOptions(dbOptions); const Profile* profile = getProfile(); URI tmsURI = _options.url().value(); if ( tmsURI.empty() ) { return Status::Error( "Fail: TMS driver requires a valid \"url\" property" ); } // Take the override profile if one is given if ( profile ) { OE_INFO << LC << "Using override profile \"" << getProfile()->toString() << "\" for URI \"" << tmsURI.base() << "\"" << std::endl; _tileMap = TMS::TileMap::create( _options.url()->full(), profile, _options.format().value(), _options.tileSize().value(), _options.tileSize().value() ); } else { // Attempt to read the tile map parameters from a TMS TileMap XML tile on the server: _tileMap = TMS::TileMapReaderWriter::read( tmsURI.full(), _dbOptions.get() ); if (!_tileMap.valid()) { return Status::Error( Stringify() << "Failed to read tilemap from " << tmsURI.full() ); } OE_INFO << LC << "TMS tile map datestamp = " << DateTime(_tileMap->getTimeStamp()).asRFC1123() << std::endl; profile = _tileMap->createProfile(); if ( profile ) setProfile( profile ); } // Make sure we've established a profile by this point: if ( !profile ) { return Status::Error( Stringify() << "Failed to establish a profile for " << tmsURI.full() ); } // TileMap and profile are valid at this point. Build the tile sets. // Automatically set the min and max level of the TileMap if ( _tileMap->getTileSets().size() > 0 ) { OE_DEBUG << LC << "TileMap min/max " << _tileMap->getMinLevel() << ", " << _tileMap->getMaxLevel() << std::endl; if (_tileMap->getDataExtents().size() > 0) { for (DataExtentList::iterator itr = _tileMap->getDataExtents().begin(); itr != _tileMap->getDataExtents().end(); ++itr) { this->getDataExtents().push_back(*itr); } } else { //Push back a single area that encompasses the whole profile going up to the max level this->getDataExtents().push_back(DataExtent(profile->getExtent(), 0, _tileMap->getMaxLevel())); } } // set up the IO options so that we do not cache TMS tiles: CachePolicy::NO_CACHE.apply( _dbOptions.get() ); return STATUS_OK; }
/** override */ Status initialize( const osgDB::Options* dbOptions ) { osg::ref_ptr<const Profile> result; char sep = _options.url()->full().find_first_of('?') == std::string::npos? '?' : '&'; URI capUrl = _options.capabilitiesUrl().value(); if ( capUrl.empty() ) { capUrl = URI( _options.url()->full() + sep + std::string("SERVICE=WMS") + std::string("&VERSION=") + _options.wmsVersion().value() + std::string("&REQUEST=GetCapabilities") ); } //Try to read the WMS capabilities osg::ref_ptr<WMSCapabilities> capabilities = WMSCapabilitiesReader::read( capUrl, dbOptions ); if ( !capabilities.valid() ) { return Status::Error( Status::ResourceUnavailable, "Unable to read WMS GetCapabilities." ); } else { OE_INFO << LC << "Got capabilities from " << capUrl.full() << std::endl; } if ( _formatToUse.empty() && capabilities.valid() ) { _formatToUse = capabilities->suggestExtension(); OE_INFO << LC << "No format specified, capabilities suggested extension " << _formatToUse << std::endl; } if ( _formatToUse.empty() ) _formatToUse = "png"; if ( _srsToUse.empty() ) _srsToUse = "EPSG:4326"; std::string wmsFormatToUse = _options.wmsFormat().value(); //Initialize the WMS request prototype std::stringstream buf; // first the mandatory keys: buf << std::fixed << _options.url()->full() << sep << "SERVICE=WMS" << "&VERSION=" << _options.wmsVersion().value() << "&REQUEST=GetMap" << "&LAYERS=" << _options.layers().value() << "&FORMAT=" << ( wmsFormatToUse.empty() ? std::string("image/") + _formatToUse : wmsFormatToUse ) << "&STYLES=" << _options.style().value() << (_options.wmsVersion().value() == "1.3.0" ? "&CRS=" : "&SRS=") << _srsToUse << "&WIDTH="<< getPixelsPerTile() << "&HEIGHT=" << getPixelsPerTile() << "&BBOX=%lf,%lf,%lf,%lf"; // then the optional keys: if ( _options.transparent().isSet() ) buf << "&TRANSPARENT=" << (_options.transparent() == true ? "TRUE" : "FALSE"); _prototype = ""; _prototype = buf.str(); //OE_NOTICE << "Prototype " << _prototype << std::endl; osg::ref_ptr<SpatialReference> wms_srs = SpatialReference::create( _srsToUse ); // check for spherical mercator: if ( wms_srs.valid() && wms_srs->isEquivalentTo( osgEarth::Registry::instance()->getSphericalMercatorProfile()->getSRS() ) ) { result = osgEarth::Registry::instance()->getSphericalMercatorProfile(); } else if (wms_srs.valid() && wms_srs->isEquivalentTo( osgEarth::Registry::instance()->getGlobalGeodeticProfile()->getSRS())) { result = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } // Next, try to glean the extents from the layer list if ( capabilities.valid() ) { StringTokenizer tok(","); StringVector tized; tok.tokenize(_options.layers().value(), tized); for (StringVector::const_iterator itr = tized.begin(); itr != tized.end(); ++itr) { std::string layerName = *itr; WMSLayer* layer = capabilities->getLayerByName(layerName); if (layer) { // Get the lat/lon extents double minLon, minLat, maxLon, maxLat; layer->getLatLonExtents(minLon, minLat, maxLon, maxLat); GeoExtent wgs84Extent(SpatialReference::create("wgs84"), minLon, minLat, maxLon, maxLat); getDataExtents().push_back(DataExtent(wgs84Extent, 0)); } } // If we don't have a profile yet, transform the lat/lon extents to the requested srs and use it as the extents of the profile. if (!result.valid()) { const SpatialReference* srs = SpatialReference::create(_srsToUse); GeoExtent totalExtent(srs); for (DataExtentList::const_iterator itr = getDataExtents().begin(); itr != getDataExtents().end(); ++itr) { GeoExtent dataExtent = *itr; GeoExtent nativeExtent; dataExtent.transform(srs, nativeExtent); totalExtent.expandToInclude(nativeExtent); } result = Profile::create(srs, totalExtent.xMin(), totalExtent.yMin(), totalExtent.xMax(), totalExtent.yMax()); } } // Last resort: create a global extent profile (only valid for global maps) if ( !result.valid() && wms_srs->isGeographic()) { result = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); } #ifdef SUPPORT_JPL_TILESERVICE // JPL uses an experimental interface called TileService -- ping to see if that's what // we are trying to read: URI tsUrl = _options.tileServiceUrl().value(); if ( tsUrl.empty() ) { tsUrl = URI(_options.url()->full() + sep + std::string("request=GetTileService"), tsUrl.context()); } OE_INFO << LC << "Testing for JPL/TileService at " << tsUrl.full() << std::endl; osg::ref_ptr<TileService> tileService = TileServiceReader::read(tsUrl.full(), dbOptions); if (tileService.valid()) { OE_INFO << LC << "Found JPL/TileService spec" << std::endl; TileService::TilePatternList patterns; tileService->getMatchingPatterns( _options.layers().value(), _formatToUse, _options.style().value(), _srsToUse, getPixelsPerTile(), getPixelsPerTile(), patterns ); if (patterns.size() > 0) { result = tileService->createProfile( patterns ); _prototype = _options.url()->full() + sep + patterns[0].getPrototype(); } } else { OE_INFO << LC << "No JPL/TileService spec found; assuming standard WMS" << std::endl; } #endif // Use the override profile if one is passed in. if ( getProfile() == 0L ) { setProfile( result.get() ); } if ( getProfile() ) { OE_INFO << LC << "Profile=" << getProfile()->toString() << std::endl; // set up the cache options properly for a TileSource. _dbOptions = Registry::instance()->cloneOrCreateOptions( dbOptions ); return Status::OK(); } else { return Status::Error( "Unable to establish profile" ); } }
Status TMSTileSource::initialize(const osgDB::Options* dbOptions) { // local copy of options we can modify if necessary. _dbOptions = Registry::instance()->cloneOrCreateOptions(dbOptions); // see if the use passed in a profile const Profile* profile = getProfile(); // URI is mandatory. URI tmsURI = _options.url().value(); if ( tmsURI.empty() ) { return Status::Error( Status::ConfigurationError, "Fail: TMS driver requires a valid \"url\" property" ); } // A repo is writable only if it's local. if ( tmsURI.isRemote() ) { OE_DEBUG << LC << "Repo is remote; opening in read-only mode" << std::endl; } // Is this a new repo? (You can only create a new repo at a local non-archive URI.) bool isNewRepo = false; if (!tmsURI.isRemote() && !osgEarth::isPathToArchivedFile(tmsURI.full()) && !osgDB::fileExists(tmsURI.full()) ) { isNewRepo = true; // new repo REQUIRES a profile: if ( !profile ) { return Status::Error(Status::ConfigurationError, "Fail: profile required to create new TMS repo"); } } // Take the override profile if one is given if ( profile ) { OE_INFO << LC << "Using express profile \"" << getProfile()->toString() << "\" for URI \"" << tmsURI.base() << "\"" << std::endl; DataExtentList extents; _tileMap = TMS::TileMap::create( _options.url()->full(), profile, extents, _options.format().value(), 256, 256); //getPixelsPerTile(), //getPixelsPerTile() ); // If this is a new repo, write the tilemap file to disk now. if ( isNewRepo ) { if ( !_options.format().isSet() ) { return Status::Error(Status::ConfigurationError, "Missing required \"format\" property: e.g. png, jpg"); } TMS::TileMapReaderWriter::write( _tileMap.get(), tmsURI.full() ); OE_INFO << LC << "Created new TMS repo at " << tmsURI.full() << std::endl; } } else { // Attempt to read the tile map parameters from a TMS TileMap XML tile on the server: _tileMap = TMS::TileMapReaderWriter::read( tmsURI.full(), _dbOptions.get() ); if (!_tileMap.valid()) { return Status::Error( Status::ResourceUnavailable, Stringify() << "Failed to read configuration from " << tmsURI.full() ); } OE_INFO << LC << "TMS tile map datestamp = " << DateTime(_tileMap->getTimeStamp()).asRFC1123() << std::endl; profile = _tileMap->createProfile(); if ( profile ) setProfile( profile ); } // Make sure we've established a profile by this point: if ( !profile ) { return Status::Error( Stringify() << "Failed to establish a profile for " << tmsURI.full() ); } // resolve the writer if ( !tmsURI.isRemote() && !resolveWriter() ) { OE_WARN << LC << "Cannot create writer; writing disabled" << std::endl; } // TileMap and profile are valid at this point. Build the tile sets. // Automatically set the min and max level of the TileMap if ( _tileMap->getTileSets().size() > 0 ) { OE_DEBUG << LC << "TileMap min/max " << _tileMap->getMinLevel() << ", " << _tileMap->getMaxLevel() << std::endl; if (_tileMap->getDataExtents().size() > 0) { for (DataExtentList::iterator itr = _tileMap->getDataExtents().begin(); itr != _tileMap->getDataExtents().end(); ++itr) { this->getDataExtents().push_back(*itr); } } } return STATUS_OK; }