[mapguide-commits] r9605 - in sandbox/jng/mvt_alt/Common/Renderers: . mvt

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Sat Sep 21 10:25:41 PDT 2019


Author: jng
Date: 2019-09-21 10:25:40 -0700 (Sat, 21 Sep 2019)
New Revision: 9605

Added:
   sandbox/jng/mvt_alt/Common/Renderers/mvt/
   sandbox/jng/mvt_alt/Common/Renderers/mvt/gpb.h
   sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.cpp
   sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.h
   sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile_test.cpp
   sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.cpp
   sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.h
Log:
Add OGR sources for writing MVT tiles. We'll kitbash this code into shape so it can work in our renderer infrastructure

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/gpb.h
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/gpb.h	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/gpb.h	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,540 @@
+/******************************************************************************
+ * $Id: gpb.h 1758774fff760a0e28521d01de2d9ae74ca66701 2018-02-14 22:17:51Z Kurt Schwehr $
+ *
+ * Project:  OpenGIS Simple Features Reference Implementation
+ * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
+ * Purpose:  Google Protocol Buffer generic handling functions
+ *
+ ******************************************************************************
+ * Copyright (c) 2012, Even Rouault <even dot rouault at mines-paris dot org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef GPB_H_INCLUDED
+#define GPB_H_INCLUDED
+
+#include "cpl_port.h"
+#include "cpl_error.h"
+#include "cpl_string.h"
+
+#include <string>
+#include <exception>
+
+#ifndef CHECK_OOB
+#define CHECK_OOB 1
+#endif
+
+class GPBException: public std::exception
+{
+        std::string m_osMessage;
+    public:
+        explicit GPBException(int nLine): m_osMessage(
+            CPLSPrintf("Parsing error occurred at line %d", nLine)) {}
+
+        const char* what() const noexcept override
+                                        { return m_osMessage.c_str(); }
+};
+
+#define THROW_GPB_EXCEPTION throw GPBException(__LINE__)
+
+/************************************************************************/
+/*                Google Protocol Buffer definitions                    */
+/************************************************************************/
+
+// TODO(schwehr): This should be an enum.
+constexpr int WT_VARINT = 0;
+constexpr int WT_64BIT = 1;
+constexpr int WT_DATA = 2;
+// constexpr WT_STARTGROUP = 3; // unused
+// constexpr WT_ENDGROUP = 4; // unused
+constexpr int WT_32BIT = 5;
+
+#define MAKE_KEY(nFieldNumber, nWireType) ((nFieldNumber << 3) | nWireType)
+#define GET_WIRETYPE(nKey) (nKey & 0x7)
+#define GET_FIELDNUMBER(nKey) (nKey >> 3)
+
+/************************************************************************/
+/*                          ReadVarUInt32()                             */
+/************************************************************************/
+
+inline int ReadVarUInt32(const GByte** ppabyData)
+{
+    unsigned int nVal = 0;
+    int nShift = 0;
+    const GByte* pabyData = *ppabyData;
+
+    while(true)
+    {
+        int nByte = *pabyData;
+        if (!(nByte & 0x80))
+        {
+            *ppabyData = pabyData + 1;
+            return nVal | ((unsigned)nByte << nShift);
+        }
+        nVal |= (nByte & 0x7f) << nShift;
+        pabyData ++;
+        nShift += 7;
+        if( nShift == 28 )
+        {
+            nByte = *pabyData;
+            if (!(nByte & 0x80))
+            {
+                *ppabyData = pabyData + 1;
+                return nVal | (((unsigned)nByte & 0xf) << nShift);
+            }
+            *ppabyData = pabyData;
+            return nVal;
+        }
+    }
+}
+
+#define READ_VARUINT32(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarUInt32(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+#define READ_SIZE(pabyData, pabyDataLimit, nSize) \
+    { \
+        READ_VARUINT32(pabyData, pabyDataLimit, nSize); \
+        if (CHECK_OOB && nSize > (unsigned int)(pabyDataLimit - pabyData)) THROW_GPB_EXCEPTION; \
+    }
+
+/************************************************************************/
+/*                          ReadVarUInt64()                             */
+/************************************************************************/
+
+inline GUIntBig ReadVarUInt64(const GByte** ppabyData)
+{
+    GUIntBig nVal = 0;
+    int nShift = 0;
+    const GByte* pabyData = *ppabyData;
+
+    while(true)
+    {
+        int nByte = *pabyData;
+        if (!(nByte & 0x80))
+        {
+            *ppabyData = pabyData + 1;
+            return nVal | ((GUIntBig)nByte << nShift);
+        }
+        nVal |= ((GUIntBig)(nByte & 0x7f)) << nShift;
+        pabyData ++;
+        nShift += 7;
+        if( nShift == 63 )
+        {
+            nByte = *pabyData;
+            if (!(nByte & 0x80))
+            {
+                *ppabyData = pabyData + 1;
+                return nVal | (((GUIntBig)nByte & 1) << nShift);
+            }
+            *ppabyData = pabyData;
+            return nVal;
+        }
+    }
+}
+
+#define READ_VARUINT64(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarUInt64(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+#define READ_SIZE64(pabyData, pabyDataLimit, nSize) \
+    { \
+        READ_VARUINT64(pabyData, pabyDataLimit, nSize); \
+        if (CHECK_OOB && nSize > (unsigned int)(pabyDataLimit - pabyData)) THROW_GPB_EXCEPTION; \
+    }
+
+/************************************************************************/
+/*                           ReadVarInt64()                             */
+/************************************************************************/
+
+inline GIntBig ReadVarInt64(const GByte** ppabyData)
+{
+    return static_cast<GIntBig>(ReadVarUInt64(ppabyData));
+}
+
+#define READ_VARINT64(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarInt64(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+/************************************************************************/
+/*                            DecodeSInt()                              */
+/************************************************************************/
+
+inline GIntBig DecodeSInt(GUIntBig nVal)
+{
+    return ((nVal & 1) == 0) ?
+        static_cast<GIntBig>(nVal >> 1) : -static_cast<GIntBig>(nVal >> 1)-1;
+}
+
+inline GInt32 DecodeSInt(GUInt32 nVal)
+{
+    return ((nVal & 1) == 0) ?
+        static_cast<GInt32>(nVal >> 1) : -static_cast<GInt32>(nVal >> 1)-1;
+}
+
+/************************************************************************/
+/*                            ReadVarSInt64()                           */
+/************************************************************************/
+
+inline GIntBig ReadVarSInt64(const GByte** ppabyPtr)
+{
+    return DecodeSInt(ReadVarUInt64(ppabyPtr));
+}
+
+#define READ_VARSINT64(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarSInt64(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+#define READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarSInt64(&pabyData); \
+    }
+
+/************************************************************************/
+/*                           ReadVarInt32()                             */
+/************************************************************************/
+
+inline int ReadVarInt32(const GByte** ppabyData)
+{
+    /*  If you use int32 or int64 as the type for a negative number, */
+    /* the resulting varint is always ten bytes long */
+    GIntBig nVal = static_cast<GIntBig>(ReadVarUInt64(ppabyData));
+    return static_cast<int>(nVal);
+}
+
+#define READ_VARINT32(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = ReadVarInt32(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+#define READ_VARSINT32(pabyData, pabyDataLimit, nVal)  \
+    { \
+        nVal = DecodeSInt(static_cast<GUInt32>(ReadVarUInt64(&pabyData))); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+/************************************************************************/
+/*                            ReadFloat32()                             */
+/************************************************************************/
+
+inline float ReadFloat32(const GByte** ppabyData, const GByte* pabyDataLimit)
+{
+    if( *ppabyData + sizeof(float) > pabyDataLimit )
+        THROW_GPB_EXCEPTION;
+    float fValue;
+    memcpy(&fValue, *ppabyData, sizeof(float));
+    CPL_LSBPTR32(&fValue);
+    *ppabyData += sizeof(float);
+    return fValue;
+}
+
+/************************************************************************/
+/*                            ReadFloat64()                             */
+/************************************************************************/
+
+inline double ReadFloat64(const GByte** ppabyData, const GByte* pabyDataLimit)
+{
+    if( *ppabyData + sizeof(double) > pabyDataLimit )
+        THROW_GPB_EXCEPTION;
+    double dfValue;
+    memcpy(&dfValue, *ppabyData, sizeof(double));
+    CPL_LSBPTR64(&dfValue);
+    *ppabyData += sizeof(double);
+    return dfValue;
+}
+
+/************************************************************************/
+/*                            SkipVarInt()                              */
+/************************************************************************/
+
+inline void SkipVarInt(const GByte** ppabyData)
+{
+    const GByte* pabyData = *ppabyData;
+    while(true)
+    {
+        int nByte = *pabyData;
+        if (!(nByte & 0x80))
+        {
+            *ppabyData = pabyData + 1;
+            return;
+        }
+        pabyData ++;
+    }
+}
+
+#define SKIP_VARINT(pabyData, pabyDataLimit) \
+    { \
+        SkipVarInt(&pabyData); \
+        if (CHECK_OOB && pabyData > pabyDataLimit) THROW_GPB_EXCEPTION; \
+    }
+
+#define READ_FIELD_KEY(nKey) READ_VARINT32(pabyData, pabyDataLimit, nKey)
+
+#define READ_TEXT_WITH_SIZE(pabyData, pabyDataLimit, pszTxt, l_nDataLength) do { \
+        READ_SIZE(pabyData, pabyDataLimit, l_nDataLength); \
+        pszTxt = (char*)VSI_MALLOC_VERBOSE(l_nDataLength + 1); \
+        if( pszTxt == nullptr ) THROW_GPB_EXCEPTION; \
+        memcpy(pszTxt, pabyData, l_nDataLength); \
+        pszTxt[l_nDataLength] = 0; \
+        pabyData += l_nDataLength; } while(0)
+
+#define READ_TEXT(pabyData, pabyDataLimit, pszTxt) do { \
+        unsigned int l_nDataLength; \
+        READ_TEXT_WITH_SIZE(pabyData, pabyDataLimit, pszTxt, l_nDataLength); } while(0)
+
+/************************************************************************/
+/*                         SkipUnknownField()                           */
+/************************************************************************/
+
+#define SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, verbose) \
+        int nWireType = GET_WIRETYPE(nKey); \
+        if (verbose) \
+        { \
+            int nFieldNumber = GET_FIELDNUMBER(nKey); \
+            CPLDebug("PBF", "Unhandled case: nFieldNumber = %d, nWireType = %d", nFieldNumber, nWireType); \
+        } \
+        switch (nWireType) \
+        { \
+            case WT_VARINT: \
+            { \
+                SKIP_VARINT(pabyData, pabyDataLimit); \
+                break; \
+            } \
+            case WT_64BIT: \
+            { \
+                if (CHECK_OOB && pabyDataLimit - pabyData < 8) THROW_GPB_EXCEPTION; \
+                pabyData += 8; \
+                break; \
+            } \
+            case WT_DATA: \
+            { \
+                unsigned int nDataLength; \
+                READ_SIZE(pabyData, pabyDataLimit, nDataLength); \
+                pabyData += nDataLength; \
+                break; \
+            } \
+            case WT_32BIT: \
+            { \
+                if (CHECK_OOB && pabyDataLimit - pabyData < 4) THROW_GPB_EXCEPTION; \
+                pabyData += 4; \
+                break; \
+            } \
+            default: \
+                THROW_GPB_EXCEPTION; \
+        }
+
+inline int SkipUnknownField(int nKey, const GByte* pabyData, const GByte* pabyDataLimit, int verbose)
+{
+    const GByte* pabyDataBefore = pabyData;
+    try
+    {
+        SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, verbose);
+        return static_cast<int>(pabyData - pabyDataBefore);
+    }
+    catch( const GPBException& e )
+    {
+        if( verbose )
+        {
+            CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
+        }
+        return -1;
+    }
+}
+
+#define SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, verbose) \
+    { \
+        int _nOffset = SkipUnknownField(nKey, pabyData, pabyDataLimit, verbose); \
+        if (_nOffset < 0) \
+            THROW_GPB_EXCEPTION; \
+        pabyData += _nOffset; \
+    }
+
+
+/************************************************************************/
+/*                          GetVarUIntSize()                            */
+/************************************************************************/
+
+inline int GetVarUIntSize(GUIntBig nVal)
+{
+    int nBytes = 1;
+    while( nVal > 127 )
+    {
+        nBytes ++;
+        nVal >>= 7;
+    }
+    return nBytes;
+}
+
+/************************************************************************/
+/*                            EncodeSInt()                              */
+/************************************************************************/
+
+inline GUIntBig EncodeSInt(GIntBig nVal)
+{
+    if( nVal < 0 )
+        return (static_cast<GUIntBig>(-(nVal + 1)) << 1) | 1;
+    else
+        return static_cast<GUIntBig>(nVal) << 1;
+}
+
+inline GUInt32 EncodeSInt(GInt32 nVal)
+{
+    if( nVal < 0 )
+        return (static_cast<GUInt32>(-(nVal + 1)) << 1) | 1;
+    else
+        return static_cast<GUInt32>(nVal) << 1;
+}
+
+/************************************************************************/
+/*                          GetVarIntSize()                             */
+/************************************************************************/
+
+inline int GetVarIntSize(GIntBig nVal)
+{
+    return GetVarUIntSize(static_cast<GUIntBig>(nVal));
+}
+
+/************************************************************************/
+/*                          GetVarSIntSize()                            */
+/************************************************************************/
+
+inline int GetVarSIntSize(GIntBig nVal)
+{
+    return GetVarUIntSize(EncodeSInt(nVal));
+}
+
+/************************************************************************/
+/*                           WriteVarUInt()                             */
+/************************************************************************/
+
+inline void WriteVarUInt(GByte** ppabyData, GUIntBig nVal)
+{
+    GByte* pabyData = *ppabyData;
+    while( nVal > 127 )
+    {
+        *pabyData = static_cast<GByte>((nVal & 0x7f) | 0x80);
+        pabyData ++;
+        nVal >>= 7;
+    }
+    *pabyData = static_cast<GByte>(nVal);
+    pabyData ++;
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                        WriteVarUIntSingleByte()                      */
+/************************************************************************/
+
+inline void WriteVarUIntSingleByte(GByte** ppabyData, GUIntBig nVal)
+{
+    GByte* pabyData = *ppabyData;
+    CPLAssert( nVal < 128 );
+    *pabyData = static_cast<GByte>(nVal);
+    pabyData ++;
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                           WriteVarInt()                              */
+/************************************************************************/
+
+inline void WriteVarInt(GByte** ppabyData, GIntBig nVal)
+{
+    WriteVarUInt(ppabyData, static_cast<GUIntBig>(nVal));
+}
+
+/************************************************************************/
+/*                           WriteVarSInt()                             */
+/************************************************************************/
+
+inline void WriteVarSInt(GByte** ppabyData, GIntBig nVal)
+{
+    WriteVarUInt(ppabyData, EncodeSInt(nVal) );
+}
+
+/************************************************************************/
+/*                           WriteFloat32()                             */
+/************************************************************************/
+
+inline void WriteFloat32(GByte** ppabyData, float fVal)
+{
+    CPL_LSBPTR32(&fVal);
+    memcpy(*ppabyData, &fVal, sizeof(float));
+    *ppabyData += sizeof(float);
+}
+
+/************************************************************************/
+/*                           WriteFloat64()                             */
+/************************************************************************/
+
+inline void WriteFloat64(GByte** ppabyData, double dfVal)
+{
+    CPL_LSBPTR64(&dfVal);
+    memcpy(*ppabyData, &dfVal, sizeof(double));
+    *ppabyData += sizeof(double);
+}
+
+/************************************************************************/
+/*                           GetTextSize()                              */
+/************************************************************************/
+
+inline int GetTextSize(const char* pszText)
+{
+    size_t nTextSize = strlen(pszText);
+    return GetVarUIntSize(nTextSize) + static_cast<int>(nTextSize);
+}
+
+inline int GetTextSize(const std::string& osText)
+{
+    size_t nTextSize = osText.size();
+    return GetVarUIntSize(nTextSize) + static_cast<int>(nTextSize);
+}
+
+/************************************************************************/
+/*                            WriteText()                               */
+/************************************************************************/
+
+inline void WriteText(GByte** ppabyData, const char* pszText)
+{
+    size_t nTextSize = strlen(pszText);
+    WriteVarUInt(ppabyData, nTextSize);
+    memcpy(*ppabyData, pszText, nTextSize);
+    *ppabyData += nTextSize;
+}
+
+inline void WriteText(GByte** ppabyData, const std::string& osText)
+{
+    size_t nTextSize = osText.size();
+    WriteVarUInt(ppabyData, nTextSize);
+    memcpy(*ppabyData, osText.c_str(), nTextSize);
+    *ppabyData += nTextSize;
+}
+
+
+#endif /* GPB_H_INCLUDED */

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.cpp
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.cpp	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.cpp	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,928 @@
+/******************************************************************************
+ *
+ * Project:  MVT Translator
+ * Purpose:  Mapbox Vector Tile decoder and encoder
+ * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "gpb.h"
+
+#include <limits>
+#include <memory>
+#include <vector>
+
+#include "mvt_tile.h"
+
+constexpr int knSIZE_KEY = 1;
+
+/************************************************************************/
+/*                        MVTTileLayerValue()                           */
+/************************************************************************/
+
+MVTTileLayerValue::MVTTileLayerValue(): m_nUIntValue(0)
+{
+}
+
+MVTTileLayerValue::MVTTileLayerValue(const MVTTileLayerValue& oOther)
+{
+    operator=(oOther);
+}
+
+/************************************************************************/
+/*                       ~MVTTileLayerValue()                           */
+/************************************************************************/
+
+MVTTileLayerValue::~MVTTileLayerValue()
+{
+    unset();
+}
+
+/************************************************************************/
+/*                            operator=                                 */
+/************************************************************************/
+
+MVTTileLayerValue& MVTTileLayerValue::operator=(
+                                            const MVTTileLayerValue& oOther)
+
+{
+    if( this != &oOther )
+    {
+        unset();
+        m_eType = oOther.m_eType;
+        if( m_eType == ValueType::STRING )
+        {
+            const size_t nSize = strlen(oOther.m_pszValue);
+            m_pszValue = static_cast<char*>(CPLMalloc(1 + nSize));
+            memcpy(m_pszValue, oOther.m_pszValue, nSize);
+            m_pszValue[nSize] = 0;
+        }
+        else
+        {
+            m_nUIntValue = oOther.m_nUIntValue;
+        }
+    }
+    return *this;
+}
+
+/************************************************************************/
+/*                            operator<                                 */
+/************************************************************************/
+
+bool MVTTileLayerValue::operator <(const MVTTileLayerValue& rhs) const
+{
+    if( m_eType < rhs.m_eType )
+        return false;
+    if( m_eType > rhs.m_eType )
+        return true;
+    if( m_eType == ValueType::NONE )
+        return false;
+    if( m_eType == ValueType::STRING )
+        return strcmp( m_pszValue, rhs.m_pszValue ) < 0;
+    if( m_eType == ValueType::FLOAT )
+        return m_fValue < rhs.m_fValue;
+    if( m_eType == ValueType::DOUBLE )
+        return m_dfValue < rhs.m_dfValue;
+    if( m_eType == ValueType::INT )
+        return m_nIntValue < rhs.m_nIntValue;
+    if( m_eType == ValueType::UINT )
+        return m_nUIntValue < rhs.m_nUIntValue;
+    if( m_eType == ValueType::SINT )
+        return m_nIntValue < rhs.m_nIntValue;
+    if( m_eType == ValueType::BOOL )
+        return m_bBoolValue < rhs.m_bBoolValue;
+    if( m_eType == ValueType::STRING_MAX_8 )
+        return strncmp( m_achValue, rhs.m_achValue, 8 ) < 0;
+    CPLAssert(false);
+    return false;
+}
+
+/************************************************************************/
+/*                             unset()                                  */
+/************************************************************************/
+
+void MVTTileLayerValue::unset()
+{
+    if( m_eType == ValueType::STRING )
+        CPLFree(m_pszValue);
+    m_eType = ValueType::NONE;
+    m_nUIntValue = 0;
+}
+
+/************************************************************************/
+/*                            GetSizeMax8()                             */
+/************************************************************************/
+
+static size_t GetSizeMax8(const char achValue[8])
+{
+    size_t nSize = 0;
+    while( nSize < 8 && achValue[nSize] != 0 )
+        nSize ++;
+    return nSize;
+}
+
+/************************************************************************/
+/*                        setStringValue()                              */
+/************************************************************************/
+
+void MVTTileLayerValue::setStringValue(const std::string& osValue)
+{
+    unset();
+    const size_t nSize = osValue.size();
+    if( nSize <= 8 )
+    {
+        m_eType = ValueType::STRING_MAX_8;
+        if( nSize )
+            memcpy( m_achValue, osValue.c_str(), nSize );
+        if( nSize < 8 )
+            m_achValue[nSize] = 0;
+    }
+    else
+    {
+        m_eType = ValueType::STRING;
+        m_pszValue = static_cast<char*>(CPLMalloc(1 + nSize));
+        memcpy(m_pszValue, osValue.c_str(), nSize);
+        m_pszValue[nSize] = 0;
+    }
+}
+
+/************************************************************************/
+/*                          setValue()                                  */
+/************************************************************************/
+
+void MVTTileLayerValue::setValue(double dfVal)
+{
+    if( dfVal >= 0 &&
+        dfVal <= static_cast<double>(std::numeric_limits<GUInt64>::max()) &&
+        dfVal == static_cast<double>(static_cast<GUInt64>(dfVal)) )
+    {
+        setUIntValue(static_cast<GUInt64>(dfVal));
+    }
+    else if( dfVal >= static_cast<double>(
+                                        std::numeric_limits<GInt64>::min()) &&
+             dfVal < 0 &&
+             dfVal == static_cast<double>(static_cast<GInt64>(dfVal)) )
+    {
+        setSIntValue(static_cast<GInt64>(dfVal));
+    }
+    else if( !CPLIsFinite(dfVal) ||
+             (dfVal >= -std::numeric_limits<float>::max() &&
+              dfVal <= std::numeric_limits<float>::max() &&
+              dfVal == static_cast<float>(dfVal)) )
+    {
+        setFloatValue( static_cast<float>(dfVal) );
+    }
+    else
+    {
+        setDoubleValue( dfVal );
+    }
+}
+
+/************************************************************************/
+/*                            getSize()                                 */
+/************************************************************************/
+
+size_t MVTTileLayerValue::getSize() const
+{
+    switch( m_eType )
+    {
+        case ValueType::NONE: return 0;
+        case ValueType::STRING:
+        {
+            const size_t nSize = strlen(m_pszValue);
+            return knSIZE_KEY + GetVarUIntSize(nSize) + nSize;
+        }
+        case ValueType::STRING_MAX_8:
+        {
+            const size_t nSize = GetSizeMax8(m_achValue);
+            return knSIZE_KEY + GetVarUIntSize(nSize) + nSize;
+        }
+        case ValueType::FLOAT: return knSIZE_KEY + sizeof(float);
+        case ValueType::DOUBLE: return knSIZE_KEY + sizeof(double);
+        case ValueType::INT: return knSIZE_KEY + GetVarIntSize(m_nIntValue);
+        case ValueType::UINT: return knSIZE_KEY + GetVarUIntSize(m_nUIntValue);
+        case ValueType::SINT: return knSIZE_KEY + GetVarSIntSize(m_nIntValue);
+        case ValueType::BOOL: return knSIZE_KEY + 1;
+        default: return 0;
+    }
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTileLayerValue::write(GByte** ppabyData) const
+{
+    GByte* pabyData = *ppabyData;
+
+    switch( m_eType )
+    {
+        case ValueType::STRING:
+        {
+            const size_t nSize = strlen(m_pszValue);
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_STRING, WT_DATA));
+            WriteVarUInt(&pabyData, nSize);
+            memcpy(pabyData, m_pszValue, nSize);
+            pabyData += nSize;
+            break;
+        }
+
+        case ValueType::STRING_MAX_8:
+        {
+            const size_t nSize = GetSizeMax8(m_achValue);
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_STRING, WT_DATA));
+            WriteVarUInt(&pabyData, nSize);
+            if( nSize )
+                memcpy(pabyData, m_achValue, nSize);
+            pabyData += nSize;
+            break;
+        }
+
+        case ValueType::FLOAT:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_FLOAT, WT_32BIT));
+            WriteFloat32(&pabyData, m_fValue);
+            break;
+
+        case ValueType::DOUBLE:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_DOUBLE, WT_64BIT));
+            WriteFloat64(&pabyData, m_dfValue);
+            break;
+
+        case ValueType::INT:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_INT, WT_VARINT));
+            WriteVarInt(&pabyData, m_nIntValue);
+            break;
+
+        case ValueType::UINT:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_UINT, WT_VARINT));
+            WriteVarUInt(&pabyData, m_nUIntValue);
+            break;
+
+        case ValueType::SINT:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_SINT, WT_VARINT));
+            WriteVarSInt(&pabyData, m_nIntValue);
+            break;
+
+        case ValueType::BOOL:
+            WriteVarUIntSingleByte(&pabyData,
+                                   MAKE_KEY(knVALUE_BOOL, WT_VARINT));
+            WriteVarUIntSingleByte(&pabyData, m_bBoolValue ? 1 : 0);
+            break;
+
+        default:
+            break;
+    }
+
+    CPLAssert(pabyData == *ppabyData + getSize());
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTileLayerValue::read(const GByte** ppabyData, const GByte* pabyDataLimit)
+{
+    const GByte* pabyData = *ppabyData;
+
+    try
+    {
+        unsigned int nKey = 0;
+        if( pabyData < pabyDataLimit )
+        {
+            READ_FIELD_KEY(nKey);
+
+            if( nKey == MAKE_KEY(knVALUE_STRING, WT_DATA) )
+            {
+                char* pszValue = nullptr;
+                READ_TEXT(pabyData, pabyDataLimit, pszValue);
+                // cppcheck-suppress nullPointer
+                setStringValue(pszValue);
+                CPLFree(pszValue);
+            }
+            else if( nKey == MAKE_KEY(knVALUE_FLOAT, WT_32BIT) )
+            {
+                setFloatValue(ReadFloat32(&pabyData, pabyDataLimit));
+            }
+            else if( nKey == MAKE_KEY(knVALUE_DOUBLE, WT_64BIT) )
+            {
+                setDoubleValue(ReadFloat64(&pabyData, pabyDataLimit));
+            }
+            else if( nKey == MAKE_KEY(knVALUE_INT, WT_VARINT) )
+            {
+                GIntBig nVal = 0;
+                READ_VARINT64(pabyData, pabyDataLimit, nVal);
+                setIntValue(nVal);
+            }
+            else if( nKey == MAKE_KEY(knVALUE_UINT, WT_VARINT) )
+            {
+                GUIntBig nVal = 0;
+                READ_VARUINT64(pabyData, pabyDataLimit, nVal);
+                setUIntValue(nVal);
+            }
+            else if( nKey == MAKE_KEY(knVALUE_SINT, WT_VARINT) )
+            {
+                GIntBig nVal = 0;
+                READ_VARSINT64(pabyData, pabyDataLimit, nVal);
+                setSIntValue(nVal);
+            }
+            else if( nKey == MAKE_KEY(knVALUE_BOOL, WT_VARINT) )
+            {
+                unsigned nVal = 0;
+                READ_VARUINT32(pabyData, pabyDataLimit, nVal);
+                setBoolValue(nVal != 0);
+            }
+        }
+        *ppabyData = pabyData;
+        return true;
+    }
+    catch( const GPBException& )
+    {
+        return false;
+    }
+}
+
+/************************************************************************/
+/*                           MVTTileLayer()                             */
+/************************************************************************/
+
+MVTTileLayerFeature::MVTTileLayerFeature()
+{
+}
+
+/************************************************************************/
+/*                             setOwner()                               */
+/************************************************************************/
+
+void MVTTileLayerFeature::setOwner(MVTTileLayer* poOwner)
+{
+    CPLAssert( !m_poOwner );
+    m_poOwner = poOwner;
+    m_poOwner->invalidateCachedSize();
+}
+
+/************************************************************************/
+/*                       invalidateCachedSize()                         */
+/************************************************************************/
+
+void MVTTileLayerFeature::invalidateCachedSize()
+{
+    m_bCachedSize = false;
+    m_nCachedSize = 0;
+    if( m_poOwner )
+        m_poOwner->invalidateCachedSize();
+}
+
+/************************************************************************/
+/*                        GetPackedArraySize()                          */
+/************************************************************************/
+
+static size_t GetPackedArraySize(const std::vector<GUInt32>& anVals)
+{
+    size_t nPackedSize = 0;
+    for( const auto& nVal: anVals )
+    {
+        nPackedSize += GetVarUIntSize(nVal);
+    }
+    return nPackedSize;
+}
+
+/************************************************************************/
+/*                            getSize()                                 */
+/************************************************************************/
+
+size_t MVTTileLayerFeature::getSize() const
+{
+    if( m_bCachedSize )
+        return m_nCachedSize;
+    m_bCachedSize = true;
+    m_nCachedSize = 0;
+    if( m_bHasId )
+        m_nCachedSize += knSIZE_KEY + GetVarUIntSize(m_nId);
+    if( !m_anTags.empty() )
+    {
+        size_t nPackedSize = GetPackedArraySize(m_anTags);
+        m_nCachedSize += knSIZE_KEY;
+        m_nCachedSize += GetVarUIntSize(nPackedSize);
+        m_nCachedSize += nPackedSize;
+    }
+    if( m_bHasType )
+        m_nCachedSize += knSIZE_KEY + 1; // fixed size for m_eType
+    if( !m_anGeometry.empty() )
+    {
+        size_t nPackedSize = GetPackedArraySize(m_anGeometry);
+        m_nCachedSize += knSIZE_KEY;
+        m_nCachedSize += GetVarUIntSize(nPackedSize);
+        m_nCachedSize += nPackedSize;
+    }
+    return m_nCachedSize;
+}
+
+/************************************************************************/
+/*                        WriteUIntPackedArray()                        */
+/************************************************************************/
+
+static void WriteUIntPackedArray(GByte** ppabyData, int nKey,
+                                 const std::vector<GUInt32>& anVals)
+{
+    GByte* pabyData = *ppabyData;
+    const size_t nPackedSize = GetPackedArraySize(anVals);
+    WriteVarUIntSingleByte(&pabyData, nKey);
+    WriteVarUInt(&pabyData, nPackedSize);
+    for( const auto& nVal: anVals )
+    {
+        WriteVarUInt(&pabyData, nVal);
+    }
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTileLayerFeature::write(GByte** ppabyData) const
+{
+    GByte* pabyData = *ppabyData;
+
+    if( m_bHasId )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knFEATURE_ID, WT_VARINT));
+        WriteVarUInt(&pabyData, m_nId);
+    }
+    if( !m_anTags.empty() )
+    {
+        WriteUIntPackedArray(&pabyData, MAKE_KEY(knFEATURE_TAGS, WT_DATA),
+                             m_anTags);
+    }
+    if( m_bHasType )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knFEATURE_TYPE, WT_VARINT));
+        WriteVarUIntSingleByte(&pabyData, static_cast<GUIntBig>(m_eType));
+    }
+    if( !m_anGeometry.empty() )
+    {
+        WriteUIntPackedArray(&pabyData, MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA),
+                             m_anGeometry);
+    }
+
+    CPLAssert(pabyData == *ppabyData + getSize());
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTileLayerFeature::read(const GByte** ppabyData,
+                               const GByte* pabyDataLimit)
+{
+    const GByte* pabyData = *ppabyData;
+
+    try
+    {
+        unsigned int nKey = 0;
+        while( pabyData < pabyDataLimit )
+        {
+            READ_FIELD_KEY(nKey);
+            if( nKey == MAKE_KEY(knFEATURE_ID, WT_VARINT) )
+            {
+                GUIntBig nID = 0;
+                READ_VARUINT64(pabyData, pabyDataLimit, nID);
+                setId(nID);
+            }
+            else if( nKey == MAKE_KEY(knFEATURE_TAGS, WT_DATA) )
+            {
+                unsigned int nTagsSize = 0;
+                READ_SIZE(pabyData, pabyDataLimit, nTagsSize);
+                const GByte* pabyDataTagsEnd = pabyData + nTagsSize;
+                while( pabyData < pabyDataTagsEnd )
+                {
+                    unsigned int nTag = 0;
+                    READ_VARUINT32(pabyData, pabyDataTagsEnd, nTag);
+                    addTag(nTag);
+                }
+                pabyData = pabyDataTagsEnd;
+            }
+            else if( nKey == MAKE_KEY(knFEATURE_TYPE, WT_VARINT) )
+            {
+                unsigned int nType = 0;
+                READ_VARUINT32(pabyData, pabyDataLimit, nType);
+                if( nType <= knGEOM_TYPE_POLYGON )
+                    setType(static_cast<GeomType>(nType));
+            }
+            else if( nKey == MAKE_KEY(knFEATURE_GEOMETRY, WT_DATA) )
+            {
+                unsigned int nGeometrySize = 0;
+                READ_SIZE(pabyData, pabyDataLimit, nGeometrySize);
+                const GByte* pabyDataGeometryEnd = pabyData + nGeometrySize;
+                while( pabyData < pabyDataGeometryEnd )
+                {
+                    unsigned int nGeometry = 0;
+                    READ_VARUINT32(pabyData, pabyDataGeometryEnd, nGeometry);
+                    addGeometry(nGeometry);
+                }
+                pabyData = pabyDataGeometryEnd;
+            }
+            else
+            {
+                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
+            }
+        }
+        *ppabyData = pabyData;
+        return true;
+    }
+    catch( const GPBException& )
+    {
+        return false;
+    }
+}
+
+/************************************************************************/
+/*                           MVTTileLayer()                             */
+/************************************************************************/
+
+MVTTileLayer::MVTTileLayer()
+{
+}
+
+/************************************************************************/
+/*                             setOwner()                               */
+/************************************************************************/
+
+void MVTTileLayer::setOwner(MVTTile* poOwner)
+{
+    CPLAssert( !m_poOwner );
+    m_poOwner = poOwner;
+    m_poOwner->invalidateCachedSize();
+}
+
+/************************************************************************/
+/*                       invalidateCachedSize()                         */
+/************************************************************************/
+
+void MVTTileLayer::invalidateCachedSize()
+{
+    m_bCachedSize = false;
+    m_nCachedSize = 0;
+    if( m_poOwner )
+        m_poOwner->invalidateCachedSize();
+}
+
+/************************************************************************/
+/*                          addFeature()                                */
+/************************************************************************/
+
+size_t MVTTileLayer::addFeature(std::shared_ptr<MVTTileLayerFeature> poFeature)
+{
+    poFeature->setOwner(this);
+    m_apoFeatures.push_back(poFeature);
+    invalidateCachedSize();
+    return m_apoFeatures.size() - 1;
+}
+
+/************************************************************************/
+/*                            getSize()                                 */
+/************************************************************************/
+
+size_t MVTTileLayer::getSize() const
+{
+    if( m_bCachedSize )
+        return m_nCachedSize;
+    m_nCachedSize = knSIZE_KEY + GetTextSize(m_osName);
+    for( const auto& poFeature: m_apoFeatures )
+    {
+        const size_t nFeatureSize = poFeature->getSize();
+        m_nCachedSize += knSIZE_KEY +
+                         GetVarUIntSize(nFeatureSize) + nFeatureSize;
+    }
+    for( const auto& osKey: m_aosKeys )
+    {
+        m_nCachedSize += knSIZE_KEY + GetTextSize(osKey);
+    }
+    for( const auto& oValue: m_aoValues )
+    {
+        const size_t nValueSize = oValue.getSize();
+        m_nCachedSize += knSIZE_KEY + GetVarUIntSize(nValueSize) + nValueSize;
+    }
+    if( m_bHasExtent )
+    {
+        m_nCachedSize += knSIZE_KEY + GetVarUIntSize(m_nExtent);
+    }
+    m_nCachedSize += knSIZE_KEY + GetVarUIntSize(m_nVersion);
+    m_bCachedSize = true;
+    return m_nCachedSize;
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTileLayer::write(GByte** ppabyData) const
+{
+    GByte* pabyData = *ppabyData;
+
+    WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_NAME, WT_DATA));
+    WriteText(&pabyData, m_osName);
+
+    for( const auto& poFeature: m_apoFeatures )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_FEATURES, WT_DATA));
+        WriteVarUInt(&pabyData, poFeature->getSize());
+        poFeature->write(&pabyData);
+    }
+
+    for( const auto& osKey: m_aosKeys )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_KEYS, WT_DATA));
+        WriteText(&pabyData, osKey);
+    }
+
+    for( const auto& oValue: m_aoValues )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_VALUES, WT_DATA));
+        WriteVarUInt(&pabyData, oValue.getSize());
+        oValue.write(&pabyData);
+    }
+
+    if( m_bHasExtent )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_EXTENT, WT_VARINT));
+        WriteVarUInt(&pabyData, m_nExtent);
+    }
+
+    WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER_VERSION, WT_VARINT));
+    WriteVarUInt(&pabyData, m_nVersion);
+
+    CPLAssert(pabyData == *ppabyData + getSize());
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTileLayer::write(GByte* pabyData) const
+{
+    write(&pabyData);
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+std::string MVTTileLayer::write() const
+{
+    std::string buffer;
+    size_t nSize = getSize();
+    buffer.resize(nSize);
+    write( reinterpret_cast<GByte*>(&buffer[0]) );
+    return buffer;
+}
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTileLayer::read(const GByte** ppabyData, const GByte* pabyDataLimit)
+{
+    const GByte* pabyData = *ppabyData;
+
+    try
+    {
+        unsigned int nKey = 0;
+        while( pabyData < pabyDataLimit )
+        {
+            READ_FIELD_KEY(nKey);
+            if( nKey == MAKE_KEY(knLAYER_NAME, WT_DATA) )
+            {
+                char* pszLayerName = nullptr;
+                READ_TEXT(pabyData, pabyDataLimit, pszLayerName);
+                // cppcheck-suppress nullPointer
+                setName(pszLayerName);
+                CPLFree(pszLayerName);
+            }
+            else if( nKey == MAKE_KEY(knLAYER_FEATURES, WT_DATA) )
+            {
+                unsigned int nFeatureLength = 0;
+                READ_SIZE(pabyData, pabyDataLimit, nFeatureLength);
+                const GByte* pabyDataFeatureEnd = pabyData + nFeatureLength;
+                std::shared_ptr<MVTTileLayerFeature> poFeature(
+                    new MVTTileLayerFeature());
+                addFeature(poFeature);
+                if( !poFeature->read(&pabyData, pabyDataFeatureEnd) )
+                    return false;
+                pabyData = pabyDataFeatureEnd;
+            }
+            else if( nKey == MAKE_KEY(knLAYER_KEYS, WT_DATA) )
+            {
+                char* pszKey = nullptr;
+                READ_TEXT(pabyData, pabyDataLimit, pszKey);
+                // cppcheck-suppress nullPointer
+                addKey(pszKey);
+                CPLFree(pszKey);
+            }
+            else if( nKey == MAKE_KEY(knLAYER_VALUES, WT_DATA) )
+            {
+                unsigned int nValueLength = 0;
+                READ_SIZE(pabyData, pabyDataLimit, nValueLength);
+                const GByte* pabyDataValueEnd = pabyData + nValueLength;
+                MVTTileLayerValue oValue;
+                if( !oValue.read(&pabyData, pabyDataValueEnd) )
+                    return false;
+                addValue(oValue);
+                pabyData = pabyDataValueEnd;
+            }
+            else if( nKey == MAKE_KEY(knLAYER_EXTENT, WT_VARINT) )
+            {
+                unsigned int nExtent = 0;
+                READ_VARUINT32(pabyData, pabyDataLimit, nExtent);
+                setExtent(nExtent);
+            }
+            else if( nKey == MAKE_KEY(knLAYER_VERSION, WT_VARINT) )
+            {
+                unsigned int nVersion = 0;
+                READ_VARUINT32(pabyData, pabyDataLimit, nVersion);
+                setVersion(nVersion);
+            }
+            else
+            {
+                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
+            }
+        }
+        *ppabyData = pabyData;
+        return true;
+    }
+    catch( const GPBException& )
+    {
+        return false;
+    }
+}
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTileLayer::read(const GByte* pabyData, const GByte* pabyEnd)
+{
+    return read(&pabyData, pabyEnd);
+}
+
+/************************************************************************/
+/*                             MVTTile()                                */
+/************************************************************************/
+
+MVTTile::MVTTile()
+{
+}
+
+/************************************************************************/
+/*                            addLayer()                                */
+/************************************************************************/
+
+void MVTTile::addLayer(std::shared_ptr<MVTTileLayer> poLayer)
+{
+    poLayer->setOwner(this);
+    invalidateCachedSize();
+    m_apoLayers.push_back(poLayer);
+}
+
+/************************************************************************/
+/*                            getSize()                                 */
+/************************************************************************/
+
+size_t MVTTile::getSize() const
+{
+    if( m_bCachedSize )
+        return m_nCachedSize;
+    m_nCachedSize = 0;
+    for( const auto& poLayer: m_apoLayers )
+    {
+        const size_t nLayerSize = poLayer->getSize();
+        m_nCachedSize += knSIZE_KEY + GetVarUIntSize(nLayerSize) + nLayerSize;
+    }
+    m_bCachedSize = true;
+    return m_nCachedSize;
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTile::write(GByte** ppabyData) const
+{
+    GByte* pabyData = *ppabyData;
+
+    for( const auto& poLayer: m_apoLayers )
+    {
+        WriteVarUIntSingleByte(&pabyData, MAKE_KEY(knLAYER, WT_DATA));
+        WriteVarUInt(&pabyData, poLayer->getSize());
+        poLayer->write(&pabyData);
+    }
+
+    CPLAssert(pabyData == *ppabyData + getSize());
+    *ppabyData = pabyData;
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+void MVTTile::write(GByte* pabyData) const
+{
+    write(&pabyData);
+}
+
+/************************************************************************/
+/*                             write()                                  */
+/************************************************************************/
+
+std::string MVTTile::write() const
+{
+    std::string buffer;
+    size_t nSize = getSize();
+    if( nSize )
+    {
+        buffer.resize(nSize);
+        write( reinterpret_cast<GByte*>(&buffer[0]) );
+    }
+    return buffer;
+}
+
+#ifdef ADD_MVT_TILE_READ
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTile::read(const GByte** ppabyData, const GByte* pabyDataLimit)
+{
+    const GByte* pabyData = *ppabyData;
+
+    try
+    {
+        unsigned int nKey = 0;
+        while( pabyData < pabyDataLimit )
+        {
+            READ_FIELD_KEY(nKey);
+            if( nKey == MAKE_KEY(knLAYER, WT_DATA) )
+            {
+                unsigned int nLayerSize = 0;
+                READ_SIZE(pabyData, pabyDataLimit, nLayerSize);
+                const GByte* pabyDataLimitLayer = pabyData + nLayerSize;
+                std::shared_ptr<MVTTileLayer> poLayer(new MVTTileLayer());
+                addLayer(poLayer);
+                if( !poLayer->read(&pabyData, pabyDataLimitLayer) )
+                    return false;
+                pabyData = pabyDataLimitLayer;
+            }
+            else
+            {
+                SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, FALSE);
+            }
+        }
+        *ppabyData = pabyData;
+        return true;
+    }
+    catch( const GPBException& )
+    {
+        return false;
+    }
+}
+
+/************************************************************************/
+/*                             read()                                   */
+/************************************************************************/
+
+bool MVTTile::read(const GByte* pabyData, const GByte* pabyEnd)
+{
+    return read(&pabyData, pabyEnd);
+}
+
+#endif

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.h
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.h	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile.h	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,355 @@
+/******************************************************************************
+ *
+ * Project:  MVT Translator
+ * Purpose:  Mapbox Vector Tile decoder and encoder
+ * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef MVT_TILE_H
+#define MVT_TILE_H
+
+#include "cpl_port.h"
+
+#include <memory>
+#include <vector>
+
+/* See https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto */
+constexpr int knLAYER = 3;
+
+constexpr int knLAYER_NAME = 1;
+constexpr int knLAYER_FEATURES = 2;
+constexpr int knLAYER_KEYS = 3;
+constexpr int knLAYER_VALUES = 4;
+constexpr int knLAYER_EXTENT = 5;
+constexpr int knLAYER_VERSION = 15;
+
+constexpr int knVALUE_STRING = 1;
+constexpr int knVALUE_FLOAT = 2;
+constexpr int knVALUE_DOUBLE = 3;
+constexpr int knVALUE_INT = 4;
+constexpr int knVALUE_UINT = 5;
+constexpr int knVALUE_SINT = 6;
+constexpr int knVALUE_BOOL = 7;
+
+constexpr int knFEATURE_ID = 1;
+constexpr int knFEATURE_TAGS = 2;
+constexpr int knFEATURE_TYPE = 3;
+constexpr int knFEATURE_GEOMETRY = 4;
+
+constexpr int knGEOM_TYPE_UNKNOWN = 0;
+constexpr int knGEOM_TYPE_POINT = 1;
+constexpr int knGEOM_TYPE_LINESTRING = 2;
+constexpr int knGEOM_TYPE_POLYGON = 3;
+
+constexpr int knCMD_MOVETO = 1;
+constexpr int knCMD_LINETO = 2;
+constexpr int knCMD_CLOSEPATH = 7;
+
+constexpr unsigned knDEFAULT_EXTENT = 4096;
+
+/************************************************************************/
+/*                         MVTTileLayerValue                            */
+/************************************************************************/
+
+class MVTTileLayerValue
+{
+    public:
+        enum class ValueType
+        {
+            NONE,
+            STRING,
+            FLOAT,
+            DOUBLE,
+            INT,
+            UINT,
+            SINT,
+            BOOL,
+            STRING_MAX_8, // optimization for short strings.
+        };
+
+    private:
+        // Layout optimized for small memory footprint
+        union
+        {
+            float m_fValue;
+            double m_dfValue;
+            GInt64 m_nIntValue;
+            GUInt64 m_nUIntValue;
+            bool m_bBoolValue;
+            char* m_pszValue;
+            char m_achValue[8]; // optimization for short strings
+        };
+        ValueType m_eType = ValueType::NONE;
+
+        void unset();
+
+    public:
+        MVTTileLayerValue();
+       ~MVTTileLayerValue();
+        MVTTileLayerValue(const MVTTileLayerValue& oOther);
+        MVTTileLayerValue& operator=(const MVTTileLayerValue& oOther);
+
+        bool operator <(const MVTTileLayerValue& rhs) const;
+
+        ValueType getType() const { return m_eType; }
+        bool isNumeric() const { return m_eType == ValueType::FLOAT ||
+                                        m_eType == ValueType::DOUBLE ||
+                                        m_eType == ValueType::INT ||
+                                        m_eType == ValueType::UINT ||
+                                        m_eType == ValueType::SINT; }
+        bool isString() const { return m_eType == ValueType::STRING ||
+                                       m_eType == ValueType::STRING_MAX_8; }
+
+        float getFloatValue() const { return m_fValue; }
+        double getDoubleValue() const { return m_dfValue; }
+        GInt64 getIntValue() const { return m_nIntValue; }
+        GUInt64 getUIntValue() const { return m_nUIntValue; }
+        bool getBoolValue() const { return m_bBoolValue; }
+
+        double getNumericValue() const
+            { if( m_eType == ValueType::FLOAT )
+                return m_fValue;
+              if( m_eType == ValueType::DOUBLE )
+                return m_dfValue;
+              if( m_eType == ValueType::INT || m_eType == ValueType::SINT )
+                return static_cast<double>(m_nIntValue);
+              if( m_eType == ValueType::UINT )
+                return static_cast<double>(m_nUIntValue);
+              return 0.0;
+            }
+
+        std::string getStringValue() const
+            { if( m_eType == ValueType::STRING )
+                  return m_pszValue;
+              else if( m_eType == ValueType::STRING_MAX_8 )
+              {
+                  char szBuf[8+1];
+                  memcpy(szBuf, m_achValue, 8);
+                  szBuf[8] = 0;
+                  return szBuf;
+              }
+              return std::string();
+            }
+
+        void setStringValue(const std::string& osValue);
+        void setFloatValue(float fValue)
+            { unset(); m_eType = ValueType::FLOAT; m_fValue = fValue; }
+        void setDoubleValue(double dfValue)
+            { unset(); m_eType = ValueType::DOUBLE; m_dfValue = dfValue; }
+        void setIntValue(GInt64 nVal)
+            { unset(); m_eType = ValueType::INT; m_nIntValue = nVal; }
+        void setUIntValue(GUInt64 nVal)
+            { unset(); m_eType = ValueType::UINT; m_nUIntValue = nVal; }
+        void setSIntValue(GInt64 nVal)
+            { unset(); m_eType = ValueType::SINT; m_nIntValue = nVal; }
+        void setBoolValue(bool bVal)
+            { unset(); m_eType = ValueType::BOOL; m_bBoolValue = bVal; }
+
+        void setValue(double dfVal);
+        void setValue(int nVal) { setValue(static_cast<GInt64>(nVal)); }
+        void setValue(GInt64 nVal)
+            { if (nVal < 0)
+                setSIntValue(nVal);
+              else 
+                setUIntValue(nVal);
+            }
+
+        size_t getSize() const;
+        void write(GByte** ppabyData) const;
+        bool read(const GByte** ppabyData, const GByte* pabyEnd);
+};
+
+/************************************************************************/
+/*                       MVTTileLayerFeature                            */
+/************************************************************************/
+
+class MVTTileLayer;
+
+class MVTTileLayerFeature
+{
+    public:
+        enum class GeomType: char
+        {
+            UNKNOWN = 0,
+            POINT = 1,
+            LINESTRING = 2,
+            POLYGON = 3
+        };
+
+    private:
+        mutable size_t m_nCachedSize = 0;
+        GUInt64 m_nId = 0;
+        std::vector<GUInt32> m_anTags;
+        std::vector<GUInt32> m_anGeometry;
+        GeomType m_eType = GeomType::UNKNOWN;
+        mutable bool m_bCachedSize = false;
+        bool m_bHasId = false;
+        bool m_bHasType = false;
+        MVTTileLayer* m_poOwner = nullptr;
+
+    public:
+        MVTTileLayerFeature();
+        void setOwner(MVTTileLayer* poOwner);
+
+        bool hasId() const { return m_bHasId; }
+        GUInt64 getId() const { return m_nId; }
+        const std::vector<GUInt32>& getTags() const { return m_anTags; }
+        bool hasType() const { return m_bHasType; }
+        GeomType getType() const { return m_eType; }
+        GUInt32 getGeometryCount() const
+            { return static_cast<GUInt32>(m_anGeometry.size()); }
+        const std::vector<GUInt32>& getGeometry() const { return m_anGeometry; }
+
+        void setId(GUInt64 nId)
+            { m_bHasId = true;
+              m_nId = nId;
+              invalidateCachedSize(); }
+        void addTag(GUInt32 nTag)
+            { m_anTags.push_back(nTag);
+              invalidateCachedSize(); }
+        void setType(GeomType eType)
+            { m_bHasType = true;
+              m_eType = eType;
+              invalidateCachedSize(); }
+        void resizeGeometryArray(GUInt32 nNewSize)
+            { m_anGeometry.resize(nNewSize);
+              invalidateCachedSize(); }
+        void addGeometry(GUInt32 nGeometry)
+            { m_anGeometry.push_back(nGeometry);
+              invalidateCachedSize(); }
+        void setGeometry(GUInt32 nIdx, GUInt32 nVal)
+            { m_anGeometry[nIdx] = nVal;
+              invalidateCachedSize(); }
+        void setGeometry(const std::vector<GUInt32>& anGeometry )
+            { m_anGeometry = anGeometry;
+              invalidateCachedSize(); }
+
+        size_t getSize() const;
+        void write(GByte** ppabyData) const;
+        bool read(const GByte** ppabyData, const GByte* pabyEnd);
+
+        void invalidateCachedSize();
+};
+
+/************************************************************************/
+/*                           MVTTileLayer                               */
+/************************************************************************/
+
+class MVTTile;
+
+class MVTTileLayer
+{
+        mutable bool m_bCachedSize = false;
+        mutable size_t m_nCachedSize = 0;
+        GUInt32 m_nVersion = 1;
+        std::string m_osName;
+        std::vector<std::shared_ptr<MVTTileLayerFeature>> m_apoFeatures;
+        std::vector<std::string> m_aosKeys;
+        std::vector<MVTTileLayerValue> m_aoValues;
+        bool m_bHasExtent = false;
+        GUInt32 m_nExtent = 4096;
+        MVTTile* m_poOwner = nullptr;
+
+    public:
+        MVTTileLayer();
+        void setOwner(MVTTile* poOwner);
+
+        GUInt32 getVersion() const { return m_nVersion; }
+        const std::string& getName() const { return m_osName; }
+        const std::vector<std::shared_ptr<MVTTileLayerFeature>>& getFeatures()
+            const
+            { return m_apoFeatures; }
+        const std::vector<std::string>& getKeys() const { return m_aosKeys; }
+        const std::vector<MVTTileLayerValue>& getValues() const
+            { return m_aoValues; }
+        GUInt32 getExtent() const { return m_nExtent; }
+
+        void setVersion(GUInt32 nVersion)
+                            { m_nVersion = nVersion;
+                              invalidateCachedSize(); }
+        void setName(const std::string& osName)
+                            { m_osName = osName;
+                              invalidateCachedSize(); }
+        size_t addFeature(std::shared_ptr<MVTTileLayerFeature> poFeature);
+        GUInt32 addKey(const std::string& osKey)
+            {
+                m_aosKeys.push_back(osKey);
+                invalidateCachedSize();
+                return static_cast<GUInt32>(m_aosKeys.size()) - 1;
+            }
+
+        GUInt32 addValue(const MVTTileLayerValue& oValue)
+            {
+                m_aoValues.push_back(oValue);
+                invalidateCachedSize();
+                return static_cast<GUInt32>(m_aoValues.size()) - 1;
+            }
+
+        void setExtent(GUInt32 nExtent)
+            {
+                m_nExtent = nExtent;
+                m_bHasExtent = true;
+                invalidateCachedSize();
+            }
+
+        size_t getSize() const;
+        void write(GByte** ppabyData) const;
+        void write(GByte* pabyData) const;
+        std::string write() const;
+        bool read(const GByte** ppabyData, const GByte* pabyEnd);
+        bool read(const GByte* pabyData, const GByte* pabyEnd);
+
+        void invalidateCachedSize();
+};
+
+/************************************************************************/
+/*                              MVTTile                                 */
+/************************************************************************/
+
+class MVTTile
+{
+        std::vector<std::shared_ptr<MVTTileLayer>> m_apoLayers;
+        mutable size_t m_nCachedSize = 0;
+        mutable bool m_bCachedSize = false;
+
+    public:
+        MVTTile();
+
+        const std::vector<std::shared_ptr<MVTTileLayer>>& getLayers() const
+            { return m_apoLayers; }
+
+        void clear() { m_apoLayers.clear(); invalidateCachedSize(); }
+        void addLayer(std::shared_ptr<MVTTileLayer> poLayer);
+        size_t getSize() const;
+        void write(GByte** ppabyData) const;
+        void write(GByte* pabyData) const;
+        std::string write() const;
+#ifdef ADD_MVT_TILE_READ
+        bool read(const GByte** ppabyData, const GByte* pabyEnd);
+        bool read(const GByte* pabyData, const GByte* pabyEnd);
+#endif
+        void invalidateCachedSize() { m_bCachedSize = false; m_nCachedSize = 0; }
+};
+
+#endif // MVT_TILE_H

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile_test.cpp
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile_test.cpp	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/mvt_tile_test.cpp	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,203 @@
+/******************************************************************************
+ *
+ * Project:  MVT Translator
+ * Purpose:  Test Mapbox Vector Tile encoder
+ * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#define ADD_MVT_TILE_READ
+#include "mvt_tile.h"
+#include "mvt_tile.cpp"
+
+#include "cpl_conv.h"
+#include "cpl_error.h"
+#include "cpl_vsi.h"
+
+#include <limits>
+#include <memory>
+
+int main()
+{
+    {
+        MVTTile oEmptyTile;
+        CPLAssert( oEmptyTile.getSize() == 0 );
+    }
+
+    {
+        MVTTile oTile;
+        MVTTileLayer* poLayer = new MVTTileLayer();
+        oTile.addLayer( std::shared_ptr<MVTTileLayer>(poLayer) );
+        CPLAssert( oTile.getSize() ==
+            1 /* layer key */ +
+            1 /* layer size*/ +
+            1 /* name key */ +
+            1 /* version size */ +
+            1 /* version key */ +
+            1 /* version */
+        );
+    }
+
+    {
+        MVTTile oTile;
+        MVTTileLayer* poLayer = new MVTTileLayer();
+        oTile.addLayer( std::shared_ptr<MVTTileLayer>(poLayer) );
+        MVTTileLayerFeature* poFeature = new MVTTileLayerFeature();
+        poLayer->setVersion(2);
+        poLayer->setName(std::string("my_layer"));
+        poLayer->setExtent(4096);
+        poLayer->addFeature( std::shared_ptr<MVTTileLayerFeature>(poFeature) );
+        poLayer->addKey(std::string("key0"));
+        {
+            MVTTileLayerValue oValue;
+            oValue.setStringValue(std::string(""));
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setStringValue(std::string("x"));
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setStringValue(std::string("1234567"));
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setStringValue(std::string("12345678"));
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setStringValue(std::string("123456789"));
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setIntValue(-1);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setUIntValue(1);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setSIntValue(-1);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setFloatValue(1.25f);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setDoubleValue(1.25);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setBoolValue(true);
+            poLayer->addValue(oValue);
+        }
+
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(123456);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(-123456);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(123456.0);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(-123456.0);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(1.25);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(1.256789);
+            poLayer->addValue(oValue);
+        }
+        {
+            MVTTileLayerValue oValue;
+            oValue.setValue(std::numeric_limits<double>::infinity());
+            poLayer->addValue(oValue);
+        }
+
+        poFeature->setId(1);
+        poFeature->addTag(0);
+        poFeature->addTag(0);
+        poFeature->setType(MVTTileLayerFeature::GeomType::POINT);
+        poFeature->addGeometry(9);
+        poFeature->addGeometry(0);
+        poFeature->addGeometry(0);
+
+        poLayer->addFeature(
+            std::shared_ptr<MVTTileLayerFeature>(new MVTTileLayerFeature()) );
+
+        oTile.addLayer( std::shared_ptr<MVTTileLayer>(new MVTTileLayer()) );
+
+        poLayer = new MVTTileLayer();
+        oTile.addLayer( std::shared_ptr<MVTTileLayer>(poLayer) );
+        poLayer->addValue(MVTTileLayerValue());
+
+        size_t nSize = oTile.getSize();
+        GByte* pabyBuffer = static_cast<GByte*>(CPLMalloc(nSize));
+        oTile.write(pabyBuffer);
+        VSILFILE* fp = VSIFOpenL("out.gpb", "wb");
+        if( fp )
+        {
+            VSIFWriteL(pabyBuffer, 1, nSize, fp);
+            VSIFCloseL(fp);
+        }
+
+        MVTTile oTileDeserialized;
+        bool bRet = oTileDeserialized.read(pabyBuffer, pabyBuffer + nSize);
+        CPLAssert(bRet);
+        size_t nSize2 = oTileDeserialized.getSize();
+        CPLAssert(nSize == nSize2);
+        GByte* pabyBuffer2 = static_cast<GByte*>(CPLMalloc(nSize2));
+        oTileDeserialized.write(pabyBuffer2);
+        CPLAssert(memcmp(pabyBuffer, pabyBuffer2, nSize) == 0);
+        CPLFree(pabyBuffer);
+        CPLFree(pabyBuffer2);
+    }
+
+    return 0;
+}

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.cpp
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.cpp	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.cpp	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,218 @@
+/******************************************************************************
+ *
+ * Project:  MVT Translator
+ * Purpose:  Mapbox Vector Tile decoder
+ * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "mvtutils.h"
+#include "ogr_api.h"
+
+/************************************************************************/
+/*                        OGRMVTInitFields()                            */
+/************************************************************************/
+
+void OGRMVTInitFields(OGRFeatureDefn* poFeatureDefn,
+                      const CPLJSONObject& oFields)
+{
+    {
+        OGRFieldDefn oFieldDefnId("mvt_id", OFTInteger64);
+        poFeatureDefn->AddFieldDefn(&oFieldDefnId);
+    }
+
+    if( oFields.IsValid() )
+    {
+        for( const auto& oField: oFields.GetChildren() )
+        {
+            if( oField.GetType() == CPLJSONObject::String )
+            {
+                if( oField.ToString() == "Number" )
+                {
+                    OGRFieldDefn oFieldDefn(
+                        oField.GetName().c_str(), OFTReal );
+                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
+                }
+                else if( oField.ToString() == "Integer" ) // GDAL extension
+                {
+                    OGRFieldDefn oFieldDefn(
+                        oField.GetName().c_str(), OFTInteger );
+                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
+                }
+                else if( oField.ToString() == "Boolean" )
+                {
+                    OGRFieldDefn oFieldDefn(
+                        oField.GetName().c_str(), OFTInteger );
+                    oFieldDefn.SetSubType(OFSTBoolean);
+                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
+                }
+                else
+                {
+                    OGRFieldDefn oFieldDefn(
+                        oField.GetName().c_str(), OFTString );
+                    poFeatureDefn->AddFieldDefn(&oFieldDefn);
+                }
+            }
+        }
+    }
+}
+
+/************************************************************************/
+/*                     OGRMVTFindGeomTypeFromTileStat()                 */
+/************************************************************************/
+
+OGRwkbGeometryType OGRMVTFindGeomTypeFromTileStat(
+                                const CPLJSONArray& oTileStatLayers,
+                                const char* pszLayerName)
+{
+    OGRwkbGeometryType eGeomType = wkbUnknown;
+    for( int i = 0; i < oTileStatLayers.Size(); i++ )
+    {
+        CPLJSONObject oId =
+            oTileStatLayers[i].GetObj("layer");
+        if( oId.IsValid() && oId.GetType() ==
+                CPLJSONObject::String )
+        {
+            if( oId.ToString() == pszLayerName )
+            {
+                CPLJSONObject oGeom =
+                    oTileStatLayers[i].GetObj("geometry");
+                if( oGeom.IsValid() && oGeom.GetType() ==
+                        CPLJSONObject::String )
+                {
+                    const std::string oGeomType(
+                        oGeom.ToString());
+                    // Note: this information is not 
+                    // reliable in case
+                    // of mix of geometry types
+                    if( oGeomType == "Point" )
+                    {
+                        eGeomType = wkbMultiPoint;
+                    }
+                    else if( oGeomType == "LineString" )
+                    {
+                        eGeomType = wkbMultiLineString;
+                    }
+                    else if( oGeomType == "Polygon" )
+                    {
+                        eGeomType = wkbMultiPolygon;
+                    }
+                }
+                break;
+            }
+        }
+    }
+    return eGeomType;
+}
+
+
+/************************************************************************/
+/*                     OGRMVTCreateFeatureFrom()                        */
+/************************************************************************/
+
+OGRFeature* OGRMVTCreateFeatureFrom(OGRFeature* poSrcFeature,
+                                    OGRFeatureDefn* poTargetFeatureDefn,
+                                    bool bJsonField,
+                                    OGRSpatialReference* poSRS)
+{
+    OGRFeature* poFeature = new OGRFeature(poTargetFeatureDefn);
+    if( bJsonField )
+    {
+        CPLJSONObject oProperties;
+        bool bEmpty = true;
+        for( int i = 1; i < poSrcFeature->GetFieldCount(); i++ )
+        {
+            if( poSrcFeature->IsFieldSet(i) )
+            {
+                bEmpty = false;
+                OGRFieldDefn* poFDefn = poSrcFeature->GetFieldDefnRef(i);
+                if( poSrcFeature->IsFieldNull(i) )
+                {
+                    oProperties.AddNull(poFDefn->GetNameRef());
+                }
+                else if( poFDefn->GetType() == OFTInteger ||
+                         poFDefn->GetType() == OFTInteger64 )
+                {
+                    if( poFDefn->GetSubType() == OFSTBoolean )
+                    {
+                        oProperties.Add(poFDefn->GetNameRef(),
+                                  poSrcFeature->GetFieldAsInteger(i) == 1);
+                    }
+                    else
+                    {
+                        oProperties.Add(poFDefn->GetNameRef(),
+                                  poSrcFeature->GetFieldAsInteger64(i));
+                    }
+                }
+                else if( poFDefn->GetType() == OFTReal )
+                {
+                    oProperties.Add(poFDefn->GetNameRef(),
+                              poSrcFeature->GetFieldAsDouble(i));
+                }
+                else
+                {
+                    oProperties.Add(poFDefn->GetNameRef(),
+                              poSrcFeature->GetFieldAsString(i));
+                }
+            }
+        }
+        if( !bEmpty )
+        {
+            poFeature->SetField("json",
+                            oProperties.Format(CPLJSONObject::Pretty).c_str());
+        }
+
+        OGRGeometry* poSrcGeom = poSrcFeature->GetGeometryRef();
+        if( poSrcGeom )
+        {
+            poFeature->SetGeometry( poSrcGeom );
+        }
+#ifdef nodef
+        CPLJSONObject oObj;
+        oObj.Add("type", "Feature");
+        if( poSrcFeature->IsFieldSet(0) )
+            oObj.Add("id", poSrcFeature->GetFieldAsInteger64("mvt_id"));
+        oObj.Add("properties", oProperties);
+        if( poSrcGeom )
+        {
+            char* pszGeomJson = OGR_G_ExportToJson(
+                reinterpret_cast<OGRGeometryH>(poSrcGeom) );
+            CPLJSONDocument oJSonDoc;
+            oJSonDoc.LoadMemory(reinterpret_cast<const GByte*>(pszGeomJson));
+            CPLFree(pszGeomJson);
+            oObj.Add( "geometry", oJSonDoc.GetRoot() );
+        }
+        poFeature->SetNativeData(
+            oObj.Format(CPLJSONObject::Pretty).c_str());
+        poFeature->SetNativeMediaType("application/vnd.geo+json");
+#endif
+    }
+    else
+    {
+        poFeature->SetFrom( poSrcFeature );
+    }
+    OGRGeometry* poGeom = poFeature->GetGeometryRef();
+    if( poGeom )
+        poGeom->assignSpatialReference(poSRS);
+    return poFeature;
+}

