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 #include "KMZArchive" 00020 #ifdef SUPPORT_KMZ 00021 00022 #include <osgDB/ReaderWriter> 00023 #include <osgDB/FileNameUtils> 00024 #include <osgDB/ObjectWrapper> 00025 #include <osgEarth/HTTPClient> 00026 #include <osgEarth/Registry> 00027 #include <osgEarth/ThreadingUtils> 00028 00029 #define LC "[KMZArchive] " 00030 00031 using namespace osgEarth; 00032 00033 namespace 00034 { 00035 URI downloadToCache( const URI& uri ) 00036 { 00037 // get a handle on the file cache. This is a temporary setup just to get things 00038 // working. 00039 static Threading::Mutex s_fcMutex; 00040 00041 static URIContext s_cache; 00042 if ( s_cache.empty() ) 00043 { 00044 Threading::ScopedMutexLock exclusiveLock(s_fcMutex); 00045 if ( s_cache.empty() ) 00046 { 00047 const char* osgCacheDir = ::getenv("OSG_FILE_CACHE"); 00048 if ( osgCacheDir ) 00049 s_cache = URIContext(std::string(osgCacheDir) + "/"); 00050 else 00051 s_cache = URIContext("osgearth_kmz_cache/"); 00052 } 00053 } 00054 00055 URI cachedFile( osgDB::getSimpleFileName(uri.full()), s_cache); 00056 00057 if ( !osgDB::fileExists(cachedFile.full()) ) 00058 { 00059 osgDB::makeDirectoryForFile(cachedFile.full()); 00060 HTTPClient::download( uri.full(), cachedFile.full() ); 00061 } 00062 00063 if ( osgDB::fileExists(cachedFile.full()) ) 00064 return cachedFile; 00065 00066 return URI(); 00067 } 00068 } 00069 00070 //------------------------------------------------------------------------ 00071 00072 KMZArchive::KMZArchive( const URI& archiveURI ) : 00073 _archiveURI( archiveURI ), 00074 _buf( 0L ), 00075 _bufsize( 1024000 ) 00076 { 00077 supportsExtension( "kmz", "KMZ" ); 00078 00079 // download the KMZ to a local cache - cannot open zip files remotely. 00080 URI localURI( archiveURI ); 00081 if ( osgDB::containsServerAddress(archiveURI.full()) ) 00082 { 00083 localURI = downloadToCache( archiveURI ); 00084 } 00085 00086 _uf = unzOpen( localURI.full().c_str() ); 00087 _buf = (void*)new char[_bufsize]; 00088 } 00089 00090 KMZArchive::~KMZArchive() 00091 { 00092 if ( _buf ) 00093 delete [] _buf; 00094 } 00095 00096 void 00097 KMZArchive::close() 00098 { 00099 _uf = 0; 00100 } 00101 00103 std::string 00104 KMZArchive::getArchiveFileName() const 00105 { 00106 return _archiveURI.base(); 00107 } 00108 00110 std::string 00111 KMZArchive::getMasterFileName() const 00112 { 00113 return "doc.kml"; 00114 } 00115 00117 bool 00118 KMZArchive::fileExists(const std::string& filename) const 00119 { 00120 //todo 00121 return false; 00122 } 00123 00125 osgDB::FileType 00126 KMZArchive::getFileType(const std::string& filename) const 00127 { 00128 return osgDB::REGULAR_FILE; 00129 } 00130 00132 bool 00133 KMZArchive::getFileNames(FileNameList& fileNames) const 00134 { 00135 return false; 00136 } 00137 00140 osgDB::DirectoryContents 00141 KMZArchive::getDirectoryContents(const std::string& dirName) const 00142 { 00143 return osgDB::DirectoryContents(); 00144 } 00145 00147 bool 00148 KMZArchive::readToBuffer( const std::string& fileInZip, std::ostream& iobuf ) const 00149 { 00150 // help from: 00151 // http://bytes.com/topic/c/answers/764381-reading-contents-zip-files 00152 00153 int err = UNZ_OK; 00154 unz_file_info file_info; 00155 char filename_inzip[2048]; 00156 bool got_file_info = false; 00157 00158 if ( _uf == 0 ) 00159 { 00160 OE_WARN << LC << "Archive is not open." << std::endl; 00161 return false; 00162 } 00163 00164 if ( fileInZip == ".kml" ) 00165 { 00166 // special case; first try the master file (doc.kml), then failing that, look 00167 // for the first KML file in the archive. 00168 if ( unzLocateFile( _uf, "doc.kml", 0 ) != 0 ) 00169 { 00170 if ( unzGoToFirstFile( _uf ) != UNZ_OK ) 00171 { 00172 OE_WARN << LC << "Archive is empty" << std::endl; 00173 return false; 00174 } 00175 00176 while( err == UNZ_OK ) 00177 { 00178 if ( unzGetCurrentFileInfo( _uf, &file_info, filename_inzip, sizeof(filename_inzip), 0L, 0, 0L, 0) ) 00179 { 00180 OE_WARN << LC << "Error with zipfile " << _archiveURI.base() << std::endl; 00181 return false; 00182 } 00183 00184 got_file_info = true; 00185 std::string lc = osgEarth::toLower( std::string(filename_inzip) ); 00186 if ( endsWith( lc, ".kml" ) ) 00187 { 00188 break; 00189 } 00190 00191 err = unzGoToNextFile( _uf ); 00192 } 00193 00194 if ( err != UNZ_OK ) 00195 { 00196 OE_WARN << LC << "No KML file found in archive" << std::endl; 00197 return false; 00198 } 00199 } 00200 } 00201 00202 else if ( unzLocateFile( _uf, fileInZip.c_str(), 0 ) ) 00203 { 00204 OE_WARN << LC << "Failed to locate '" << fileInZip << "' in '" << _archiveURI.base() << "'" << std::endl; 00205 return false; 00206 } 00207 00208 if ( !got_file_info ) 00209 { 00210 if ( unzGetCurrentFileInfo( _uf, &file_info, filename_inzip, sizeof(filename_inzip), 0L, 0, 0L, 0) ) 00211 { 00212 OE_WARN << LC << "Error with zipfile " << _archiveURI.base() << std::endl; 00213 return false; 00214 } 00215 } 00216 00217 err = unzOpenCurrentFilePassword( _uf, 0L ); 00218 if ( err != UNZ_OK ) 00219 { 00220 OE_WARN << LC << "unzOpenCurrentFilePassword failed" << std::endl; 00221 return false; 00222 } 00223 00224 do 00225 { 00226 err = unzReadCurrentFile( _uf, _buf, _bufsize ); 00227 if ( err < 0 ) 00228 { 00229 OE_WARN << LC << "Error in unzReadCurrentFile" << std::endl; 00230 break; 00231 } 00232 if ( err > 0 ) 00233 { 00234 for( unsigned i=0; i<err; ++i ) 00235 { 00236 iobuf.put( *(((char*)_buf)+i) ); 00237 } 00238 } 00239 } 00240 while( err > 0 ); 00241 00242 err = unzCloseCurrentFile( _uf ); 00243 if ( err != UNZ_OK ) 00244 { 00245 //ignore it... 00246 } 00247 00248 return true; 00249 } 00250 00251 osgDB::ReaderWriter::ReadResult 00252 KMZArchive::readImage(const std::string& filename, const osgDB::Options* options) const 00253 { 00254 osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( 00255 osgDB::getLowerCaseFileExtension( filename ) ); 00256 if ( rw ) 00257 { 00258 std::stringstream iobuf; 00259 if ( readToBuffer( filename, iobuf ) ) 00260 { 00261 osg::ref_ptr<osgDB::Options> myOptions = Registry::instance()->cloneOrCreateOptions(options); 00262 URIContext(*_archiveURI).add(filename).store( myOptions.get() ); 00263 return rw->readImage( iobuf, options ); 00264 } 00265 else return ReadResult::ERROR_IN_READING_FILE; 00266 } 00267 else return ReadResult::FILE_NOT_HANDLED; 00268 } 00269 00270 osgDB::ReaderWriter::ReadResult 00271 KMZArchive::readNode(const std::string& filename, const osgDB::Options* options) const 00272 { 00273 osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( 00274 osgDB::getLowerCaseFileExtension( filename ) ); 00275 if ( rw ) 00276 { 00277 std::stringstream iobuf; 00278 if ( readToBuffer( filename, iobuf ) ) 00279 { 00280 osg::ref_ptr<osgDB::Options> myOptions = Registry::instance()->cloneOrCreateOptions(options); 00281 URIContext(*_archiveURI).add(filename).store( myOptions.get() ); 00282 return rw->readNode( iobuf, options ); 00283 } 00284 else return ReadResult::ERROR_IN_READING_FILE; 00285 } 00286 else return ReadResult::FILE_NOT_HANDLED; 00287 } 00288 00289 osgDB::ReaderWriter::ReadResult 00290 KMZArchive::readObject(const std::string& filename, const osgDB::Options* options) const 00291 { 00292 osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( 00293 osgDB::getLowerCaseFileExtension( filename ) ); 00294 if ( rw ) 00295 { 00296 std::stringstream iobuf; 00297 if ( readToBuffer( filename, iobuf ) ) 00298 { 00299 osg::ref_ptr<osgDB::Options> myOptions = Registry::instance()->cloneOrCreateOptions(options); 00300 URIContext(*_archiveURI).add(filename).store( myOptions.get() ); 00301 return rw->readObject( iobuf, myOptions.get() ); 00302 } 00303 else return ReadResult::ERROR_IN_READING_FILE; 00304 } 00305 else return ReadResult::FILE_NOT_HANDLED; 00306 } 00307 00308 #endif // SUPPORT_KMZ