LCOV - code coverage report
Current view: top level - core/vectortile - qgsvectortilemvtdecoder.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 207 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       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                 :          0 : QgsVectorTileMVTDecoder::QgsVectorTileMVTDecoder() = default;
      35                 :            : 
      36                 :          0 : QgsVectorTileMVTDecoder::~QgsVectorTileMVTDecoder() = default;
      37                 :            : 
      38                 :          0 : bool QgsVectorTileMVTDecoder::decode( QgsTileXYZ tileID, const QByteArray &rawTileData )
      39                 :            : {
      40                 :          0 :   if ( !tile.ParseFromArray( rawTileData.constData(), rawTileData.count() ) )
      41                 :          0 :     return false;
      42                 :            : 
      43                 :          0 :   mTileID = tileID;
      44                 :            : 
      45                 :          0 :   mLayerNameToIndex.clear();
      46                 :          0 :   for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
      47                 :            :   {
      48                 :          0 :     const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
      49                 :          0 :     QString layerName = layer.name().c_str();
      50                 :          0 :     mLayerNameToIndex[layerName] = layerNum;
      51                 :          0 :   }
      52                 :          0 :   return true;
      53                 :          0 : }
      54                 :            : 
      55                 :          0 : QStringList QgsVectorTileMVTDecoder::layers() const
      56                 :            : {
      57                 :          0 :   QStringList layerNames;
      58                 :          0 :   for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
      59                 :            :   {
      60                 :          0 :     const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
      61                 :          0 :     QString layerName = layer.name().c_str();
      62                 :          0 :     layerNames << layerName;
      63                 :          0 :   }
      64                 :          0 :   return layerNames;
      65                 :          0 : }
      66                 :            : 
      67                 :          0 : QStringList QgsVectorTileMVTDecoder::layerFieldNames( const QString &layerName ) const
      68                 :            : {
      69                 :          0 :   if ( !mLayerNameToIndex.contains( layerName ) )
      70                 :          0 :     return QStringList();
      71                 :            : 
      72                 :          0 :   const ::vector_tile::Tile_Layer &layer = tile.layers( mLayerNameToIndex[layerName] );
      73                 :          0 :   QStringList fieldNames;
      74                 :          0 :   for ( int i = 0; i < layer.keys_size(); ++i )
      75                 :            :   {
      76                 :          0 :     QString fieldName = layer.keys( i ).c_str();
      77                 :          0 :     fieldNames << fieldName;
      78                 :          0 :   }
      79                 :          0 :   return fieldNames;
      80                 :          0 : }
      81                 :            : 
      82                 :          0 : QgsVectorTileFeatures QgsVectorTileMVTDecoder::layerFeatures( const QMap<QString, QgsFields> &perLayerFields, const QgsCoordinateTransform &ct, const QSet<QString> *layerSubset ) const
      83                 :            : {
      84                 :          0 :   QgsVectorTileFeatures features;
      85                 :            : 
      86                 :          0 :   int numTiles = static_cast<int>( pow( 2, mTileID.zoomLevel() ) ); // assuming we won't ever go over 30 zoom levels
      87                 :          0 :   double z0xMin = -20037508.3427892, z0yMin = -20037508.3427892;
      88                 :          0 :   double z0xMax =  20037508.3427892, z0yMax =  20037508.3427892;
      89                 :          0 :   double tileDX = ( z0xMax - z0xMin ) / numTiles;
      90                 :          0 :   double tileDY = ( z0yMax - z0yMin ) / numTiles;
      91                 :          0 :   double tileXMin = z0xMin + mTileID.column() * tileDX;
      92                 :          0 :   double tileYMax = z0yMax - mTileID.row() * tileDY;
      93                 :            : 
      94                 :          0 :   for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ )
      95                 :            :   {
      96                 :          0 :     const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum );
      97                 :            : 
      98                 :          0 :     const QString layerName = layer.name().c_str();
      99                 :          0 :     if ( layerSubset && !layerSubset->contains( QString() ) && !layerSubset->contains( layerName ) )
     100                 :          0 :       continue;
     101                 :            : 
     102                 :          0 :     QVector<QgsFeature> layerFeatures;
     103                 :          0 :     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                 :          0 :     QHash<int, int> tagKeyIndexToFieldIndex;
     107                 :          0 :     for ( int i = 0; i < layer.keys_size(); ++i )
     108                 :            :     {
     109                 :          0 :       int fieldIndex = layerFields.indexOf( layer.keys( i ).c_str() );
     110                 :          0 :       if ( fieldIndex != -1 )
     111                 :          0 :         tagKeyIndexToFieldIndex.insert( i, fieldIndex );
     112                 :          0 :     }
     113                 :            : 
     114                 :            :     // go through features of a layer
     115                 :          0 :     for ( int featureNum = 0; featureNum < layer.features_size(); featureNum++ )
     116                 :            :     {
     117                 :          0 :       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                 :          0 :         fid = featureNum;
     132                 :          0 :         fid |= ( layerNum & 0xff ) << 24;
     133                 :          0 :         fid |= ( static_cast<QgsFeatureId>( mTileID.row() ) & 0xff ) << 32;
     134                 :          0 :         fid |= ( static_cast<QgsFeatureId>( mTileID.column() ) & 0xff ) << 40;
     135                 :            :       }
     136                 :            : 
     137                 :          0 :       QgsFeature f( layerFields, fid );
     138                 :            : 
     139                 :            :       //
     140                 :            :       // parse attributes
     141                 :            :       //
     142                 :            : 
     143                 :          0 :       for ( int tagNum = 0; tagNum + 1 < feature.tags_size(); tagNum += 2 )
     144                 :            :       {
     145                 :          0 :         int keyIndex = static_cast<int>( feature.tags( tagNum ) );
     146                 :          0 :         int fieldIndex = tagKeyIndexToFieldIndex.value( keyIndex, -1 );
     147                 :          0 :         if ( fieldIndex == -1 )
     148                 :          0 :           continue;
     149                 :            : 
     150                 :          0 :         int valueIndex = static_cast<int>( feature.tags( tagNum + 1 ) );
     151                 :          0 :         if ( valueIndex >= layer.values_size() )
     152                 :            :         {
     153                 :          0 :           QgsDebugMsg( QStringLiteral( "Invalid value index for attribute" ) );
     154                 :          0 :           continue;
     155                 :            :         }
     156                 :          0 :         const ::vector_tile::Tile_Value &value = layer.values( valueIndex );
     157                 :            : 
     158                 :          0 :         if ( value.has_string_value() )
     159                 :          0 :           f.setAttribute( fieldIndex, QString::fromStdString( value.string_value() ) );
     160                 :          0 :         else if ( value.has_float_value() )
     161                 :          0 :           f.setAttribute( fieldIndex, static_cast<double>( value.float_value() ) );
     162                 :          0 :         else if ( value.has_double_value() )
     163                 :          0 :           f.setAttribute( fieldIndex, value.double_value() );
     164                 :          0 :         else if ( value.has_int_value() )
     165                 :          0 :           f.setAttribute( fieldIndex, static_cast<int>( value.int_value() ) );
     166                 :          0 :         else if ( value.has_uint_value() )
     167                 :          0 :           f.setAttribute( fieldIndex, static_cast<int>( value.uint_value() ) );
     168                 :          0 :         else if ( value.has_sint_value() )
     169                 :          0 :           f.setAttribute( fieldIndex, static_cast<int>( value.sint_value() ) );
     170                 :          0 :         else if ( value.has_bool_value() )
     171                 :          0 :           f.setAttribute( fieldIndex, static_cast<bool>( value.bool_value() ) );
     172                 :            :         else
     173                 :            :         {
     174                 :          0 :           QgsDebugMsg( QStringLiteral( "Unexpected attribute value" ) );
     175                 :            :         }
     176                 :          0 :       }
     177                 :            : 
     178                 :            :       //
     179                 :            :       // parse geometry
     180                 :            :       //
     181                 :            : 
     182                 :          0 :       int extent = static_cast<int>( layer.extent() );
     183                 :          0 :       int cursorx = 0, cursory = 0;
     184                 :            : 
     185                 :          0 :       QVector<QgsPoint *> outputPoints; // for point/multi-point
     186                 :          0 :       QVector<QgsLineString *> outputLinestrings;  // for linestring/multi-linestring
     187                 :          0 :       QVector<QgsPolygon *> outputPolygons;
     188                 :          0 :       QVector<QgsPoint> tmpPoints;
     189                 :            : 
     190                 :          0 :       for ( int i = 0; i < feature.geometry_size(); i ++ )
     191                 :            :       {
     192                 :          0 :         unsigned g = feature.geometry( i );
     193                 :          0 :         unsigned cmdId = g & 0x7;
     194                 :          0 :         unsigned cmdCount = g >> 3;
     195                 :          0 :         if ( cmdId == 1 ) // MoveTo
     196                 :            :         {
     197                 :          0 :           if ( i + static_cast<int>( cmdCount ) * 2 >= feature.geometry_size() )
     198                 :            :           {
     199                 :          0 :             QgsDebugMsg( QStringLiteral( "Malformed geometry: invalid cmdCount" ) );
     200                 :          0 :             break;
     201                 :            :           }
     202                 :            : 
     203                 :          0 :           if ( feature.type() == vector_tile::Tile_GeomType_POINT )
     204                 :          0 :             outputPoints.reserve( outputPoints.size() + cmdCount );
     205                 :            :           else
     206                 :          0 :             tmpPoints.reserve( tmpPoints.size() + cmdCount );
     207                 :            : 
     208                 :          0 :           for ( unsigned j = 0; j < cmdCount; j++ )
     209                 :            :           {
     210                 :          0 :             unsigned v = feature.geometry( i + 1 );
     211                 :          0 :             unsigned w = feature.geometry( i + 2 );
     212                 :          0 :             int dx = ( ( v >> 1 ) ^ ( -( v & 1 ) ) );
     213                 :          0 :             int dy = ( ( w >> 1 ) ^ ( -( w & 1 ) ) );
     214                 :          0 :             cursorx += dx;
     215                 :          0 :             cursory += dy;
     216                 :          0 :             double px = tileXMin + tileDX * double( cursorx ) / double( extent );
     217                 :          0 :             double py = tileYMax - tileDY * double( cursory ) / double( extent );
     218                 :            : 
     219                 :          0 :             if ( feature.type() == vector_tile::Tile_GeomType_POINT )
     220                 :            :             {
     221                 :          0 :               outputPoints.append( new QgsPoint( px, py ) );
     222                 :          0 :             }
     223                 :          0 :             else if ( feature.type() == vector_tile::Tile_GeomType_LINESTRING )
     224                 :            :             {
     225                 :          0 :               if ( tmpPoints.size() > 0 )
     226                 :            :               {
     227                 :          0 :                 outputLinestrings.append( new QgsLineString( tmpPoints ) );
     228                 :          0 :                 tmpPoints.clear();
     229                 :          0 :               }
     230                 :          0 :               tmpPoints.append( QgsPoint( px, py ) );
     231                 :          0 :             }
     232                 :          0 :             else if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
     233                 :            :             {
     234                 :          0 :               tmpPoints.append( QgsPoint( px, py ) );
     235                 :          0 :             }
     236                 :          0 :             i += 2;
     237                 :          0 :           }
     238                 :          0 :         }
     239                 :          0 :         else if ( cmdId == 2 ) // LineTo
     240                 :            :         {
     241                 :          0 :           if ( i + static_cast<int>( cmdCount ) * 2 >= feature.geometry_size() )
     242                 :            :           {
     243                 :          0 :             QgsDebugMsg( QStringLiteral( "Malformed geometry: invalid cmdCount" ) );
     244                 :          0 :             break;
     245                 :            :           }
     246                 :          0 :           tmpPoints.reserve( tmpPoints.size() + cmdCount );
     247                 :          0 :           for ( unsigned j = 0; j < cmdCount; j++ )
     248                 :            :           {
     249                 :          0 :             unsigned v = feature.geometry( i + 1 );
     250                 :          0 :             unsigned w = feature.geometry( i + 2 );
     251                 :          0 :             int dx = ( ( v >> 1 ) ^ ( -( v & 1 ) ) );
     252                 :          0 :             int dy = ( ( w >> 1 ) ^ ( -( w & 1 ) ) );
     253                 :          0 :             cursorx += dx;
     254                 :          0 :             cursory += dy;
     255                 :          0 :             double px = tileXMin + tileDX * double( cursorx ) / double( extent );
     256                 :          0 :             double py = tileYMax - tileDY * double( cursory ) / double( extent );
     257                 :            : 
     258                 :          0 :             tmpPoints.push_back( QgsPoint( px, py ) );
     259                 :          0 :             i += 2;
     260                 :          0 :           }
     261                 :          0 :         }
     262                 :          0 :         else if ( cmdId == 7 ) // ClosePath
     263                 :            :         {
     264                 :          0 :           if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
     265                 :            :           {
     266                 :          0 :             tmpPoints.append( tmpPoints.first() );  // close the ring
     267                 :            : 
     268                 :          0 :             std::unique_ptr<QgsLineString> ring( new QgsLineString( tmpPoints ) );
     269                 :          0 :             tmpPoints.clear();
     270                 :            : 
     271                 :          0 :             if ( QgsVectorTileMVTUtils::isExteriorRing( ring.get() ) )
     272                 :            :             {
     273                 :            :               // start a new polygon
     274                 :          0 :               QgsPolygon *p = new QgsPolygon;
     275                 :          0 :               p->setExteriorRing( ring.release() );
     276                 :          0 :               outputPolygons.append( p );
     277                 :          0 :             }
     278                 :            :             else
     279                 :            :             {
     280                 :            :               // interior ring (hole)
     281                 :          0 :               if ( outputPolygons.count() != 0 )
     282                 :            :               {
     283                 :          0 :                 outputPolygons[outputPolygons.count() - 1]->addInteriorRing( ring.release() );
     284                 :          0 :               }
     285                 :            :               else
     286                 :            :               {
     287                 :          0 :                 QgsDebugMsg( QStringLiteral( "Malformed geometry: first ring of a polygon is interior ring" ) );
     288                 :            :               }
     289                 :            :             }
     290                 :          0 :           }
     291                 :            : 
     292                 :          0 :         }
     293                 :            :         else
     294                 :            :         {
     295                 :          0 :           QgsDebugMsg( QStringLiteral( "Unexpected command ID: %1" ).arg( cmdId ) );
     296                 :            :         }
     297                 :          0 :       }
     298                 :            : 
     299                 :          0 :       QString geomType;
     300                 :          0 :       if ( feature.type() == vector_tile::Tile_GeomType_POINT )
     301                 :            :       {
     302                 :          0 :         geomType = QStringLiteral( "Point" );
     303                 :          0 :         if ( outputPoints.count() == 1 )
     304                 :          0 :           f.setGeometry( QgsGeometry( outputPoints.at( 0 ) ) );
     305                 :            :         else
     306                 :            :         {
     307                 :          0 :           QgsMultiPoint *mp = new QgsMultiPoint;
     308                 :          0 :           mp->reserve( outputPoints.count() );
     309                 :          0 :           for ( int k = 0; k < outputPoints.count(); ++k )
     310                 :          0 :             mp->addGeometry( outputPoints[k] );
     311                 :          0 :           f.setGeometry( QgsGeometry( mp ) );
     312                 :            :         }
     313                 :          0 :       }
     314                 :          0 :       else if ( feature.type() == vector_tile::Tile_GeomType_LINESTRING )
     315                 :            :       {
     316                 :          0 :         geomType = QStringLiteral( "LineString" );
     317                 :            : 
     318                 :            :         // finish the linestring we have started
     319                 :          0 :         outputLinestrings.append( new QgsLineString( tmpPoints ) );
     320                 :            : 
     321                 :          0 :         if ( outputLinestrings.count() == 1 )
     322                 :          0 :           f.setGeometry( QgsGeometry( outputLinestrings.at( 0 ) ) );
     323                 :            :         else
     324                 :            :         {
     325                 :          0 :           QgsMultiLineString *mls = new QgsMultiLineString;
     326                 :          0 :           mls->reserve( outputLinestrings.size() );
     327                 :          0 :           for ( int k = 0; k < outputLinestrings.count(); ++k )
     328                 :          0 :             mls->addGeometry( outputLinestrings[k] );
     329                 :          0 :           f.setGeometry( QgsGeometry( mls ) );
     330                 :            :         }
     331                 :          0 :       }
     332                 :          0 :       else if ( feature.type() == vector_tile::Tile_GeomType_POLYGON )
     333                 :            :       {
     334                 :          0 :         geomType = QStringLiteral( "Polygon" );
     335                 :            : 
     336                 :          0 :         if ( outputPolygons.count() == 1 )
     337                 :          0 :           f.setGeometry( QgsGeometry( outputPolygons.at( 0 ) ) );
     338                 :            :         else
     339                 :            :         {
     340                 :          0 :           QgsMultiPolygon *mpl = new QgsMultiPolygon;
     341                 :          0 :           mpl->reserve( outputPolygons.size() );
     342                 :          0 :           for ( int k = 0; k < outputPolygons.count(); ++k )
     343                 :          0 :             mpl->addGeometry( outputPolygons[k] );
     344                 :          0 :           f.setGeometry( QgsGeometry( mpl ) );
     345                 :            :         }
     346                 :          0 :       }
     347                 :            : 
     348                 :          0 :       f.setAttribute( QStringLiteral( "_geom_type" ), geomType );
     349                 :          0 :       f.geometry().transform( ct );
     350                 :            : 
     351                 :          0 :       layerFeatures.append( f );
     352                 :          0 :     }
     353                 :            : 
     354                 :          0 :     features[layerName] = layerFeatures;
     355                 :          0 :   }
     356                 :          0 :   return features;
     357                 :          0 : }

Generated by: LCOV version 1.14