Skip to content

Commit

Permalink
[Server][Feature][needs-docs] Update WMTS service: Add GetFeatureInfo
Browse files Browse the repository at this point in the history
Support GetFeatureInfo Request in WMTS.
  • Loading branch information
rldhont committed Aug 20, 2018
1 parent dc7e8e4 commit cff8469
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 181 deletions.
1 change: 1 addition & 0 deletions src/server/services/wmts/CMakeLists.txt
Expand Up @@ -7,6 +7,7 @@ SET (wmts_SRCS
qgswmtsutils.cpp
qgswmtsgetcapabilities.cpp
qgswmtsgettile.cpp
qgswmtsgetfeatureinfo.cpp
)

########################################################
Expand Down
5 changes: 5 additions & 0 deletions src/server/services/wmts/qgswmts.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgswmtsutils.h"
#include "qgswmtsgetcapabilities.h"
#include "qgswmtsgettile.h"
#include "qgswmtsgetfeatureinfo.h"

#define QSTR_COMPARE( str, lit )\
(str.compare( QStringLiteral( lit ), Qt::CaseInsensitive ) == 0)
Expand Down Expand Up @@ -82,6 +83,10 @@ namespace QgsWmts
{
writeGetTile( mServerIface, project, versionString, request, response );
}
else if ( QSTR_COMPARE( req, "GetFeatureInfo" ) )
{
writeGetFeatureInfo( mServerIface, project, versionString, request, response );
}
else
{
// Operation not supported
Expand Down
38 changes: 35 additions & 3 deletions src/server/services/wmts/qgswmtsgetcapabilities.cpp
Expand Up @@ -302,9 +302,9 @@ namespace QgsWmts
operationsMetadataElement.appendChild( getTileElement );

//ows:Operation element with name GetFeatureInfo
/*QDomElement getFeatureInfoElement = getCapabilitiesElement.cloneNode().toElement();//this is the same as 'GetCapabilities'
QDomElement getFeatureInfoElement = getCapabilitiesElement.cloneNode().toElement();//this is the same as 'GetCapabilities'
getFeatureInfoElement.setAttribute( QStringLiteral( "name" ), QStringLiteral( "GetFeatureInfo" ) );
operationsMetadataElement.appendChild( getFeatureInfoElement );*/
operationsMetadataElement.appendChild( getFeatureInfoElement );

// End
return operationsMetadataElement;
Expand All @@ -327,6 +327,8 @@ namespace QgsWmts
QgsCoordinateReferenceSystem wgs84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs( GEO_EPSG_CRS_AUTHID );
QList<tileMatrixSet>::iterator tmsIt = tmsList.begin();

QStringList nonIdentifiableLayers = project->nonIdentifiableLayers();

// WMTS Project configuration
bool wmtsProject = project->readBoolEntry( QStringLiteral( "WMTSLayers" ), QStringLiteral( "Project" ) );

Expand Down Expand Up @@ -371,6 +373,10 @@ namespace QgsWmts
if ( wmtsJpegProject )
pLayer.formats << QStringLiteral( "image/jpeg" );

// Project is not queryable in WMS
//pLayer.queryable = ( nonIdentifiableLayers.count() != project->count() );
pLayer.queryable = false;

wmtsLayers.append( pLayer );
}

Expand Down Expand Up @@ -401,11 +407,12 @@ namespace QgsWmts

pLayer.abstract = treeGroup->customProperty( QStringLiteral( "wmsAbstract" ) ).toString();

QgsRectangle wgs84BoundingRect;
bool queryable = false;
for ( QgsLayerTreeLayer *layer : treeGroup->findLayers() )
{
QgsMapLayer *l = layer->layer();
//transform the layer native CRS into WGS84
QgsRectangle wgs84BoundingRect;
QgsCoordinateReferenceSystem layerCrs = l->crs();
Q_NOWARN_DEPRECATED_PUSH
QgsCoordinateTransform exGeoTransform( layerCrs, wgs84 );
Expand All @@ -418,7 +425,13 @@ namespace QgsWmts
{
wgs84BoundingRect.combineExtentWith( QgsRectangle( -180, -90, 180, 90 ) );
}
if ( !queryable && !nonIdentifiableLayers.contains( l->id() ) )
{
queryable = true;
}
}
pLayer.wgs84BoundingRect = wgs84BoundingRect;
pLayer.queryable = queryable;

// Formats
if ( wmtsPngGroupNameList.contains( gName ) )
Expand Down Expand Up @@ -477,9 +490,19 @@ namespace QgsWmts
if ( wmtsJpegLayerIdList.contains( lId ) )
pLayer.formats << QStringLiteral( "image/jpeg" );

pLayer.queryable = ( !nonIdentifiableLayers.contains( l->id() ) );

wmtsLayers.append( pLayer );
}