Added: sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.h
===================================================================
--- sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.h	                        (rev 0)
+++ sandbox/jng/mvt_alt/Common/Renderers/mvt/mvtutils.h	2019-09-21 17:25:40 UTC (rev 9605)
@@ -0,0 +1,91 @@
+/******************************************************************************
+ *
+ * Project:  MVT Translator
+ * Purpose:  Mapbox Vector Tile decoder
+ * Author:   Even Rouault, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef MVTUTILS_H
+#define MVTUTILS_H
+
+#include "cpl_json.h"
+#include "ogrsf_frmts.h"
+
+#define MVT_LCO \
+"<LayerCreationOptionList>" \
+"  <Option name='MINZOOM' type='int' min='0' max='22' " \
+        "description='Minimum zoom level'/>" \
+"  <Option name='MAXZOOM' type='int' min='0' max='22' " \
+        "description='Maximum zoom level'/>" \
+"  <Option name='NAME' type='string' description='Target layer name'/>" \
+"  <Option name='DESCRIPTION' type='string' " \
+        "description='A description of the layer'/>" \
+"</LayerCreationOptionList>"
+
+#define MVT_MBTILES_COMMON_DSCO \
+"  <Option name='MINZOOM' scope='vector' type='int' min='0' max='22' " \
+        "description='Minimum zoom level' default='0'/>" \
+"  <Option name='MAXZOOM' scope='vector' type='int' min='0' max='22' " \
+        "description='Maximum zoom level' default='5'/>" \
+"  <Option name='CONF' scope='vector' type='string' " \
+        "description='Layer configuration as a JSon serialized string, or a filename pointing to a JSon file'/>" \
+"  <Option name='SIMPLIFICATION' scope='vector' type='float' " \
+        "description='Simplification factor'/>" \
+"  <Option name='SIMPLIFICATION_MAX_ZOOM' scope='vector' type='float' " \
+        "description='Simplification factor at max zoom'/>" \
+"  <Option name='EXTENT' scope='vector' type='unsigned int' default='4096' " \
+        "description='Number of units in a tile'/>" \
+"  <Option name='BUFFER' scope='vector' type='unsigned int' default='80' " \
+        "description='Number of units for geometry buffering'/>" \
+"  <Option name='COMPRESS' scope='vector' type='boolean' description=" \
+        "'Whether to deflate-compress tiles' default='YES'/>" \
+"  <Option name='TEMPORARY_DB' scope='vector' type='string' description='" \
+        "Filename with path for the temporary database'/>" \
+"  <Option name='MAX_SIZE' scope='vector' type='unsigned int' min='100' default='500000' " \
+        "description='Maximum size of a tile in bytes'/>" \
+"  <Option name='MAX_FEATURES' scope='vector' type='unsigned int' min='1' default='200000' " \
+        "description='Maximum number of features per tile'/>"
+
+void OGRMVTInitFields(OGRFeatureDefn* poFeatureDefn,
+                      const CPLJSONObject& oFields);
+
+OGRwkbGeometryType OGRMVTFindGeomTypeFromTileStat(
+                                const CPLJSONArray& oTileStatLayers,
+                                const char* pszLayerName);
+
+OGRFeature* OGRMVTCreateFeatureFrom(OGRFeature* poSrcFeature,
+                                    OGRFeatureDefn* poTargetFeatureDefn,
+                                    bool bJsonField,
+                                    OGRSpatialReference* poSRS);
+
+#ifdef HAVE_MVT_WRITE_SUPPORT
+GDALDataset* OGRMVTWriterDatasetCreate( const char * pszFilename,
+                                   int nXSize,
+                                   int nYSize,
+                                   int nBandsIn,
+                                   GDALDataType eDT,
+                                   char ** papszOptions );
+#endif
+
+#endif // MVTUTILS_H



More information about the mapguide-commits mailing list