1 /***************************************************************************
2 qgsvectortilemvtdecoder.cpp
3 --------------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include <string>
17
18 #include "qgsvectortilemvtdecoder.h"
19
20 #include "qgsvectortilelayerrenderer.h"
21 #include "qgsvectortilemvtutils.h"
22 #include "qgsvectortileutils.h"
23
24 #include "qgslogger.h"
25 #include "qgsmultipoint.h"
26 #include "qgslinestring.h"
27 #include "qgsmultilinestring.h"
28 #include "qgsmultipolygon.h"
29 #include "qgspolygon.h"
30
31 #include <QPointer>
32
33
34 QgsVectorTileMVTDecoder::QgsVectorTileMVTDecoder() = default;
35
36 QgsVectorTileMVTDecoder::~QgsVectorTileMVTDecoder() = default;
37
decode(QgsTileXYZ tileID,const QByteArray & rawTileData)38 bool QgsVectorTileMVTDecoder::decode( QgsTileXYZ tileID, const QByteArray &rawTileData )
39 {
40 if ( !tile.ParseFromArray( rawTileData.constData(), rawTileData.count() ) )
41 return false;
42
43 mTileID = tileID;
44
45 mLayerNameToIndex.clear();
46 for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
47 {
48 const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
49 const QString layerName = layer.name().c_str();
50 mLayerNameToIndex[layerName] = layerNum;
51 }
52 return true;
53 }
54
layers() const55 QStringList QgsVectorTileMVTDecoder::layers() const
56 {
57 QStringList layerNames;
58 for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
59 {
60 const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
61 const QString layerName = layer.name().c_str();
62 layerNames << layerName;
63 }
64 return layerNames;
65 }
66
layerFieldNames(const QString & layerName) const67 QStringList QgsVectorTileMVTDecoder::layerFieldNames( const QString &layerName ) const
68 {
69 if ( !mLayerNameToIndex.contains( layerName ) )
70 return QStringList();
71
72 const ::vector_tile::Tile_Layer &layer = tile.layers( mLayerNameToIndex[layerName] );
73 QStringList fieldNames;
74 for ( int i = 0; i < layer.keys_size(); ++i )
75 {
76 const QString fieldName = layer.keys( i ).c_str();
77 fieldNames << fieldName;
78 }
79 return fieldNames;
80 }
81
layerFeatures(const QMap<QString,QgsFields> & perLayerFields,const QgsCoordinateTransform & ct,const QSet<QString> * layerSubset) const82 QgsVectorTileFeatures QgsVectorTileMVTDecoder::layerFeatures( const QMap<QString, QgsFields> &perLayerFields, const QgsCoordinateTransform &ct, const QSet<QString> *layerSubset ) const
83 {
84 QgsVectorTileFeatures features;
85
86 const int numTiles = static_cast<int>( pow( 2, mTileID.zoomLevel() ) ); // assuming we won't ever go over 30 zoom levels
87 double z0xMin = -20037508.3427892, z0yMin = -20037508.3427892;
88 double z0xMax = 20037508.3427892, z0yMax = 20037508.3427892;
89 const double tileDX = ( z0xMax - z0xMin ) / numTiles;
90 const double tileDY = ( z0yMax - z0yMin ) / numTiles;
91 const double tileXMin = z0xMin + mTileID.column() * tileDX;
92 const double tileYMax = z0yMax - mTileID.row() * tileDY;
93
94 for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
95 {
96 const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
97
98 const QString layerName = layer.name().c_str();
99 if ( layerSubset && !layerSubset->contains( QString() ) && !layerSubset->contains( layerName ) )
100 continue;
101
102 QVector<QgsFeature> layerFeatures;
103 const QgsFields layerFields = perLayerFields[layerName];
104
105 // figure out how field indexes in MVT encoding map to field indexes in QgsFields (we may not use all available fields)
106 QHash<int, int> tagKeyIndexToFieldIndex;
107 for ( int i = 0; i < layer.keys_size(); ++i )
108 {
109 const int fieldIndex = layerFields.indexOf( layer.keys( i ).c_str() );
110 if ( fieldIndex != -1 )
111 tagKeyIndexToFieldIndex.insert( i, fieldIndex );
112 }
113
114 // go through features of a layer
115 for ( int featureNum = 0; featureNum < layer.features_size(); featureNum++ )
116 {
117 const ::vector_tile::Tile_Feature &feature = layer.features( featureNum );
118
119 QgsFeatureId fid;
120 #if 0
121 // even if a feature has an internal ID, it's not guaranteed to be unique across different
122 // tiles. This may violate the specifications, but it's been seen on mbtiles files in the wild...
123 if ( feature.has_id() )
124 fid = static_cast<QgsFeatureId>( feature.id() );
125 else
126 #endif
127 {
128 // There is no assigned ID, but some parts of QGIS do not work correctly if all IDs are zero
129 // (e.g. labeling will not register two features with the same FID within a single layer),
130 // so let's generate some pseudo-unique FIDs to keep those bits happy
131 fid = featureNum;
132 fid |= ( layerNum & 0xff ) << 24;
133 fid |= ( static_cast<QgsFeatureId>( mTileID.row() ) & 0xff ) << 32;
134 fid |= ( static_cast<QgsFeatureId>( mTileID.column() ) & 0xff ) << 40;
135 }
136
137 QgsFeature f( layerFields, fid );
138
139 //
140 // parse attributes
141 //
142
143 for ( int tagNum = 0; tagNum + 1 < feature.tags_size(); tagNum += 2 )
144 {
145 const int keyIndex = static_cast<int>( feature.tags( tagNum ) );
146 const int fieldIndex = tagKeyIndexToFieldIndex.value( keyIndex, -1 );
147 if ( fieldIndex == -1 )
148 continue;
149
150 const int valueIndex = static_cast<int>( feature.tags( tagNum + 1 ) );
151 if ( valueIndex >= layer.values_size() )
152 {
153 QgsDebugMsg( QStringLiteral( "Invalid value index for attribute" ) );
154 continue;
155 }
156 const ::vector_tile::Tile_Value &value = layer.values( valueIndex );
157
158 if ( value.has_string_value() )
159 f.setAttribute( fieldIndex, QString::fromStdString( value.string_value() ) );
160 else if ( value.has_float_value() )
161 f.setAttribute( fieldIndex, static_cast<double>( value.float_value() ) );
162 else if ( value.has_double_value() )
163 f.setAttribute( fieldIndex, value.double_value() );
164 else if ( value.has_int_value() )
165 f.setAttribute( fieldIndex, static_cast<int>( value.int_value() ) );
166 else if ( value.has_uint_value() )
167 f.setAttribute( fieldIndex, static_cast<int>( value.uint_value() ) );
168 else if ( value.has_sint_value() )
169 f.setAttribute( fieldIndex, static_cast<int>( value.sint_value() ) );
170 else if ( value.has_bool_value() )
171 f.setAttribute( fieldIndex, static_cast<bool>( value.bool_value() ) );
172 else
173 {
174 QgsDebugMsg( QStringLiteral( "Unexpected attribute value" ) );
175 }
176 }
177
178 //
179 // parse geometry
180 //
181
182 const int extent = static_cast<int>( layer.extent() );
183 int cursorx = 0, cursory = 0;
184
185 QVector<QgsPoint *> outputPoints; // for point/multi-point
186 QVector<QgsLineString *> outputLinestrings; // for linestring/multi-linestring
187 QVector<QgsPolygon *> outputPolygons;
188 QVector<QgsPoint> tmpPoints;
189
190 for ( int i = 0; i < feature.geometry_size(); i ++ )
191 {
192 const unsigned g = feature.geometry( i );
193 const unsigned cmdId = g & 0x7;
194 const unsigned cmdCount = g >> 3;
195 if ( cmdId == 1 ) // MoveTo
196 {
197 if ( i + static_cast<int>( cmdCount ) * 2 >= feature.geometry_size() )
198 {
199 QgsDebugMsg( QStringLiteral( "Malformed geometry: invalid cmdCount" ) );
200 break;
201 }
202
203 if ( feature.type() == vector_tile::Tile_GeomType_POINT )
204 outputPoints.reserve( outputPoints.size() + cmdCount );
205 else
206 tmpPoints.reserve( tmpPoints.size() + cmdCount );
207
208 for ( unsigned j = 0; j < cmdCount; j++ )
209 {
210 const unsigned v = feature.geometry( i + 1 );
211 const unsigned w = feature.geometry( i + 2 );
212 const int dx = ( ( v >> 1 ) ^ ( -( v & 1 ) ) );
213 const int dy = ( ( w >> 1 ) ^ ( -( w & 1 ) ) );
214 cursorx += dx;
215 cursory += dy;
216 const double px = tileXMin + tileDX * double( cursorx ) / double( extent );
217 const double py = tileYMax - tileDY * double( cursory ) / double( extent );
218
219 if ( feature.type() == vector_tile::Tile_GeomType_POINT )
220 {
221 outputPoints.append( new QgsPoint( px, py ) );
222 }
223 else if ( feature.type() == vector_tile::Tile_GeomType_LINESTRING )
224 {
225 if ( tmpPoints.size() > 0 )
226 {
227 outputLinestrings.append( new QgsLineString( tmpPoints ) );
228 tmpPoints.clear();
229 }
230 tmpPoints.append( QgsPoint( px, py ) );
231 }
232 else if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
233 {
234 tmpPoints.append( QgsPoint( px, py ) );
235 }
236 i += 2;
237 }
238 }
239 else if ( cmdId == 2 ) // LineTo
240 {
241 if ( i + static_cast<int>( cmdCount ) * 2 >= feature.geometry_size() )
242 {
243 QgsDebugMsg( QStringLiteral( "Malformed geometry: invalid cmdCount" ) );
244 break;
245 }
246 tmpPoints.reserve( tmpPoints.size() + cmdCount );
247 for ( unsigned j = 0; j < cmdCount; j++ )
248 {
249 const unsigned v = feature.geometry( i + 1 );
250 const unsigned w = feature.geometry( i + 2 );
251 const int dx = ( ( v >> 1 ) ^ ( -( v & 1 ) ) );
252 const int dy = ( ( w >> 1 ) ^ ( -( w & 1 ) ) );
253 cursorx += dx;
254 cursory += dy;
255 const double px = tileXMin + tileDX * double( cursorx ) / double( extent );
256 const double py = tileYMax - tileDY * double( cursory ) / double( extent );
257
258 tmpPoints.push_back( QgsPoint( px, py ) );
259 i += 2;
260 }
261 }
262 else if ( cmdId == 7 ) // ClosePath
263 {
264 if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
265 {
266 tmpPoints.append( tmpPoints.first() ); // close the ring
267
268 std::unique_ptr<QgsLineString> ring( new QgsLineString( tmpPoints ) );
269 tmpPoints.clear();
270
271 if ( QgsVectorTileMVTUtils::isExteriorRing( ring.get() ) )
272 {
273 // start a new polygon
274 QgsPolygon *p = new QgsPolygon;
275 p->setExteriorRing( ring.release() );
276 outputPolygons.append( p );
277 }
278 else
279 {
280 // interior ring (hole)
281 if ( outputPolygons.count() != 0 )
282 {
283 outputPolygons[outputPolygons.count() - 1]->addInteriorRing( ring.release() );
284 }
285 else
286 {
287 QgsDebugMsg( QStringLiteral( "Malformed geometry: first ring of a polygon is interior ring" ) );
288 }
289 }
290 }
291
292 }
293 else
294 {
295 QgsDebugMsg( QStringLiteral( "Unexpected command ID: %1" ).arg( cmdId ) );
296 }
297 }
298
299 QString geomType;
300 if ( feature.type() == vector_tile::Tile_GeomType_POINT )
301 {
302 geomType = QStringLiteral( "Point" );
303 if ( outputPoints.count() == 1 )
304 f.setGeometry( QgsGeometry( outputPoints.at( 0 ) ) );
305 else
306 {
307 QgsMultiPoint *mp = new QgsMultiPoint;
308 mp->reserve( outputPoints.count() );
309 for ( int k = 0; k < outputPoints.count(); ++k )
310 mp->addGeometry( outputPoints[k] );
311 f.setGeometry( QgsGeometry( mp ) );
312 }
313 }
314 else if ( feature.type() == vector_tile::Tile_GeomType_LINESTRING )
315 {
316 geomType = QStringLiteral( "LineString" );
317
318 // finish the linestring we have started
319 outputLinestrings.append( new QgsLineString( tmpPoints ) );
320
321 if ( outputLinestrings.count() == 1 )
322 f.setGeometry( QgsGeometry( outputLinestrings.at( 0 ) ) );
323 else
324 {
325 QgsMultiLineString *mls = new QgsMultiLineString;
326 mls->reserve( outputLinestrings.size() );
327 for ( int k = 0; k < outputLinestrings.count(); ++k )
328 mls->addGeometry( outputLinestrings[k] );
329 f.setGeometry( QgsGeometry( mls ) );
330 }
331 }
332 else if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
333 {
334 geomType = QStringLiteral( "Polygon" );
335
336 if ( outputPolygons.count() == 1 )
337 f.setGeometry( QgsGeometry( outputPolygons.at( 0 ) ) );
338 else
339 {
340 QgsMultiPolygon *mpl = new QgsMultiPolygon;
341 mpl->reserve( outputPolygons.size() );
342 for ( int k = 0; k < outputPolygons.count(); ++k )
343 mpl->addGeometry( outputPolygons[k] );
344 f.setGeometry( QgsGeometry( mpl ) );
345 }
346 }
347
348 f.setAttribute( QStringLiteral( "_geom_type" ), geomType );
349 f.geometry().transform( ct );
350
351 layerFeatures.append( f );
352 }
353
354 features[layerName] = layerFeatures;
355 }
356 return features;
357 }
358