// Append InfoFormat helper
std::function < void ( QDomElement &, const QString & ) > appendInfoFormat = [&doc]( QDomElement & elem, const QString & format )
{
QDomElement formatElem = doc.createElement( QStringLiteral( "InfoFormat" )/*wmts:InfoFormat*/ );
formatElem.appendChild( doc.createTextNode( format ) );
elem.appendChild( formatElem );
};

Q_FOREACH ( layerDef wmtsLayer, wmtsLayers )
{
if ( wmtsLayer.id.isEmpty() )
Expand Down Expand Up @@ -542,6 +565,15 @@ namespace QgsWmts
layerElem.appendChild( layerFormatElem );
}

if ( wmtsLayer.queryable )
{
appendInfoFormat( layerElem, QStringLiteral( "text/plain" ) );
appendInfoFormat( layerElem, QStringLiteral( "text/html" ) );
appendInfoFormat( layerElem, QStringLiteral( "text/xml" ) );
appendInfoFormat( layerElem, QStringLiteral( "application/vnd.ogc.gml" ) );
appendInfoFormat( layerElem, QStringLiteral( "application/vnd.ogc.gml/3.1.1" ) );
}

tmsIt = tmsList.begin();
for ( ; tmsIt != tmsList.end(); ++tmsIt )
{
Expand Down
52 changes: 52 additions & 0 deletions src/server/services/wmts/qgswmtsgetfeatureinfo.cpp
@@ -0,0 +1,52 @@
/***************************************************************************
qgswmsgetfeatureinfo.cpp
-------------------------
begin : July 23 , 2017
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgswmtsutils.h"
#include "qgswmtsgetfeatureinfo.h"

#include <QImage>

namespace QgsWmts
{

void writeGetFeatureInfo( QgsServerInterface *serverIface, const QgsProject *project,
const QString &version, const QgsServerRequest &request,
QgsServerResponse &response )
{
Q_UNUSED( version );

QgsServerRequest::Parameters params = request.parameters();

// WMS query
QUrlQuery query = translateWmtsParamToWmsQueryItem( QStringLiteral( "GetFeatureInfo" ), params, project, serverIface );

// GetFeatureInfo query items
query.addQueryItem( QStringLiteral( "query_layers" ), query.queryItemValue( QStringLiteral( "layers" ) ) );
query.addQueryItem( QStringLiteral( "i" ), params.value( QStringLiteral( "I" ) ) );
query.addQueryItem( QStringLiteral( "j" ), params.value( QStringLiteral( "J" ) ) );
query.addQueryItem( QStringLiteral( "info_format" ), params.value( QStringLiteral( "INFOFORMAT" ) ) );

QgsServerParameters wmsParams( query );
QgsServerRequest wmsRequest( "?" + query.query( QUrl::FullyDecoded ) );
QgsService *service = serverIface->serviceRegistry()->getService( wmsParams.service(), wmsParams.version() );
service->executeRequest( wmsRequest, response, project );
}

} // namespace QgsWmts




28 changes: 28 additions & 0 deletions src/server/services/wmts/qgswmtsgetfeatureinfo.h
@@ -0,0 +1,28 @@
/***************************************************************************
qgswmsgetfeatureinfo.h
-------------------------
begin : July 23 , 2017
copyright : (C) 2018 by René-Luc D'Hont
email : rldhont at 3liz dot com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

namespace QgsWmts
{

/**
* Output GetFeatureInfo response
*/
void writeGetFeatureInfo( QgsServerInterface *serverIface, const QgsProject *project,
const QString &version, const QgsServerRequest &request,
QgsServerResponse &response );

} // namespace QgsWmts
180 changes: 2 additions & 178 deletions src/server/services/wmts/qgswmtsgettile.cpp
Expand Up @@ -30,184 +30,8 @@ namespace QgsWmts

QgsServerRequest::Parameters params = request.parameters();

//defining Layer
QString layer;
//read Layer
QMap<QString, QString>::const_iterator layer_it = params.constFind( QStringLiteral( "LAYER" ) );
if ( layer_it != params.constEnd() )
{
layer = layer_it.value();
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "Layer is mandatory" ) );
}

//defining Format
QString format;
//read Format
QMap<QString, QString>::const_iterator format_it = params.constFind( QStringLiteral( "FORMAT" ) );
if ( format_it != params.constEnd() )
{
format = format_it.value();
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "Format is mandatory" ) );
}

QList< tileMatrixSet > tmsList = getTileMatrixSetList( project );
if ( tmsList.isEmpty() )
{
throw QgsServiceException( QStringLiteral( "UnknownError" ),
QStringLiteral( "Service not well configured" ) );
}

//defining TileMatrixSet ref
QString tms_ref;
//read TileMatrixSet
QMap<QString, QString>::const_iterator tms_ref_it = params.constFind( QStringLiteral( "TILEMATRIXSET" ) );
if ( tms_ref_it != params.constEnd() )
{
tms_ref = tms_ref_it.value();
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrixSet is mandatory" ) );
}

