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