1 /* -*-c++-*- */ 2 /* osgEarth - Geospatial SDK for OpenSceneGraph 3 * Copyright 2008-2014 Pelican Mapping 4 * http://osgearth.org 5 * 6 * osgEarth is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/> 18 */ 19 #include "MVTFeatureOptions" 20 21 #include <osgEarth/Registry> 22 #include <osgEarth/FileUtils> 23 #include <osgEarth/GeoData> 24 #include <osgEarthFeatures/FeatureCursor> 25 #include <osgEarthFeatures/FeatureSource> 26 #include <osgEarthFeatures/MVT> 27 #include <osgEarthFeatures/Filter> 28 #include <osg/Notify> 29 #include <osgDB/FileNameUtils> 30 #include <osgDB/FileUtils> 31 #include <list> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <sqlite3.h> 35 36 37 #define LC "[MVT FeatureSource] " 38 39 using namespace osgEarth; 40 using namespace osgEarth::Features; 41 using namespace osgEarth::Drivers; 42 43 44 class MVTFeatureSource : public FeatureSource 45 { 46 public: MVTFeatureSource(const MVTFeatureOptions & options)47 MVTFeatureSource(const MVTFeatureOptions& options ) : 48 FeatureSource( options ), 49 _options ( options ), 50 _minLevel(0), 51 _maxLevel(14), 52 _database(0L) 53 { 54 _compressor = osgDB::Registry::instance()->getObjectWrapperManager()->findCompressor("zlib"); 55 if (!_compressor.valid()) 56 { 57 OE_WARN << LC << "Failed to get zlib compressor" << std::endl; 58 } 59 } 60 61 /** Destruct the object, cleaning up and OGR handles. */ ~MVTFeatureSource()62 virtual ~MVTFeatureSource() 63 { 64 //nop 65 } 66 createFeatureCursor(const Symbology::Query & query,ProgressCallback * progress)67 FeatureCursor* createFeatureCursor( const Symbology::Query& query, ProgressCallback* progress ) 68 { 69 if (!query.tileKey().isSet()) 70 { 71 OE_WARN << LC << "No tile key in query; no features will be returned\n"; 72 return 0L; 73 } 74 75 TileKey key = *query.tileKey(); 76 77 int z = key.getLevelOfDetail(); 78 int tileX = key.getTileX(); 79 int tileY = key.getTileY(); 80 81 unsigned int numRows, numCols; 82 key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows); 83 tileY = numRows - tileY - 1; 84 85 //Get the image 86 sqlite3_stmt* select = NULL; 87 std::string queryStr = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?"; 88 int rc = sqlite3_prepare_v2( _database, queryStr.c_str(), -1, &select, 0L ); 89 if ( rc != SQLITE_OK ) 90 { 91 OE_WARN << LC << "Failed to prepare SQL: " << queryStr << "; " << sqlite3_errmsg(_database) << std::endl; 92 return NULL; 93 } 94 95 bool valid = true; 96 97 sqlite3_bind_int( select, 1, z ); 98 sqlite3_bind_int( select, 2, tileX ); 99 sqlite3_bind_int( select, 3, tileY ); 100 101 rc = sqlite3_step( select ); 102 103 FeatureList features; 104 105 if ( rc == SQLITE_ROW) 106 { 107 // the pointer returned from _blob gets freed internally by sqlite, supposedly 108 const char* data = (const char*)sqlite3_column_blob( select, 0 ); 109 int dataLen = sqlite3_column_bytes( select, 0 ); 110 std::string dataBuffer( data, dataLen ); 111 std::stringstream in(dataBuffer); 112 MVT::read(in, key, features); 113 } 114 else 115 { 116 OE_DEBUG << LC << "SQL QUERY failed for " << queryStr << ": " << std::endl; 117 valid = false; 118 } 119 120 sqlite3_finalize( select ); 121 122 // apply filters before returning. 123 applyFilters( features, query.tileKey()->getExtent() ); 124 125 // If we have any features and we have an fid attribute, override the fid of the features 126 if (_options.fidAttribute().isSet()) 127 { 128 for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr) 129 { 130 std::string attr = itr->get()->getString(_options.fidAttribute().get()); 131 FeatureID fid = as<long>(attr, 0); 132 itr->get()->setFID( fid ); 133 } 134 } 135 136 if (!features.empty()) 137 { 138 //OE_NOTICE << "Returning " << features.size() << " features" << std::endl; 139 return new FeatureListCursor(features); 140 } 141 142 return 0; 143 } 144 145 /** 146 * Gets the Feature with the given FID 147 * @returns 148 * The Feature with the given FID or NULL if not found. 149 */ getFeature(FeatureID fid)150 virtual Feature* getFeature( FeatureID fid ) 151 { 152 return 0; 153 } 154 isWritable() const155 virtual bool isWritable() const 156 { 157 return false; 158 } 159 getSchema() const160 virtual const FeatureSchema& getSchema() const 161 { 162 //TODO: Populate the schema from the DescribeFeatureType call 163 return _schema; 164 } 165 getGeometryType() const166 virtual osgEarth::Symbology::Geometry::Type getGeometryType() const 167 { 168 return Geometry::TYPE_UNKNOWN; 169 } 170 getMetaData(const std::string & key,std::string & value)171 bool getMetaData(const std::string& key, std::string& value) 172 { 173 //get the metadata 174 sqlite3_stmt* select = NULL; 175 std::string query = "SELECT value from metadata where name = ?"; 176 int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &select, 0L ); 177 if ( rc != SQLITE_OK ) 178 { 179 OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl; 180 return false; 181 } 182 183 184 bool valid = true; 185 std::string keyStr = std::string( key ); 186 rc = sqlite3_bind_text( select, 1, keyStr.c_str(), keyStr.length(), SQLITE_STATIC ); 187 if (rc != SQLITE_OK ) 188 { 189 OE_WARN << LC << "Failed to bind text: " << query << "; " << sqlite3_errmsg(_database) << std::endl; 190 return false; 191 } 192 193 rc = sqlite3_step( select ); 194 if ( rc == SQLITE_ROW) 195 { 196 value = (char*)sqlite3_column_text( select, 0 ); 197 } 198 else 199 { 200 OE_DEBUG << LC << "SQL QUERY failed for " << query << ": " << std::endl; 201 valid = false; 202 } 203 204 sqlite3_finalize( select ); 205 return valid; 206 } 207 computeLevels()208 void computeLevels() 209 { 210 211 osg::Timer_t startTime = osg::Timer::instance()->tick(); 212 sqlite3_stmt* select = NULL; 213 std::string query = "SELECT min(zoom_level), max(zoom_level) from tiles"; 214 int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &select, 0L ); 215 if ( rc != SQLITE_OK ) 216 { 217 OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl; 218 } 219 220 rc = sqlite3_step( select ); 221 if ( rc == SQLITE_ROW) 222 { 223 _minLevel = sqlite3_column_int( select, 0 ); 224 _maxLevel = sqlite3_column_int( select, 1 ); 225 OE_DEBUG << LC << "Min=" << _minLevel << " Max=" << _maxLevel << std::endl; 226 } 227 else 228 { 229 OE_DEBUG << LC << "SQL QUERY failed for " << query << ": " << std::endl; 230 } 231 sqlite3_finalize( select ); 232 osg::Timer_t endTime = osg::Timer::instance()->tick(); 233 OE_DEBUG << LC << "Computing levels took " << osg::Timer::instance()->delta_s(startTime, endTime ) << " s" << std::endl; 234 } 235 236 protected: 237 //override initialize(const osgDB::Options * readOptions)238 Status initialize(const osgDB::Options* readOptions) 239 { 240 _dbOptions = Registry::cloneOrCreateOptions(readOptions); 241 std::string fullFilename = _options.url()->full(); 242 243 int rc = sqlite3_open_v2( fullFilename.c_str(), &_database, SQLITE_OPEN_READONLY, 0L ); 244 if ( rc != 0 ) 245 { 246 return Status::Error(Status::ResourceUnavailable, Stringify() << "Failed to open database, " << sqlite3_errmsg(_database)); 247 } 248 249 setFeatureProfile(createFeatureProfile()); 250 251 return Status::OK(); 252 } 253 254 private: createFeatureProfile()255 const FeatureProfile* createFeatureProfile() 256 { 257 const osgEarth::Profile* profile = osgEarth::Registry::instance()->getSphericalMercatorProfile(); 258 FeatureProfile* result = new FeatureProfile(profile->getExtent()); 259 result->setTiled(true); 260 std::string minLevelStr, maxLevelStr; 261 if (getMetaData("minzoom", minLevelStr) && getMetaData("maxzoom", maxLevelStr)) 262 { 263 _minLevel = as<int>(minLevelStr, 0); 264 _maxLevel = as<int>(maxLevelStr, 0); 265 OE_NOTICE << LC << "Got levels from metadata " << _minLevel << ", " << _maxLevel << std::endl; 266 } 267 else 268 { 269 computeLevels(); 270 OE_NOTICE << LC << "Got levels from database " << _minLevel << ", " << _maxLevel << std::endl; 271 } 272 273 274 // Use the max level for now as the min level. 275 result->setFirstLevel(_maxLevel); 276 result->setMaxLevel(_maxLevel); 277 result->setProfile(profile); 278 result->geoInterp() = osgEarth::GEOINTERP_GREAT_CIRCLE; 279 return result; 280 } 281 282 283 private: 284 const MVTFeatureOptions _options; 285 FeatureSchema _schema; 286 osg::ref_ptr<osgDB::Options> _dbOptions; 287 osg::ref_ptr<osgDB::BaseCompressor> _compressor; 288 sqlite3* _database; 289 unsigned int _minLevel; 290 unsigned int _maxLevel; 291 }; 292 293 294 class MVTFeatureSourceFactory : public FeatureSourceDriver 295 { 296 public: MVTFeatureSourceFactory()297 MVTFeatureSourceFactory() 298 { 299 supportsExtension( "osgearth_feature_mapnikvectortiles", "Mapnik Vector Tiles feature driver for osgEarth" ); 300 } 301 className() const302 virtual const char* className() const 303 { 304 return "Mapnik Vector Tiles Feature Reader"; 305 } 306 readObject(const std::string & file_name,const Options * options) const307 virtual ReadResult readObject(const std::string& file_name, const Options* options) const 308 { 309 if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) 310 return ReadResult::FILE_NOT_HANDLED; 311 312 return ReadResult( new MVTFeatureSource( getFeatureSourceOptions(options) ) ); 313 } 314 }; 315 316 REGISTER_OSGPLUGIN(osgearth_feature_mapnikvectortiles, MVTFeatureSourceFactory) 317 318