bool tms_ref_valid = false;
tileMatrixSet tms;
QList<tileMatrixSet>::iterator tmsIt = tmsList.begin();
for ( ; tmsIt != tmsList.end(); ++tmsIt )
{
tileMatrixSet &tmsi = *tmsIt;
if ( tmsi.ref == tms_ref )
{
tms_ref_valid = true;
tms = tmsi;
break;
}
}
if ( !tms_ref_valid )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrixSet is unknown" ) );
}

bool conversionSuccess = false;

//difining TileMatrix idx
int tm_idx;
//read TileMatrix
QMap<QString, QString>::const_iterator tm_ref_it = params.constFind( QStringLiteral( "TILEMATRIX" ) );
if ( tm_ref_it != params.constEnd() )
{
QString tm_ref = tm_ref_it.value();
tm_idx = tm_ref.toInt( &conversionSuccess );
if ( !conversionSuccess )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrix is unknown" ) );
}
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrix is mandatory" ) );
}
if ( tms.tileMatrixList.count() < tm_idx )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrix is unknown" ) );
}
tileMatrix tm = tms.tileMatrixList.at( tm_idx );

//defining TileRow
int tr;
//read TileRow
QMap<QString, QString>::const_iterator tr_it = params.constFind( QStringLiteral( "TILEROW" ) );
if ( tr_it != params.constEnd() )
{
QString tr_str = tr_it.value();
conversionSuccess = false;
tr = tr_str.toInt( &conversionSuccess );
if ( !conversionSuccess )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileRow is unknown" ) );
}
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileRow is mandatory" ) );
}
if ( tm.row <= tr )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileRow is unknown" ) );
}

//defining TileCol
int tc;
//read TileCol
QMap<QString, QString>::const_iterator tc_it = params.constFind( QStringLiteral( "TILECOL" ) );
if ( tc_it != params.constEnd() )
{
QString tc_str = tc_it.value();
conversionSuccess = false;
tc = tc_str.toInt( &conversionSuccess );
if ( !conversionSuccess )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileCol is unknown" ) );
}
}
else
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileCol is mandatory" ) );
}
if ( tm.col <= tc )
{
throw QgsRequestNotWellFormedException( QStringLiteral( "TileCol is unknown" ) );
}

int tileWidth = 256;
int tileHeight = 256;
double res = tm.resolution;
double minx = tm.left + tc * ( tileWidth * res );
double miny = tm.top - ( tr + 1 ) * ( tileHeight * res );
double maxx = tm.left + ( tc + 1 ) * ( tileWidth * res );
double maxy = tm.top - tr * ( tileHeight * res );
QString bbox;
if ( tms.ref == "EPSG:4326" )
{
bbox = qgsDoubleToString( miny, 6 ) + ',' +
qgsDoubleToString( minx, 6 ) + ',' +
qgsDoubleToString( maxy, 6 ) + ',' +
qgsDoubleToString( maxx, 6 );
}
else
{
bbox = qgsDoubleToString( minx, 6 ) + ',' +
qgsDoubleToString( miny, 6 ) + ',' +
qgsDoubleToString( maxx, 6 ) + ',' +
qgsDoubleToString( maxy, 6 );
}

QUrlQuery query;
if ( !params.value( QStringLiteral( "MAP" ) ).isEmpty() )
{
query.addQueryItem( QStringLiteral( "map" ), params.value( QStringLiteral( "MAP" ) ) );
}
query.addQueryItem( QStringLiteral( "service" ), QStringLiteral( "WMS" ) );
query.addQueryItem( QStringLiteral( "version" ), QStringLiteral( "1.3.0" ) );
query.addQueryItem( QStringLiteral( "request" ), QStringLiteral( "GetMap" ) );
query.addQueryItem( QStringLiteral( "layers" ), layer );
query.addQueryItem( QStringLiteral( "styles" ), QString() );
query.addQueryItem( QStringLiteral( "crs" ), tms.ref );
query.addQueryItem( QStringLiteral( "bbox" ), bbox );
query.addQueryItem( QStringLiteral( "width" ), QStringLiteral( "256" ) );
query.addQueryItem( QStringLiteral( "height" ), QStringLiteral( "256" ) );
query.addQueryItem( QStringLiteral( "format" ), format );
if ( format.startsWith( QStringLiteral( "image/png" ) ) )
{
query.addQueryItem( QStringLiteral( "transparent" ), QStringLiteral( "true" ) );
}
query.addQueryItem( QStringLiteral( "dpi" ), QStringLiteral( "96" ) );
// WMS query
QUrlQuery query = translateWmtsParamToWmsQueryItem( QStringLiteral( "GetMap" ), params, project );

QgsServerParameters wmsParams( query );
QgsServerRequest wmsRequest( "?" + query.query( QUrl::FullyDecoded ) );
Expand Down

0 comments on commit cff8469

Please sign in to comment.