osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 00020 #include "WCS11Source.h" 00021 #include <osgEarth/HTTPClient> 00022 #include <osgEarth/ImageToHeightFieldConverter> 00023 #include <osgEarth/Registry> 00024 #include <osg/Notify> 00025 #include <osgDB/Registry> 00026 #include <iostream> 00027 #include <stdlib.h> 00028 00029 using namespace osgEarth; 00030 00031 00032 WCS11Source::WCS11Source( const TileSourceOptions& options ) : 00033 TileSource( options ), 00034 _options(options) 00035 { 00036 _covFormat = _options.format().value(); 00037 00038 if ( _covFormat.empty() ) 00039 _covFormat = "image/GeoTIFF"; 00040 00041 _osgFormat = "tif"; 00042 } 00043 00044 00045 void WCS11Source::initialize( const std::string& referenceURI, const Profile* overrideProfile) 00046 { 00047 if (overrideProfile) 00048 { 00049 setProfile( overrideProfile ); 00050 } 00051 else 00052 { 00053 //TODO: once we read GetCapabilities.. this will change.. 00054 setProfile(osgEarth::Registry::instance()->getGlobalGeodeticProfile()); 00055 } 00056 } 00057 00058 00059 std::string 00060 WCS11Source::getExtension() const 00061 { 00062 return "tif"; 00063 } 00064 00065 00066 osg::Image* 00067 WCS11Source::createImage( const TileKey& key, 00068 ProgressCallback* progress) 00069 { 00070 HTTPRequest request = createRequest( key ); 00071 00072 OE_INFO << "[osgEarth::WCS1.1] Key=" << key.str() << " URL = " << request.getURL() << std::endl; 00073 00074 double lon0,lat0,lon1,lat1; 00075 key.getExtent().getBounds( lon0, lat0, lon1, lat1 ); 00076 00077 // download the data 00078 HTTPResponse response = HTTPClient::get( request, 0L, progress ); //getOptions(), progress ); 00079 if ( !response.isOK() ) 00080 { 00081 OE_WARN << "[osgEarth::WCS1.1] WARNING: HTTP request failed" << std::endl; 00082 return NULL; 00083 } 00084 00085 //TODO: Make WCS driver use progress callback 00086 unsigned int part_num = response.getNumParts() > 1? 1 : 0; 00087 std::istream& input_stream = response.getPartStream( part_num ); 00088 00089 //TODO: un-hard-code TIFFs 00090 osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension( "tiff" ); 00091 00092 if ( !reader ) 00093 { 00094 OE_NOTICE << "[osgEarth::WCS1.1] WARNING: no reader for \"tiff\"" << std::endl; 00095 return NULL; 00096 } 00097 00098 osgDB::ReaderWriter::ReadResult result = reader->readImage( input_stream ); //, getOptions() ); 00099 if ( !result.success() ) 00100 { 00101 OE_NOTICE << "[osgEarth::WCS1.1] WARNING: readImage() failed for Reader " << reader->getName() << std::endl; 00102 return NULL; 00103 } 00104 00105 osg::Image* image = result.getImage(); 00106 //OE_NOTICE << "Returned grid is " << image->s() << "x" << image->t() << std::endl; 00107 if ( image ) image->ref(); 00108 return image; 00109 } 00110 00111 00112 osg::HeightField* 00113 WCS11Source::createHeightField( const TileKey& key, 00114 ProgressCallback* progress) 00115 { 00116 osg::HeightField* field = NULL; 00117 00118 osg::ref_ptr<osg::Image> image = createImage( key, progress ); 00119 if ( image.valid() ) 00120 { 00121 ImageToHeightFieldConverter conv; 00122 conv.setRemoveNoDataValues( true ); 00123 field = conv.convert( image.get() ); 00124 } 00125 00126 return field; 00127 } 00128 00129 /* 00130 http://server/ArcGIS/services/WorldElevation/MapServer/WCSServer 00131 ?SERVICE=WCS 00132 &VERSION=1.1.0 00133 &REQUEST=GetCoverage 00134 &IDENTIFIER=1 00135 &FORMAT=image/GeoTIFF 00136 &BOUNDINGBOX=-180,-90,0,90,urn:ogc:def:crs:EPSG::4326 // (sic - coord ordering bug in ESRI) 00137 &RangeSubset=Field_1:bilinear[Band[1]] 00138 &GridBaseCRS=urn:ogc:def:crs:EPSG::4326 00139 &GridCS=urn:ogc:def:crs:EPSG::4326 00140 &GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs 00141 &GridOrigin=-180,90 00142 &GridOffsets=6,-6 00143 */ 00144 00145 00146 HTTPRequest 00147 WCS11Source::createRequest( const TileKey& key ) const 00148 { 00149 std::stringstream buf; 00150 00151 double lon_min, lat_min, lon_max, lat_max; 00152 key.getExtent().getBounds( lon_min, lat_min, lon_max, lat_max ); 00153 00154 int lon_samples = _options.tileSize().value(); 00155 int lat_samples = _options.tileSize().value(); 00156 double lon_interval = (lon_max-lon_min)/(double)(lon_samples-1); 00157 double lat_interval = (lat_max-lat_min)/(double)(lat_samples-1); 00158 00159 HTTPRequest req( _options.url()->full() ); 00160 00161 req.addParameter( "SERVICE", "WCS" ); 00162 req.addParameter( "VERSION", "1.1.0" ); 00163 req.addParameter( "REQUEST", "GetCoverage" ); 00164 req.addParameter( "IDENTIFIER", _options.identifier().value() ); 00165 req.addParameter( "FORMAT", _covFormat ); 00166 00167 req.addParameter( "GridBaseCRS", "urn:ogc:def:crs:EPSG::4326" ); 00168 req.addParameter( "GridCS", "urn:ogc:def:crs:EPSG::4326" ); 00169 req.addParameter( "GridType", "urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs" ); 00170 00171 // IMPORTANT NOTE: 00172 // For WCS1.1+, the BOUNDINGBOX for geographic CRS's (like WGS84) are expressed 00173 // at minlat,minlon,maxlat,maxlon instead of the usual minx,miny,maxx,maxy. 00174 // So we will somehow need to figure out whether the CRS is geographic. 00175 // 00176 // MORE IMPORTANT NOTE: 00177 // ESRI's ArcGIS WCS Server doesn't obey the above rule. Their server expects 00178 // minx,miny,maxx,maxy no matter what ... 00179 00180 // Hack to guess whether it's an ArcGIS Server: 00181 buf.str(""); 00182 00183 //bool use_legacy_geog_bbox_encoding = _url.find( "/MapServer/WCSServer" ) != std::string::npos; 00184 //if ( use_legacy_geog_bbox_encoding ) 00185 // buf << lon_min << "," << lat_min << "," << lon_max << "," << lat_max; 00186 //else 00187 // buf << lat_min << "," << lon_min << "," << lat_max << "," << lon_max; 00188 //buf << ",urn:ogc:def:crs:EPSG::4326"; 00189 00190 double halfLon = lon_interval/2.0; 00191 double halfLat = lat_interval/2.0; 00192 00193 //We need to shift the bounding box out by half a pixel in all directions so that the center of the edge pixels lie on 00194 //the edge of this TileKey's extents. Doing this makes neighboring tiles have the same elevation values so there is no need 00195 //to run the tile edge normalization code. 00196 buf << lon_min - halfLon << "," << lat_min - halfLat << "," << lon_max + halfLon << "," << lat_max + halfLat << ",EPSG:4326"; 00197 std::string bufStr; 00198 bufStr = buf.str(); 00199 req.addParameter( "BOUNDINGBOX", bufStr ); 00200 00201 double originX = lon_min; 00202 double originY = lat_max; 00203 00204 buf.str(""); 00205 buf << originX << "," << originY; 00206 bufStr = buf.str(); 00207 req.addParameter( "GridOrigin", bufStr ); 00208 00209 buf.str(""); 00210 buf << lon_interval << "," << lat_interval; // note: top-down 00211 //buf << lon_interval << "," << lat_interval; 00212 bufStr = buf.str(); 00213 req.addParameter( "GridOffsets", bufStr ); 00214 00215 if ( !_options.rangeSubset()->empty() ) 00216 req.addParameter( "RangeSubset", _options.rangeSubset().value() ); 00217 00218 return req; 00219 }