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 <osgEarth/Registry> 00021 #include <osgEarth/FileUtils> 00022 #include <osgEarthFeatures/FeatureSource> 00023 #include <osgEarthFeatures/Filter> 00024 #include <osgEarthFeatures/BufferFilter> 00025 #include <osgEarthFeatures/ScaleFilter> 00026 #include <osgEarthUtil/WFS> 00027 #include "WFSFeatureOptions" 00028 #include <osgEarthFeatures/OgrUtils> 00029 #include <osg/Notify> 00030 #include <osgDB/FileNameUtils> 00031 #include <osgDB/FileUtils> 00032 #include <list> 00033 #include <stdio.h> 00034 #include <stdlib.h> 00035 00036 #include <ogr_api.h> 00037 00038 #ifdef WIN32 00039 #include <windows.h> 00040 #endif 00041 00042 #define LC "[WFS FeatureSource] " 00043 00044 using namespace osgEarth; 00045 using namespace osgEarth::Util; 00046 using namespace osgEarth::Features; 00047 using namespace osgEarth::Drivers; 00048 00049 #define OGR_SCOPED_LOCK GDAL_SCOPED_LOCK 00050 00051 std::string getTempPath() 00052 { 00053 #if defined(WIN32) && !defined(__CYGWIN__) 00054 BOOL fSuccess = FALSE; 00055 00056 TCHAR lpTempPathBuffer[MAX_PATH]; 00057 00058 // Gets the temp path env string (no guarantee it's a valid path). 00059 DWORD dwRetVal = ::GetTempPath(MAX_PATH, // length of the buffer 00060 lpTempPathBuffer); // buffer for path 00061 00062 if (dwRetVal > MAX_PATH || (dwRetVal == 0)) 00063 { 00064 OE_NOTICE << "GetTempPath failed" << std::endl; 00065 return "."; 00066 } 00067 00068 return std::string(lpTempPathBuffer); 00069 #else 00070 return "/tmp/"; 00071 #endif 00072 } 00073 00074 std::string getTempName(const std::string& prefix="", const std::string& suffix="") 00075 { 00076 //tmpname is kind of busted on Windows, it always returns a file of the form \blah which gets put in your root directory but 00077 //oftentimes can't get opened by some drivers b/c it doesn't have a drive letter in front of it. 00078 bool valid = false; 00079 while (!valid) 00080 { 00081 std::stringstream ss; 00082 ss << prefix << "~" << rand() << suffix; 00083 if (!osgDB::fileExists(ss.str())) return ss.str(); 00084 } 00085 return ""; 00086 } 00087 00093 class WFSFeatureSource : public FeatureSource 00094 { 00095 public: 00096 WFSFeatureSource( const WFSFeatureOptions& options ) : FeatureSource( options ), 00097 _options( options ) 00098 { 00099 } 00100 00102 virtual ~WFSFeatureSource() 00103 { 00104 } 00105 00106 //override 00107 void initialize( const std::string& referenceURI ) 00108 { 00109 char sep = _options.url()->full().find_first_of('?') == std::string::npos? '?' : '&'; 00110 00111 std::string capUrl; 00112 if ( capUrl.empty() ) 00113 { 00114 capUrl = 00115 _options.url()->full() + 00116 sep + 00117 "SERVICE=WFS&VERSION=1.0.0&REQUEST=GetCapabilities"; 00118 } 00119 00120 _capabilities = WFSCapabilitiesReader::read( capUrl, 0L ); 00121 if ( !_capabilities.valid() ) 00122 { 00123 OE_WARN << "[osgEarth::WFS] Unable to read WFS GetCapabilities." << std::endl; 00124 //return; 00125 } 00126 else 00127 { 00128 OE_NOTICE << "[osgEarth::WFS] Got capabilities from " << capUrl << std::endl; 00129 } 00130 } 00131 00134 const FeatureProfile* createFeatureProfile() 00135 { 00136 FeatureProfile* result = NULL; 00137 if (_capabilities.valid()) 00138 { 00139 //Find the feature type by name 00140 osg::ref_ptr< WFSFeatureType > featureType = _capabilities->getFeatureTypeByName( _options.typeName().get() ); 00141 if (featureType.valid()) 00142 { 00143 if (featureType->getExtent().isValid()) 00144 { 00145 result = new FeatureProfile(featureType->getExtent()); 00146 00147 if (featureType->getTiled()) 00148 { 00149 result->setTiled( true ); 00150 result->setFirstLevel( featureType->getFirstLevel() ); 00151 result->setMaxLevel( featureType->getMaxLevel() ); 00152 result->setProfile( osgEarth::Profile::create(osgEarth::SpatialReference::create("epsg:4326"), featureType->getExtent().xMin(), featureType->getExtent().yMin(), featureType->getExtent().xMax(), featureType->getExtent().yMax(), 0, 1, 1) ); 00153 } 00154 } 00155 } 00156 } 00157 00158 if (!result) 00159 { 00160 result = new FeatureProfile(GeoExtent(SpatialReference::create( "epsg:4326" ), -180, -90, 180, 90)); 00161 } 00162 return result; 00163 } 00164 00165 void saveResponse(HTTPResponse& response, const std::string& filename) 00166 { 00167 std::ofstream fout; 00168 fout.open(filename.c_str(), std::ios::out | std::ios::binary); 00169 00170 std::istream& input_stream = response.getPartStream(0); 00171 input_stream.seekg (0, std::ios::end); 00172 int length = input_stream.tellg(); 00173 input_stream.seekg (0, std::ios::beg); 00174 00175 char *buffer = new char[length]; 00176 input_stream.read(buffer, length); 00177 fout.write(buffer, length); 00178 delete[] buffer; 00179 fout.close(); 00180 } 00181 00182 std::string getExtensionForMimeType(const std::string& mime) 00183 { 00184 //OGR is particular sometimes about the extension of files when it's reading them so it's good to have 00185 //the temp file have an appropriate extension 00186 if ((mime.compare("text/xml") == 0) || 00187 (mime.compare("text/xml; subtype=gml/2.1.2") == 0) || 00188 (mime.compare("text/xml; subtype=gml/3.1.1") == 0) 00189 ) 00190 { 00191 return ".xml"; 00192 } 00193 else if ((mime.compare("application/json") == 0) || 00194 (mime.compare("json") == 0) || 00195 00196 (mime.compare("application/x-javascript") == 0) || 00197 (mime.compare("text/javascript") == 0) || 00198 (mime.compare("text/x-javascript") == 0) || 00199 (mime.compare("text/x-json") == 0) 00200 ) 00201 { 00202 return ".json"; 00203 } 00204 return ""; 00205 } 00206 00207 void getFeatures(HTTPResponse &response, FeatureList& features) 00208 { 00209 //OE_NOTICE << "mimetype=" << response.getMimeType() << std::endl; 00210 //TODO: Handle more than just geojson... 00211 std::string ext = getExtensionForMimeType(response.getMimeType()); 00212 //Save the response to a temp file 00213 std::string tmpPath = getTempPath(); 00214 std::string name = getTempName(tmpPath, ext); 00215 saveResponse(response, name ); 00216 //OE_NOTICE << "Saving to " << name << std::endl; 00217 00218 //OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, &driver); 00219 OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, NULL); 00220 if (!ds) 00221 { 00222 OE_NOTICE << "Error opening data with contents " << std::endl 00223 << response.getPartAsString(0) << std::endl; 00224 } 00225 00226 OGRLayerH layer = OGR_DS_GetLayer(ds, 0); 00227 //Read all the features 00228 if (layer) 00229 { 00230 OGR_L_ResetReading(layer); 00231 OGRFeatureH feat_handle; 00232 while ((feat_handle = OGR_L_GetNextFeature( layer )) != NULL) 00233 { 00234 if ( feat_handle ) 00235 { 00236 Feature* f = OgrUtils::createFeature( feat_handle ); 00237 if ( f ) 00238 { 00239 features.push_back( f ); 00240 } 00241 OGR_F_Destroy( feat_handle ); 00242 } 00243 } 00244 } 00245 00246 //Destroy the datasource 00247 OGR_DS_Destroy( ds ); 00248 //Remove the temporary file 00249 remove( name.c_str() ); 00250 } 00251 00252 std::string createURL(const Symbology::Query& query) 00253 { 00254 std::stringstream buf; 00255 buf << _options.url()->full() << "?SERVICE=WFS&VERSION=1.0.0&REQUEST=getfeature"; 00256 buf << "&TYPENAME=" << _options.typeName().get(); 00257 00258 std::string outputFormat = "geojson"; 00259 if (_options.outputFormat().isSet()) outputFormat = _options.outputFormat().get(); 00260 buf << "&OUTPUTFORMAT=" << outputFormat; 00261 00262 if (_options.maxFeatures().isSet()) 00263 { 00264 buf << "&MAXFEATURES=" << _options.maxFeatures().get(); 00265 } 00266 00267 if (query.tileKey().isSet()) 00268 { 00269 buf << "&Z=" << query.tileKey().get().getLevelOfDetail() << 00270 "&X=" << query.tileKey().get().getTileX() << 00271 "&Y=" << query.tileKey().get().getTileY(); 00272 } 00273 else if (query.bounds().isSet()) 00274 { 00275 buf << "&BBOX=" << query.bounds().get().xMin() << "," << query.bounds().get().yMin() << "," 00276 << query.bounds().get().xMax() << "," << query.bounds().get().yMax(); 00277 } 00278 return buf.str(); 00279 } 00280 00281 //override 00282 FeatureCursor* createFeatureCursor( const Symbology::Query& query ) 00283 { 00284 std::string url = createURL( query ); 00285 OE_DEBUG << LC << "URL: " << url << std::endl; 00286 HTTPResponse response = HTTPClient::get(url); 00287 FeatureList features; 00288 if (response.isOK()) 00289 { 00290 getFeatures(response, features); 00291 std::string data = response.getPartAsString(0); 00292 } 00293 else 00294 { 00295 OE_INFO << "Error getting url " << url << std::endl; 00296 } 00297 return new FeatureListCursor( features ); 00298 } 00299 00300 virtual bool deleteFeature(FeatureID fid) 00301 { 00302 return false; 00303 } 00304 00305 virtual int getFeatureCount() const 00306 { 00307 return -1; 00308 } 00309 00310 virtual bool insertFeature(Feature* feature) 00311 { 00312 return false; 00313 } 00314 00320 virtual Feature* getFeature( FeatureID fid ) 00321 { 00322 return 0; 00323 } 00324 00325 virtual bool isWritable() const 00326 { 00327 return false; 00328 } 00329 00330 virtual const FeatureSchema& getSchema() const 00331 { 00332 //TODO: Populate the schema from the DescribeFeatureType call 00333 return _schema; 00334 } 00335 00336 virtual osgEarth::Symbology::Geometry::Type getGeometryType() const 00337 { 00338 return Geometry::TYPE_UNKNOWN; 00339 } 00340 00341 00342 00343 private: 00344 const WFSFeatureOptions _options; 00345 osg::ref_ptr< WFSCapabilities > _capabilities; 00346 FeatureSchema _schema; 00347 }; 00348 00349 00350 class WFSFeatureSourceFactory : public FeatureSourceDriver 00351 { 00352 public: 00353 WFSFeatureSourceFactory() 00354 { 00355 supportsExtension( "osgearth_feature_wfs", "WFS feature driver for osgEarth" ); 00356 } 00357 00358 virtual const char* className() 00359 { 00360 return "WFS Feature Reader"; 00361 } 00362 00363 virtual ReadResult readObject(const std::string& file_name, const Options* options) const 00364 { 00365 if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) 00366 return ReadResult::FILE_NOT_HANDLED; 00367 00368 return ReadResult( new WFSFeatureSource( getFeatureSourceOptions(options) ) ); 00369 } 00370 }; 00371 00372 REGISTER_OSGPLUGIN(osgearth_feature_wfs, WFSFeatureSourceFactory) 00373