@@ -105,13 +105,44 @@ namespace QgsWmts
105
105
double scaleDenominator = 0.0 ;
106
106
int colRes = ( tmi.extent .xMaximum () - tmi.extent .xMinimum () ) / tileWidth;
107
107
int rowRes = ( tmi.extent .yMaximum () - tmi.extent .yMinimum () ) / tileHeight;
108
- if ( colRes < rowRes )
109
- scaleDenominator = colRes * INCHES_PER_UNIT[ tmi.unit ] * METERS_PER_INCH / 0.00028 ;
108
+ if ( colRes > rowRes )
109
+ scaleDenominator = std::ceil ( colRes * INCHES_PER_UNIT[ tmi.unit ] * METERS_PER_INCH / 0.00028 ) ;
110
110
else
111
- scaleDenominator = rowRes * INCHES_PER_UNIT[ tmi.unit ] * METERS_PER_INCH / 0.00028 ;
111
+ scaleDenominator = std::ceil ( rowRes * INCHES_PER_UNIT[ tmi.unit ] * METERS_PER_INCH / 0.00028 );
112
+
113
+ // Update extent to get a square one
114
+ QgsRectangle extent = tmi.extent ;
115
+ double res = 0.00028 * scaleDenominator / METERS_PER_INCH / INCHES_PER_UNIT[ tmi.unit ];
116
+ int col = std::ceil ( ( extent.xMaximum () - extent.xMinimum () ) / ( tileWidth * res ) );
117
+ int row = std::ceil ( ( extent.yMaximum () - extent.yMinimum () ) / ( tileHeight * res ) );
118
+ if ( col > 1 || row > 1 )
119
+ {
120
+ // Update scale
121
+ if ( col > row )
122
+ {
123
+ res = col * res;
124
+ scaleDenominator = col * scaleDenominator;
125
+ }
126
+ else
127
+ {
128
+ res = row * res;
129
+ scaleDenominator = row * scaleDenominator;
130
+ }
131
+ // set col and row to 1 for the square
132
+ col = 1 ;
133
+ row = 1 ;
134
+ }
135
+ // Calculate extent
136
+ double left = ( extent.xMinimum () + ( extent.xMaximum () - extent.xMinimum () ) / 2.0 ) - ( col / 2.0 ) * ( tileWidth * res );
137
+ double bottom = ( extent.yMinimum () + ( extent.yMaximum () - extent.yMinimum () ) / 2.0 ) - ( row / 2.0 ) * ( tileHeight * res );
138
+ double right = ( extent.xMinimum () + ( extent.xMaximum () - extent.xMinimum () ) / 2.0 ) + ( col / 2.0 ) * ( tileWidth * res );
139
+ double top = ( extent.yMinimum () + ( extent.yMaximum () - extent.yMinimum () ) / 2.0 ) + ( row / 2.0 ) * ( tileHeight * res );
140
+ tmi.extent = QgsRectangle ( left, bottom, right, top );
141
+
112
142
tmi.scaleDenominator = scaleDenominator;
113
143
114
144
tileMatrixInfoMap[crsStr] = tmi;
145
+
115
146
return tmi;
116
147
}
117
148
@@ -126,8 +157,8 @@ namespace QgsWmts
126
157
{
127
158
double scale = scaleDenominator;
128
159
double res = 0.00028 * scale / METERS_PER_INCH / INCHES_PER_UNIT[ unit ];
129
- int col = std::round ( ( extent.xMaximum () - extent.xMinimum () ) / ( tileWidth * res ) );
130
- int row = std::round ( ( extent.yMaximum () - extent.yMinimum () ) / ( tileHeight * res ) );
160
+ int col = std::ceil ( ( extent.xMaximum () - extent.xMinimum () ) / ( tileWidth * res ) );
161
+ int row = std::ceil ( ( extent.yMaximum () - extent.yMinimum () ) / ( tileHeight * res ) );
131
162
double left = ( extent.xMinimum () + ( extent.xMaximum () - extent.xMinimum () ) / 2.0 ) - ( col / 2.0 ) * ( tileWidth * res );
132
163
double top = ( extent.yMinimum () + ( extent.yMaximum () - extent.yMinimum () ) / 2.0 ) + ( row / 2.0 ) * ( tileHeight * res );
133
164
@@ -211,6 +242,254 @@ namespace QgsWmts
211
242
return tmsList;
212
243
}
213
244
245
+ QList< layerDef > getWmtsLayerList ( QgsServerInterface *serverIface, const QgsProject *project )
246
+ {
247
+ QList< layerDef > wmtsLayers;
248
+ #ifdef HAVE_SERVER_PYTHON_PLUGINS
249
+ QgsAccessControl *accessControl = serverIface->accessControls ();
250
+ #endif
251
+ QgsCoordinateReferenceSystem wgs84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs ( GEO_EPSG_CRS_AUTHID );
252
+
253
+ QStringList nonIdentifiableLayers = project->nonIdentifiableLayers ();
254
+
255
+ // WMTS Project configuration
256
+ bool wmtsProject = project->readBoolEntry ( QStringLiteral ( " WMTSLayers" ), QStringLiteral ( " Project" ) );
257
+
258
+ // Root Layer name
259
+ QString rootLayerName = QgsServerProjectUtils::wmsRootName ( *project );
260
+ if ( rootLayerName.isEmpty () && !project->title ().isEmpty () )
261
+ {
262
+ rootLayerName = project->title ();
263
+ }
264
+
265
+ if ( wmtsProject && !rootLayerName.isEmpty () )
266
+ {
267
+ layerDef pLayer;
268
+ pLayer.id = rootLayerName;
269
+
270
+ if ( !project->title ().isEmpty () )
271
+ {
272
+ pLayer.title = project->title ();
273
+ pLayer.abstract = project->title ();
274
+ }
275
+
276
+ // transform the project native CRS into WGS84
277
+ QgsRectangle projRect = QgsServerProjectUtils::wmsExtent ( *project );
278
+ QgsCoordinateReferenceSystem projCrs = project->crs ();
279
+ QgsCoordinateTransform exGeoTransform ( projCrs, wgs84, project );
280
+ try
281
+ {
282
+ pLayer.wgs84BoundingRect = exGeoTransform.transformBoundingBox ( projRect );
283
+ }
284
+ catch ( const QgsCsException & )
285
+ {
286
+ pLayer.wgs84BoundingRect = QgsRectangle ( -180 , -90 , 180 , 90 );
287
+ }
288
+
289
+ // Formats
290
+ bool wmtsPngProject = project->readBoolEntry ( QStringLiteral ( " WMTSPngLayers" ), QStringLiteral ( " Project" ) );
291
+ if ( wmtsPngProject )
292
+ pLayer.formats << QStringLiteral ( " image/png" );
293
+ bool wmtsJpegProject = project->readBoolEntry ( QStringLiteral ( " WMTSJpegLayers" ), QStringLiteral ( " Project" ) );
294
+ if ( wmtsJpegProject )
295
+ pLayer.formats << QStringLiteral ( " image/jpeg" );
296
+
297
+ // Project is not queryable in WMS
298
+ // pLayer.queryable = ( nonIdentifiableLayers.count() != project->count() );
299
+ pLayer.queryable = false ;
300
+
301
+ wmtsLayers.append ( pLayer );
302
+ }
303
+
304
+ QStringList wmtsGroupNameList = project->readListEntry ( QStringLiteral ( " WMTSLayers" ), QStringLiteral ( " Group" ) );
305
+ if ( !wmtsGroupNameList.isEmpty () )
306
+ {
307
+ QgsLayerTreeGroup *treeRoot = project->layerTreeRoot ();
308
+
309
+ QStringList wmtsPngGroupNameList = project->readListEntry ( QStringLiteral ( " WMTSPngLayers" ), QStringLiteral ( " Group" ) );
310
+ QStringList wmtsJpegGroupNameList = project->readListEntry ( QStringLiteral ( " WMTSJpegLayers" ), QStringLiteral ( " Group" ) );
311
+
312
+ for ( const QString gName : wmtsGroupNameList )
313
+ {
314
+ QgsLayerTreeGroup *treeGroup = treeRoot->findGroup ( gName );
315
+ if ( !treeGroup )
316
+ {
317
+ continue ;
318
+ }
319
+
320
+ layerDef pLayer;
321
+ pLayer.id = treeGroup->customProperty ( QStringLiteral ( " wmsShortName" ) ).toString ();
322
+ if ( pLayer.id .isEmpty () )
323
+ pLayer.id = gName ;
324
+
325
+ pLayer.title = treeGroup->customProperty ( QStringLiteral ( " wmsTitle" ) ).toString ();
326
+ if ( pLayer.title .isEmpty () )
327
+ pLayer.title = gName ;
328
+
329
+ pLayer.abstract = treeGroup->customProperty ( QStringLiteral ( " wmsAbstract" ) ).toString ();
330
+
331
+ QgsRectangle wgs84BoundingRect;
332
+ bool queryable = false ;
333
+ double maxScale = 0.0 ;
334
+ double minScale = 0.0 ;
335
+ for ( QgsLayerTreeLayer *layer : treeGroup->findLayers () )
336
+ {
337
+ QgsMapLayer *l = layer->layer ();
338
+ if ( !l )
339
+ {
340
+ continue ;
341
+ }
342
+ // transform the layer native CRS into WGS84
343
+ QgsCoordinateReferenceSystem layerCrs = l->crs ();
344
+ QgsCoordinateTransform exGeoTransform ( layerCrs, wgs84, project );
345
+ try
346
+ {
347
+ wgs84BoundingRect.combineExtentWith ( exGeoTransform.transformBoundingBox ( l->extent () ) );
348
+ }
349
+ catch ( const QgsCsException & )
350
+ {
351
+ wgs84BoundingRect.combineExtentWith ( QgsRectangle ( -180 , -90 , 180 , 90 ) );
352
+ }
353
+ if ( !queryable && !nonIdentifiableLayers.contains ( l->id () ) )
354
+ {
355
+ queryable = true ;
356
+ }
357
+
358
+ double lMaxScale = l->maximumScale ();
359
+ if ( lMaxScale > 0.0 && lMaxScale > maxScale )
360
+ {
361
+ maxScale = lMaxScale;
362
+ }
363
+ double lMinScale = l->minimumScale ();
364
+ if ( lMinScale > 0.0 && ( minScale == 0.0 || lMinScale < minScale ) )
365
+ {
366
+ minScale = lMinScale;
367
+ }
368
+ }
369
+ pLayer.wgs84BoundingRect = wgs84BoundingRect;
370
+ pLayer.queryable = queryable;
371
+ pLayer.maxScale = maxScale;
372
+ pLayer.minScale = minScale;
373
+
374
+ // Formats
375
+ if ( wmtsPngGroupNameList.contains ( gName ) )
376
+ pLayer.formats << QStringLiteral ( " image/png" );
377
+ if ( wmtsJpegGroupNameList.contains ( gName ) )
378
+ pLayer.formats << QStringLiteral ( " image/jpeg" );
379
+
380
+ wmtsLayers.append ( pLayer );
381
+ }
382
+ }
383
+
384
+ QStringList wmtsLayerIdList = project->readListEntry ( QStringLiteral ( " WMTSLayers" ), QStringLiteral ( " Layer" ) );
385
+ QStringList wmtsPngLayerIdList = project->readListEntry ( QStringLiteral ( " WMTSPngLayers" ), QStringLiteral ( " Layer" ) );
386
+ QStringList wmtsJpegLayerIdList = project->readListEntry ( QStringLiteral ( " WMTSJpegLayers" ), QStringLiteral ( " Layer" ) );
387
+
388
+ for ( const QString lId : wmtsLayerIdList )
389
+ {
390
+ QgsMapLayer *l = project->mapLayer ( lId );
391
+ if ( !l )
392
+ {
393
+ continue ;
394
+ }
395
+ #ifdef HAVE_SERVER_PYTHON_PLUGINS
396
+ if ( !accessControl->layerReadPermission ( l ) )
397
+ {
398
+ continue ;
399
+ }
400
+ #endif
401
+
402
+ layerDef pLayer;
403
+ pLayer.id = l->name ();
404
+ if ( !l->shortName ().isEmpty () )
405
+ pLayer.id = l->shortName ();
406
+ pLayer.id = pLayer.id .replace ( ' ' , ' _' );
407
+
408
+ pLayer.title = l->title ();
409
+ pLayer.abstract = l->abstract ();
410
+
411
+ // transform the layer native CRS into WGS84
412
+ QgsCoordinateReferenceSystem layerCrs = l->crs ();
413
+ QgsCoordinateTransform exGeoTransform ( layerCrs, wgs84, project );
414
+ try
415
+ {
416
+ pLayer.wgs84BoundingRect = exGeoTransform.transformBoundingBox ( l->extent () );
417
+ }
418
+ catch ( const QgsCsException & )
419
+ {
420
+ pLayer.wgs84BoundingRect = QgsRectangle ( -180 , -90 , 180 , 90 );
421
+ }
422
+
423
+ // Formats
424
+ if ( wmtsPngLayerIdList.contains ( lId ) )
425
+ pLayer.formats << QStringLiteral ( " image/png" );
426
+ if ( wmtsJpegLayerIdList.contains ( lId ) )
427
+ pLayer.formats << QStringLiteral ( " image/jpeg" );
428
+
429
+ pLayer.queryable = ( !nonIdentifiableLayers.contains ( l->id () ) );
430
+
431
+ pLayer.maxScale = l->maximumScale ();
432
+ pLayer.minScale = l->minimumScale ();
433
+
434
+ wmtsLayers.append ( pLayer );
435
+ }
436
+ return wmtsLayers;
437
+ }
438
+
439
+ tileMatrixSetLinkDef getLayerTileMatrixSetLink ( const layerDef layer, const tileMatrixSetDef tms, const QgsProject *project )
440
+ {
441
+ tileMatrixSetLinkDef tmsl;
442
+
443
+ QMap< int , tileMatrixLimitDef > tileMatrixLimits;
444
+
445
+ QgsRectangle rect ( layer.wgs84BoundingRect );
446
+ if ( tms.ref != QLatin1String ( " EPSG:4326" ) )
447
+ {
448
+ QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs ( tms.ref );
449
+ QgsCoordinateReferenceSystem wgs84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs ( GEO_EPSG_CRS_AUTHID );
450
+ QgsCoordinateTransform exGeoTransform ( wgs84, crs, project );
451
+ try
452
+ {
453
+ rect = exGeoTransform.transformBoundingBox ( layer.wgs84BoundingRect );
454
+ }
455
+ catch ( const QgsCsException & )
456
+ {
457
+ return tmsl;
458
+ }
459
+ }
460
+ tmsl.ref = tms.ref ;
461
+
462
+ rect = rect.intersect ( tms.extent );
463
+
464
+ int tmIdx = -1 ;
465
+ for ( const tileMatrixDef tm : tms.tileMatrixList )
466
+ {
467
+ ++tmIdx;
468
+
469
+ if ( layer.maxScale > 0.0 && tm.scaleDenominator > layer.maxScale )
470
+ {
471
+ continue ;
472
+ }
473
+ if ( layer.minScale > 0.0 && tm.scaleDenominator < layer.minScale )
474
+ {
475
+ continue ;
476
+ }
477
+
478
+ double res = tm.resolution ;
479
+
480
+ tileMatrixLimitDef tml;
481
+ tml.minCol = std::floor ( ( rect.xMinimum () - tm.left ) / ( tileWidth * res ) );
482
+ tml.maxCol = std::ceil ( ( rect.xMaximum () - tm.left ) / ( tileWidth * res ) ) - 1 ;
483
+ tml.minRow = std::floor ( ( tm.top - rect.yMaximum () ) / ( tileHeight * res ) );
484
+ tml.maxRow = std::ceil ( ( tm.top - rect.yMinimum () ) / ( tileHeight * res ) ) - 1 ;
485
+
486
+ tileMatrixLimits[tmIdx] = tml;
487
+ }
488
+
489
+ tmsl.tileMatrixLimits = tileMatrixLimits;
490
+ return tmsl;
491
+ }
492
+
214
493
QUrlQuery translateWmtsParamToWmsQueryItem ( const QString &request, const QgsWmtsParameters ¶ms,
215
494
const QgsProject *project, QgsServerInterface *serverIface )
216
495
{
0 commit comments