@@ -292,9 +292,14 @@ class OGRMVTDataset final : public GDALDataset
292
292
bool m_bClip = true ;
293
293
CPLString m_osTileExtension{" pbf" };
294
294
OGRSpatialReference *m_poSRS = nullptr ;
295
- double m_dfTileDim0 = 0.0 ;
296
- double m_dfTopXOrigin = 0.0 ;
297
- double m_dfTopYOrigin = 0.0 ;
295
+ double m_dfTileDim0 =
296
+ 0.0 ; // Extent (in CRS units) of a tile at zoom level 0
297
+ double m_dfTopXOrigin = 0.0 ; // top-left X of tile matrix scheme
298
+ double m_dfTopYOrigin = 0.0 ; // top-left Y of tile matrix scheme
299
+ int m_nTileMatrixWidth0 =
300
+ 1 ; // Number of tiles along X axis at zoom level 0
301
+ int m_nTileMatrixHeight0 =
302
+ 1 ; // Number of tiles along Y axis at zoom level 0
298
303
299
304
static GDALDataset *OpenDirectory (GDALOpenInfo *);
300
305
@@ -322,20 +327,30 @@ class OGRMVTDataset final : public GDALDataset
322
327
return m_poSRS;
323
328
}
324
329
325
- double GetTileDim0 () const
330
+ inline double GetTileDim0 () const
326
331
{
327
332
return m_dfTileDim0;
328
333
}
329
334
330
- double GetTopXOrigin () const
335
+ inline double GetTopXOrigin () const
331
336
{
332
337
return m_dfTopXOrigin;
333
338
}
334
339
335
- double GetTopYOrigin () const
340
+ inline double GetTopYOrigin () const
336
341
{
337
342
return m_dfTopYOrigin;
338
343
}
344
+
345
+ inline int GetTileMatrixWidth0 () const
346
+ {
347
+ return m_nTileMatrixWidth0;
348
+ }
349
+
350
+ inline int GetTileMatrixHeight0 () const
351
+ {
352
+ return m_nTileMatrixHeight0;
353
+ }
339
354
};
340
355
341
356
/* ***********************************************************************/
@@ -1747,8 +1762,10 @@ void OGRMVTDirectoryLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
1747
1762
1748
1763
if (sEnvelope .IsInit () && sEnvelope .MinX >= -10 * m_poDS->GetTileDim0 () &&
1749
1764
sEnvelope .MinY >= -10 * m_poDS->GetTileDim0 () &&
1750
- sEnvelope .MaxX <= 10 * m_poDS->GetTileDim0 () &&
1751
- sEnvelope .MaxY <= 10 * m_poDS->GetTileDim0 ())
1765
+ sEnvelope .MaxX <=
1766
+ 10 * m_poDS->GetTileDim0 () * m_poDS->GetTileMatrixWidth0 () &&
1767
+ sEnvelope .MaxY <=
1768
+ 10 * m_poDS->GetTileDim0 () * m_poDS->GetTileMatrixHeight0 ())
1752
1769
{
1753
1770
const double dfTileDim = m_poDS->GetTileDim0 () / (1 << m_nZ);
1754
1771
m_nFilterMinX = std::max (
@@ -1760,18 +1777,30 @@ void OGRMVTDirectoryLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
1760
1777
m_nFilterMaxX = std::min (
1761
1778
static_cast <int >(
1762
1779
ceil ((sEnvelope .MaxX - m_poDS->GetTopXOrigin ()) / dfTileDim)),
1763
- (1 << m_nZ) - 1 );
1780
+ static_cast <int >(std::min<int64_t >(
1781
+ INT_MAX, (static_cast <int64_t >(1 ) << m_nZ) *
1782
+ m_poDS->GetTileMatrixWidth0 () -
1783
+ 1 )));
1764
1784
m_nFilterMaxY = std::min (
1765
1785
static_cast <int >(
1766
1786
ceil ((m_poDS->GetTopYOrigin () - sEnvelope .MinY ) / dfTileDim)),
1767
- (1 << m_nZ) - 1 );
1787
+ static_cast <int >(std::min<int64_t >(
1788
+ INT_MAX, (static_cast <int64_t >(1 ) << m_nZ) *
1789
+ m_poDS->GetTileMatrixHeight0 () -
1790
+ 1 )));
1768
1791
}
1769
1792
else
1770
1793
{
1771
1794
m_nFilterMinX = 0 ;
1772
1795
m_nFilterMinY = 0 ;
1773
- m_nFilterMaxX = (1 << m_nZ) - 1 ;
1774
- m_nFilterMaxY = (1 << m_nZ) - 1 ;
1796
+ m_nFilterMaxX = static_cast <int >(
1797
+ std::min<int64_t >(INT_MAX, (static_cast <int64_t >(1 ) << m_nZ) *
1798
+ m_poDS->GetTileMatrixWidth0 () -
1799
+ 1 ));
1800
+ m_nFilterMaxY = static_cast <int >(
1801
+ std::min<int64_t >(INT_MAX, (static_cast <int64_t >(1 ) << m_nZ) *
1802
+ m_poDS->GetTileMatrixHeight0 () -
1803
+ 1 ));
1775
1804
}
1776
1805
}
1777
1806
@@ -2432,6 +2461,7 @@ static bool LoadMetadata(const CPLString &osMetadataFile,
2432
2461
CPLJSONArray &oTileStatLayers, CPLJSONObject &oBounds,
2433
2462
OGRSpatialReference *poSRS, double &dfTopX,
2434
2463
double &dfTopY, double &dfTileDim0,
2464
+ int &nTileMatrixWidth0, int &nTileMatrixHeight0,
2435
2465
const CPLString &osMetadataMemFilename)
2436
2466
2437
2467
{
@@ -2454,17 +2484,36 @@ static bool LoadMetadata(const CPLString &osMetadataFile,
2454
2484
if (!bLoadOK)
2455
2485
return false ;
2456
2486
2457
- CPLJSONObject oCrs (oDoc.GetRoot ().GetObj (" crs" ));
2458
- CPLJSONObject oTopX (oDoc.GetRoot ().GetObj (" tile_origin_upper_left_x" ));
2459
- CPLJSONObject oTopY (oDoc.GetRoot ().GetObj (" tile_origin_upper_left_y" ));
2460
- CPLJSONObject oTileDim0 (oDoc.GetRoot ().GetObj (" tile_dimension_zoom_0" ));
2487
+ const CPLJSONObject oCrs (oDoc.GetRoot ().GetObj (" crs" ));
2488
+ const CPLJSONObject oTopX (
2489
+ oDoc.GetRoot ().GetObj (" tile_origin_upper_left_x" ));
2490
+ const CPLJSONObject oTopY (
2491
+ oDoc.GetRoot ().GetObj (" tile_origin_upper_left_y" ));
2492
+ const CPLJSONObject oTileDim0 (
2493
+ oDoc.GetRoot ().GetObj (" tile_dimension_zoom_0" ));
2494
+ nTileMatrixWidth0 = 1 ;
2495
+ nTileMatrixHeight0 = 1 ;
2461
2496
if (oCrs.IsValid () && oTopX.IsValid () && oTopY.IsValid () &&
2462
2497
oTileDim0.IsValid ())
2463
2498
{
2464
2499
poSRS->SetFromUserInput (oCrs.ToString ().c_str ());
2465
2500
dfTopX = oTopX.ToDouble ();
2466
2501
dfTopY = oTopY.ToDouble ();
2467
2502
dfTileDim0 = oTileDim0.ToDouble ();
2503
+ const CPLJSONObject oTMWidth0 (
2504
+ oDoc.GetRoot ().GetObj (" tile_matrix_width_zoom_0" ));
2505
+ if (oTMWidth0.GetType () == CPLJSONObject::Type::Integer)
2506
+ nTileMatrixWidth0 = std::max (1 , oTMWidth0.ToInteger ());
2507
+
2508
+ const CPLJSONObject oTMHeight0 (
2509
+ oDoc.GetRoot ().GetObj (" tile_matrix_height_zoom_0" ));
2510
+ if (oTMHeight0.GetType () == CPLJSONObject::Type::Integer)
2511
+ nTileMatrixHeight0 = std::max (1 , oTMHeight0.ToInteger ());
2512
+
2513
+ // Assumes WorldCRS84Quad with 2 tiles in width
2514
+ // cf https://github.com/OSGeo/gdal/issues/11749
2515
+ if (!oTMWidth0.IsValid () && dfTopX == -180 && dfTileDim0 == 180 )
2516
+ nTileMatrixWidth0 = 2 ;
2468
2517
}
2469
2518
2470
2519
oVectorLayers.Deinit ();
@@ -2803,7 +2852,8 @@ GDALDataset *OGRMVTDataset::OpenDirectory(GDALOpenInfo *poOpenInfo)
2803
2852
if (!LoadMetadata (osMetadataFile, osMetadataContent, oVectorLayers,
2804
2853
oTileStatLayers, oBounds, poDS->m_poSRS ,
2805
2854
poDS->m_dfTopXOrigin , poDS->m_dfTopYOrigin ,
2806
- poDS->m_dfTileDim0 , osMetadataMemFilename))
2855
+ poDS->m_dfTileDim0 , poDS->m_nTileMatrixWidth0 ,
2856
+ poDS->m_nTileMatrixHeight0 , osMetadataMemFilename))
2807
2857
{
2808
2858
delete poDS;
2809
2859
return nullptr ;
@@ -3137,7 +3187,8 @@ GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo, bool bRecurseAllowed)
3137
3187
LoadMetadata (osMetadataFile, CPLString (), oVectorLayers,
3138
3188
oTileStatLayers, oBounds, poDS->m_poSRS ,
3139
3189
poDS->m_dfTopXOrigin , poDS->m_dfTopYOrigin ,
3140
- poDS->m_dfTileDim0 , CPLString ());
3190
+ poDS->m_dfTileDim0 , poDS->m_nTileMatrixWidth0 ,
3191
+ poDS->m_nTileMatrixHeight0 , CPLString ());
3141
3192
}
3142
3193
3143
3194
const char *pszGeorefTopX =
@@ -3166,8 +3217,10 @@ GDALDataset *OGRMVTDataset::Open(GDALOpenInfo *poOpenInfo, bool bRecurseAllowed)
3166
3217
int nX = atoi (osX);
3167
3218
int nY = atoi (osY);
3168
3219
int nZ = atoi (osZ);
3169
- if (nZ >= 0 && nZ < 30 && nX >= 0 && nX < (1 << nZ) && nY >= 0 &&
3170
- nY < (1 << nZ))
3220
+ if (nZ >= 0 && nZ < 30 && nX >= 0 &&
3221
+ nX < (static_cast <int64_t >(1 ) << nZ) * poDS->m_nTileMatrixWidth0 &&
3222
+ nY >= 0 &&
3223
+ nY < (static_cast <int64_t >(1 ) << nZ) * poDS->m_nTileMatrixHeight0 )
3171
3224
{
3172
3225
poDS->m_bGeoreferenced = true ;
3173
3226
poDS->m_dfTileDimX = poDS->m_dfTileDim0 / (1 << nZ);
@@ -3336,6 +3389,10 @@ class OGRMVTWriterDataset final : public GDALDataset
3336
3389
double m_dfTopX = 0.0 ;
3337
3390
double m_dfTopY = 0.0 ;
3338
3391
double m_dfTileDim0 = 0.0 ;
3392
+ int m_nTileMatrixWidth0 =
3393
+ 1 ; // Number of tiles along X axis at zoom level 0
3394
+ int m_nTileMatrixHeight0 =
3395
+ 1 ; // Number of tiles along Y axis at zoom level 0
3339
3396
bool m_bReuseTempFile = false ; // debug only
3340
3397
3341
3398
OGRErr PreGenerateForTile (
@@ -5528,6 +5585,10 @@ bool OGRMVTWriterDataset::GenerateMetadata(
5528
5585
oRoot);
5529
5586
WriteMetadataItem (" tile_dimension_zoom_0" , m_dfTileDim0, m_hDBMBTILES,
5530
5587
oRoot);
5588
+ WriteMetadataItem (" tile_matrix_width_zoom_0" , m_nTileMatrixWidth0,
5589
+ m_hDBMBTILES, oRoot);
5590
+ WriteMetadataItem (" tile_matrix_height_zoom_0" , m_nTileMatrixHeight0,
5591
+ m_hDBMBTILES, oRoot);
5531
5592
}
5532
5593
5533
5594
CPLJSONDocument oJsonDoc;
@@ -5830,11 +5891,17 @@ OGRErr OGRMVTWriterDataset::WriteFeature(OGRMVTWriterLayer *poLayer,
5830
5891
const int nTileMaxX =
5831
5892
std::min (static_cast <int >((sExtent .MaxX - m_dfTopX + dfBuffer) /
5832
5893
dfTileDim),
5833
- (1 << nZ) - 1 );
5894
+ static_cast <int >(std::min<int64_t >(
5895
+ INT_MAX, (static_cast <int64_t >(1 ) << nZ) *
5896
+ m_nTileMatrixWidth0 -
5897
+ 1 )));
5834
5898
const int nTileMaxY =
5835
5899
std::min (static_cast <int >((m_dfTopY - sExtent .MinY + dfBuffer) /
5836
5900
dfTileDim),
5837
- (1 << nZ) - 1 );
5901
+ static_cast <int >(std::min<int64_t >(
5902
+ INT_MAX, (static_cast <int64_t >(1 ) << nZ) *
5903
+ m_nTileMatrixHeight0 -
5904
+ 1 )));
5838
5905
for (int iX = nTileMinX; iX <= nTileMaxX; iX++)
5839
5906
{
5840
5907
for (int iY = nTileMinY; iY <= nTileMaxY; iY++)
@@ -6181,20 +6248,32 @@ GDALDataset *OGRMVTWriterDataset::Create(const char *pszFilename, int nXSize,
6181
6248
return nullptr ;
6182
6249
}
6183
6250
6184
- CPLStringList aoList (CSLTokenizeString2 (pszTilingScheme, " ," , 0 ));
6185
- if (aoList.Count () = = 4 )
6251
+ const CPLStringList aoList (CSLTokenizeString2 (pszTilingScheme, " ," , 0 ));
6252
+ if (aoList.Count () > = 4 )
6186
6253
{
6187
6254
poDS->m_poSRS ->SetFromUserInput (aoList[0 ]);
6188
6255
poDS->m_dfTopX = CPLAtof (aoList[1 ]);
6189
6256
poDS->m_dfTopY = CPLAtof (aoList[2 ]);
6190
6257
poDS->m_dfTileDim0 = CPLAtof (aoList[3 ]);
6258
+ if (aoList.Count () == 6 )
6259
+ {
6260
+ poDS->m_nTileMatrixWidth0 = std::max (1 , atoi (aoList[4 ]));
6261
+ poDS->m_nTileMatrixHeight0 = std::max (1 , atoi (aoList[5 ]));
6262
+ }
6263
+ else if (poDS->m_dfTopX == -180 && poDS->m_dfTileDim0 == 180 )
6264
+ {
6265
+ // Assumes WorldCRS84Quad with 2 tiles in width
6266
+ // cf https://github.com/OSGeo/gdal/issues/11749
6267
+ poDS->m_nTileMatrixWidth0 = 2 ;
6268
+ }
6191
6269
}
6192
6270
else
6193
6271
{
6194
6272
CPLError (CE_Failure, CPLE_AppDefined,
6195
6273
" Wrong format for TILING_SCHEME. "
6196
6274
" Expecting EPSG:XXXX,tile_origin_upper_left_x,"
6197
- " tile_origin_upper_left_y,tile_dimension_zoom_0" );
6275
+ " tile_origin_upper_left_y,tile_dimension_zoom_0[,tile_"
6276
+ " matrix_width_zoom_0,tile_matrix_height_zoom_0]" );
6198
6277
delete poDS;
6199
6278
return nullptr ;
6200
6279
}
@@ -6347,7 +6426,8 @@ void RegisterOGRMVT()
6347
6426
" <Option name='TILING_SCHEME' type='string' "
6348
6427
" description='Custom tiling scheme with following format "
6349
6428
" \" EPSG:XXXX,tile_origin_upper_left_x,tile_origin_upper_left_y,"
6350
- " tile_dimension_zoom_0\" '/>"
6429
+ " tile_dimension_zoom_0[,tile_matrix_width_zoom_0,tile_matrix_height_"
6430
+ " zoom_0]\" '/>"
6351
6431
" </CreationOptionList>" );
6352
6432
#endif // HAVE_MVT_WRITE_SUPPORT
6353
6433
0 commit comments