osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth worldwind plugin - for the osgEarth toolkit 00003 * based on the yahoo plugin provided as part of the osgearth distribution 00004 * Produced by Matt Franklin 00005 * Contact: MattFranklin1 at gmail.com 00006 * 00007 * Please note that the use of this plugin requires the user to be accept 00008 * the license agreements provided by NASA 00009 * 00010 * This plugin is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU Lesser General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Lesser General Public License for more details. 00019 * 00020 */ 00021 #include "WorldWindOptions" 00022 00023 #include <iostream> 00024 #include <fstream> 00025 #include <stdio.h> 00026 00027 #include <osgEarth/TileSource> 00028 #include <osgEarth/ImageToHeightFieldConverter> 00029 #include <osgEarth/Registry> 00030 00031 #include <osgDB/FileNameUtils> 00032 #include <osgDB/FileUtils> 00033 #include <osgDB/Registry> 00034 #include <osgDB/ReadFile> 00035 #include <osgDB/WriteFile> 00036 #include <osgDB/Archive> 00037 00038 #include "zip.h" 00039 00040 #include <sstream> 00041 #include <iomanip> 00042 #include <iostream> 00043 #include <fstream> 00044 00045 #ifdef remove 00046 #undef remove 00047 #endif 00048 00049 #define LC "[WorldWind BIL] " 00050 00051 using namespace osgEarth; 00052 using namespace osgEarth::Drivers; 00053 00054 class WorldWindSource : public TileSource 00055 { 00056 public: 00057 WorldWindSource( const TileSourceOptions& options ) : TileSource( options ) 00058 { 00059 _options = options; 00060 } 00061 00062 public: 00063 void initialize( const std::string& referenceURI, const Profile* overrideProfile) 00064 { 00065 setProfile( Profile::create( 00066 "epsg:4326", 00067 -180.0, -90.0, 180.0, 90.0, 00068 "", 00069 18, 9 ) ); 00070 00071 if ( !_options.elevationCachePath().isSet() ) 00072 { 00073 OE_WARN << LC << "Elevation cache path is not set, but is required. No data will be available" << std::endl; 00074 } 00075 } 00076 00077 osg::HeightField* createHeightFieldFromBil(char* buf,int buflength) 00078 { 00079 osg::HeightField* hf = new osg::HeightField; 00080 //osg::notify(osg::NOTICE) << "Read heightfield image" << std::endl; 00081 hf->allocate(150, 150); 00082 00083 for( unsigned int row=0; row < 150; row++ ) 00084 { 00085 for( unsigned int col=0; col < 150; col++ ) 00086 { 00087 short* ptr = (short*)buf; 00088 short val = ptr[col + ((150-row -1)*150)]; 00089 00090 hf->setHeight( col, row, (float)val * 0.3048 ); 00091 } 00092 } 00093 return hf; 00094 } 00095 00096 public: 00097 osg::Image* createImage( const TileKey& key, ProgressCallback* progress) 00098 { 00099 // NYI - eventually, consolidate the "tileservice" plugin into this one //GW 00100 return NULL; 00101 } 00102 00103 00104 osg::HeightField* createHeightField( const TileKey& key, ProgressCallback* progress) 00105 { 00106 if ( *_options.maxLOD() <= key.getLevelOfDetail()) return NULL; 00107 if ( !_options.elevationCachePath().isSet() ) return NULL; 00108 00109 osg::HeightField *hf = NULL; 00110 std::string cachefilepath = *_options.elevationCachePath() + "/" + createCachePath(key); 00111 std::string cachefilename = createCacheName(key) + ".bil"; 00112 std::string fullcachefilename = cachefilepath + "/" + cachefilename; 00113 OE_DEBUG << LC << "Cached name " << fullcachefilename << std::endl; 00114 if (osgDB::fileExists(fullcachefilename)) 00115 { 00116 // read file 00117 std::ifstream fin; 00118 fin.open(fullcachefilename.c_str(), std::ios::in | std::ios::binary); 00119 if (!fin) 00120 { 00121 OE_WARN << LC << "Coud not open elevation cache " << fullcachefilename << ", maybe a permissions problem" << std::endl; 00122 _options.elevationCachePath().unset(); 00123 return NULL; 00124 } 00125 int fullsize = 150*150*2; 00126 char *buf = new char[fullsize]; 00127 OE_DEBUG << LC << "Loading from cache " << fullcachefilename << std::endl; 00128 if ( !fin.read(buf, fullsize)) 00129 { 00130 OE_WARN << LC << "Coud not read from elevation cache " << fullcachefilename << ", file may be corrupt" << std::endl; 00131 delete[] buf; 00132 fin.close(); 00133 _options.elevationCachePath().unset(); 00134 return NULL; 00135 } 00136 hf = createHeightFieldFromBil((char*)buf,fullsize); 00137 delete[] buf; 00138 fin.close(); 00139 } 00140 else // cached BIL file doesn't exist, try to download and cache it. 00141 { 00142 // download file 00143 HTTPResponse out_response; 00144 std::string URI = createURI(key); 00145 00146 OE_DEBUG << "Requesting " << URI << std::endl; 00147 out_response = HTTPClient::get( URI, 0L, progress ); 00148 00149 if ( !out_response.isOK() ) 00150 { 00151 OE_NOTICE << "No Response received for " << URI << std::endl; 00152 return NULL; 00153 } 00154 00155 // store downloaded part as a zip file 00156 // Useful to store a local copy as the same file is requested many times 00157 unsigned int part_num = out_response.getNumParts() > 1? 1 : 0; 00158 std::string zipfilename; 00159 out_response.getPartHeader(part_num, zipfilename); 00160 std::istream& input_stream = out_response.getPartStream( part_num ); 00161 00162 if ( !osgDB::fileExists(cachefilepath) ) 00163 { 00164 osgDB::makeDirectory(cachefilepath); 00165 } 00166 std::ofstream fout; 00167 std::string tempname = fullcachefilename + ".zip"; 00168 fout.open(tempname.c_str(), std::ios::out | std::ios::binary); 00169 00170 if ( !fout ) 00171 { 00172 OE_WARN << LC << "Could not write zip file to " << tempname << std::endl; 00173 _options.elevationCachePath().unset(); 00174 return NULL; 00175 } 00176 input_stream.seekg (0, std::ios::end); 00177 int length = input_stream.tellg(); 00178 input_stream.seekg (0, std::ios::beg); 00179 00180 char *buffer = new char[length]; 00181 input_stream.read(buffer, length); 00182 fout.write(buffer, length); 00183 delete[] buffer; 00184 fout.close(); 00185 00186 00187 //Unzip the file 00188 int err; 00189 00190 //Open the zip file 00191 struct zip* pZip = zip_open(tempname.c_str(), ZIP_CHECKCONS, &err); 00192 if (pZip) 00193 { 00194 //List the files 00195 int numFiles = zip_get_num_files(pZip); 00196 //OE_DEBUG << tempname << " has " << numFiles << " files " << std::endl; 00197 /*for (int i = 0; i < numFiles; ++i) 00198 { 00199 OE_NOTICE << i << ": " << zip_get_name(pZip, i, 0) << std::endl; 00200 }*/ 00201 00202 //Find the index within the zip file for the given zip entry 00203 int zipIndex = 0; 00204 00205 //Open the first file for reading 00206 zip_file* pZipFile = zip_fopen_index(pZip, 0, 0); 00207 00208 if (pZipFile) 00209 { 00210 //Read the data from the entry into a std::string 00211 int dataSize = 0; 00212 std::string data; 00213 do{ 00214 char* buf = new char[1024]; 00215 dataSize = zip_fread(pZipFile, buf, 1024); 00216 if (dataSize == 0) 00217 { 00218 delete [](buf); 00219 buf = NULL; 00220 } 00221 if (buf) 00222 { 00223 data.append((char*)buf, dataSize); 00224 } 00225 }while (dataSize > 0); 00226 00227 //Close the zip entry and the actual zip file itself 00228 zip_fclose(pZipFile); 00229 zip_close(pZip); 00230 00231 //Write the BIL file to the cache 00232 fout.open(fullcachefilename.c_str(), std::ios::out | std::ios::binary); 00233 if ( !fout ) 00234 { 00235 std::cout << "Cannot write bil file"<< std::endl; 00236 return NULL; 00237 } 00238 00239 fout.write((char*)data.c_str(), data.size()); 00240 fout.close(); 00241 00242 hf = createHeightFieldFromBil((char*)data.c_str(),data.size()); 00243 // delete zip file as it has now been processed 00244 remove(tempname.c_str()); 00245 } 00246 } 00247 } 00248 return hf; 00249 } 00250 00251 std::string createCachePath( const TileKey& key ) const 00252 { 00253 unsigned int x, y; 00254 key.getTileXY(x, y); 00255 00256 unsigned int lod = key.getLevelOfDetail(); 00257 00258 std::stringstream buf; 00259 buf << "" << lod 00260 << "/" << std::setw(4) << std::setfill('0') << x; 00261 std::string bufStr; 00262 bufStr = buf.str(); 00263 return bufStr; 00264 } 00265 00266 std::string createCacheName( const TileKey& key ) const 00267 { 00268 unsigned int x, y; 00269 key.getTileXY(x, y); 00270 00271 unsigned int lod = key.getLevelOfDetail(); 00272 00273 // flip the y based on level 00274 int flippedy = ((9 * powf((int)2,(int)lod)) - 1) - y; 00275 //printf("Key %i, %i, %i\n", lod,x,flippedy); 00276 00277 std::stringstream buf; 00278 buf << "" << std::setw(4) << std::setfill('0') << x 00279 << "_" << std::setw(4) << std::setfill('0') << flippedy; 00280 std::string bufStr; 00281 bufStr = buf.str(); 00282 return bufStr; 00283 } 00284 00285 std::string createURI( const TileKey& key ) const 00286 { 00287 unsigned int x, y; 00288 key.getTileXY(x, y); 00289 00290 unsigned int lod = key.getLevelOfDetail(); 00291 00292 // flip the y based on level 00293 int flippedy = ((9 * powf((int)2,(int)lod)) - 1) - y; 00294 00295 std::stringstream buf; 00296 buf << *_options.elevationURL() // "http://worldwind25.arc.nasa.gov/wwelevation/wwelevation.aspx?T=srtm30pluszip" 00297 << "&L=" << lod 00298 << "&X=" << x 00299 << "&Y=" << flippedy; 00300 std::string bufStr; 00301 bufStr = buf.str(); 00302 return bufStr; 00303 } 00304 00305 virtual int getPixelsPerTile() const 00306 { 00307 return 150; 00308 } 00309 00310 virtual std::string getExtension() const 00311 { 00312 return "bil"; 00313 } 00314 00315 private: 00316 WorldWindOptions _options; 00317 }; 00318 00319 00320 class ReaderWriterWorldWind : public TileSourceDriver 00321 { 00322 public: 00323 ReaderWriterWorldWind() {} 00324 00325 virtual const char* className() 00326 { 00327 return "WorldWind Reader"; 00328 } 00329 00330 virtual bool acceptsExtension(const std::string& extension) const 00331 { 00332 return osgDB::equalCaseInsensitive( extension, "osgearth_WorldWind" ); 00333 } 00334 00335 virtual ReadResult readObject(const std::string& file_name, const Options* opt) const 00336 { 00337 std::string ext = osgDB::getFileExtension( file_name ); 00338 if ( !acceptsExtension( ext ) ) 00339 { 00340 return ReadResult::FILE_NOT_HANDLED; 00341 } 00342 00343 return new WorldWindSource( getTileSourceOptions(opt) ); 00344 } 00345 }; 00346 00347 REGISTER_OSGPLUGIN(osgearth_WorldWind, ReaderWriterWorldWind)