[gdal-dev] Decoding feature blobs and tracking FIDs in MVT datasets

Even Rouault even.rouault at spatialys.com
Sun Nov 23 06:30:41 PST 2025


Linda,

the FID returned by the MVT driver, when opening on a directory, is a 
composition of the (X,Y,Z) of the tile and the feature id within it.

The id part of the tile is 
https://github.com/OSGeo/gdal/blob/master/ogr/ogrsf_frmts/mvt/ogrmvtdataset.cpp#L1672 
:

  m_nFIDBase = (static_cast<GIntBig>(nX) << m_nZ) | nY;

and then the feature ID is computed at 
https://github.com/OSGeo/gdal/blob/master/ogr/ogrsf_frmts/mvt/ogrmvtdataset.cpp#L1873:

             poFeature->SetFID(m_nFIDBase +
                               (poUnderlyingFeature->GetFID() << (2 * 
m_nZ)));

so the ID in the tile is

(GetFID() - m_nFIDBase) >> (2 * m_nZ)

But I'm realizing that the (X,Y,Z) of the tile when reading the 
directory are not accessible

Maybe you best chance is to slightly modify the driver so it creates a 
field with that information

Even


Le 23/11/2025 à 13:01, Linda Kladivová via gdal-dev a écrit :
>
> Hi,
>
>
> I’d like to ask about one specific aspect of the MVT driver. In my 
> workflow, I need to track the FIDs of features inside an MVT dataset 
> together with their corresponding internal codes (actual parcel IDs).
>
>
> My use case involves an MVT dataset of about 20 million features that 
> is frequently updated. For this purpose, I have implemented an 
> extension of the MVT driver that can open an MVT dataset in update 
> mode and create/delete features based on changes detected by a 
> preceding ETL process. Updates typically affect only a few individual 
> features every hour.
>
>
> After the initial loading of the full dataset using |ogr2ogr| I need a 
> way to iterate through the dataset and build a separate table mapping 
> each internal FID to the parcel’s real ID. I have tried opening and 
> inspecting the |temp.db| file (which I do not delete):
>
>
> GDALDataset* poTempDb =
>     (GDALDataset*) GDALOpenEx(dstTempDb.c_str(),
>                               GDAL_OF_VECTOR, nullptr, nullptr, nullptr);
>
> OGRLayer* poTempLayer = poTempDb->GetLayerByName("temp");
> poTempLayer->ResetReading();
>
> OGRFeature* poFeat = nullptr;
>
> while ((poFeat = poTempLayer->GetNextFeature()) != nullptr)
> {
>     GIntBig fid = poFeat->GetFID();
> std::cout << "FID=" << fid << " — ";
>     int idx = poFeat->GetFieldIndex("feature");
>     int blobSize = 0;
>     const GByte* pBlob = poFeat->GetFieldAsBinary(idx, &blobSize);
> std::cout << " Blob size=" << blobSize << std::endl;
> }
>
> I can successfully access the |feature| blob, but I need to decode it 
> in order to extract my custom parcel ID attribute and store those 
> values externally. This would allow me to keep track of which parcel 
> corresponds to which internal FID.
>
>
> This is a one-time (potentially slow) process. Afterward, during 
> incremental updates, I will be calling |CreateFeature| and 
> |DeleteFeature| (and updating my external mapping table accordingly). 
> For edits, I currently use a combination of |DeleteFeature| and 
> |CreateFeature| (I haven’t implemented |SetFeature| yet).
>
>
> My question is:
> Is there a way to decode this blob using existing GDAL/MVT 
> functionality, or would this require implementing a new function?
>
> Thank you very much for your help.
>
>
> Linda Karlovská
>
>
> _______________________________________________
> gdal-dev mailing list
> gdal-dev at lists.osgeo.org
> https://lists.osgeo.org/mailman/listinfo/gdal-dev

-- 
http://www.spatialys.com
My software is free, but my time generally not.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/gdal-dev/attachments/20251123/36e8e582/attachment.htm>


More information about the gdal-dev mailing list