QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgswcsutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgswcsutils.cpp
3 -------------------------
4 begin : December 9, 2013
5 copyright : (C) 2013 by René-Luc D'Hont
6 email : rldhont at 3liz dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgswcsutils.h"
19#include "qgsconfigcache.h"
22#include "qgsproject.h"
23#include "qgsexception.h"
24#include "qgsrasterlayer.h"
27
28namespace QgsWcs
29{
31 {
32 return QStringLiteral( "1.0.0" );
33 }
34
35 QDomElement getCoverageOffering( QDomDocument &doc, const QgsRasterLayer *layer, const QgsProject *project, bool brief )
36 {
37 QDomElement layerElem;
38 if ( brief )
39 layerElem = doc.createElement( QStringLiteral( "CoverageOfferingBrief" ) );
40 else
41 layerElem = doc.createElement( QStringLiteral( "CoverageOffering" ) );
42
43 // create name
44 QDomElement nameElem = doc.createElement( QStringLiteral( "name" ) );
45 QString name = layer->name();
46 if ( !layer->shortName().isEmpty() )
47 name = layer->shortName();
48 name = name.replace( ' ', '_' );
49 const QDomText nameText = doc.createTextNode( name );
50 nameElem.appendChild( nameText );
51 layerElem.appendChild( nameElem );
52
53 // create label
54 QDomElement labelElem = doc.createElement( QStringLiteral( "label" ) );
55 QString title = layer->title();
56 if ( title.isEmpty() )
57 {
58 title = layer->name();
59 }
60 const QDomText labelText = doc.createTextNode( title );
61 labelElem.appendChild( labelText );
62 layerElem.appendChild( labelElem );
63
64 //create description
65 const QString abstract = layer->abstract();
66 if ( !abstract.isEmpty() )
67 {
68 QDomElement descriptionElem = doc.createElement( QStringLiteral( "description" ) );
69 const QDomText descriptionText = doc.createTextNode( abstract );
70 descriptionElem.appendChild( descriptionText );
71 layerElem.appendChild( descriptionElem );
72 }
73
74 //lonLatEnvelope
75 const QgsCoordinateReferenceSystem layerCrs = layer->crs();
77 const int wgs84precision = 6;
78 const QgsCoordinateTransform t( layerCrs, wgs84, project );
79 //transform
80 QgsRectangle BBox;
81 try
82 {
83 BBox = t.transformBoundingBox( layer->extent() );
84 }
85 catch ( QgsCsException &e )
86 {
87 QgsDebugMsg( QStringLiteral( "Transform error caught: %1. Using original layer extent." ).arg( e.what() ) );
88 BBox = layer->extent();
89 }
90 QDomElement lonLatElem = doc.createElement( QStringLiteral( "lonLatEnvelope" ) );
91 lonLatElem.setAttribute( QStringLiteral( "srsName" ), QStringLiteral( "urn:ogc:def:crs:OGC:1.3:CRS84" ) );
92 QDomElement lowerPosElem = doc.createElement( QStringLiteral( "gml:pos" ) );
93 const QDomText lowerPosText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( BBox.xMinimum(), wgs84precision ), wgs84precision ) + " " + qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( BBox.yMinimum(), wgs84precision ), wgs84precision ) );
94 lowerPosElem.appendChild( lowerPosText );
95 lonLatElem.appendChild( lowerPosElem );
96 QDomElement upperPosElem = doc.createElement( QStringLiteral( "gml:pos" ) );
97 const QDomText upperPosText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( BBox.xMaximum(), wgs84precision ), wgs84precision ) + " " + qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( BBox.yMaximum(), wgs84precision ), wgs84precision ) );
98 upperPosElem.appendChild( upperPosText );
99 lonLatElem.appendChild( upperPosElem );
100 layerElem.appendChild( lonLatElem );
101
102 if ( brief )
103 return layerElem;
104
105 //Defines the spatial-temporal domain set of a coverage offering. The domainSet shall include a SpatialDomain
106 // (describing the spatial locations for which coverages can be requested), a TemporalDomain (describing the
107 // time instants or inter-vals for which coverages can be requested), or both.
108 QDomElement domainSetElem = doc.createElement( QStringLiteral( "domainSet" ) );
109 layerElem.appendChild( domainSetElem );
110
111 QDomElement spatialDomainElem = doc.createElement( QStringLiteral( "spatialDomain" ) );
112 domainSetElem.appendChild( spatialDomainElem );
113
114 // Define precision
115 int precision = 3;
116 if ( layer->crs().isGeographic() )
117 {
118 precision = 6;
119 }
120 //create Envelope
121 const QgsRectangle layerBBox = layer->extent();
122 QDomElement envelopeElem = doc.createElement( QStringLiteral( "gml:Envelope" ) );
123 envelopeElem.setAttribute( QStringLiteral( "srsName" ), layerCrs.authid() );
124 QDomElement lowerCornerElem = doc.createElement( QStringLiteral( "gml:pos" ) );
125 const QDomText lowerCornerText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( layerBBox.xMinimum(), precision ), wgs84precision ) + " " + qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( layerBBox.yMinimum(), wgs84precision ), precision ) );
126 lowerCornerElem.appendChild( lowerCornerText );
127 envelopeElem.appendChild( lowerCornerElem );
128 QDomElement upperCornerElem = doc.createElement( QStringLiteral( "gml:pos" ) );
129 const QDomText upperCornerText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( layerBBox.xMaximum(), precision ), wgs84precision ) + " " + qgsDoubleToString( QgsServerProjectUtils::ceilWithPrecision( layerBBox.yMaximum(), wgs84precision ), precision ) );
130 upperCornerElem.appendChild( upperCornerText );
131 envelopeElem.appendChild( upperCornerElem );
132 spatialDomainElem.appendChild( envelopeElem );
133
134 QDomElement rectGridElem = doc.createElement( QStringLiteral( "gml:RectifiedGrid" ) );
135 rectGridElem.setAttribute( QStringLiteral( "dimension" ), 2 );
136 QDomElement limitsElem = doc.createElement( QStringLiteral( "gml:limits" ) );
137 rectGridElem.appendChild( limitsElem );
138 QDomElement gridEnvElem = doc.createElement( QStringLiteral( "gml:GridEnvelope" ) );
139 limitsElem.appendChild( gridEnvElem );
140 QDomElement lowElem = doc.createElement( QStringLiteral( "gml:low" ) );
141 const QDomText lowText = doc.createTextNode( QStringLiteral( "0 0" ) );
142 lowElem.appendChild( lowText );
143 gridEnvElem.appendChild( lowElem );
144 QDomElement highElem = doc.createElement( QStringLiteral( "gml:high" ) );
145 const QDomText highText = doc.createTextNode( QString::number( layer->width() ) + " " + QString::number( layer->height() ) );
146 highElem.appendChild( highText );
147 gridEnvElem.appendChild( highElem );
148 spatialDomainElem.appendChild( rectGridElem );
149
150 QDomElement xAxisElem = doc.createElement( QStringLiteral( "gml:axisName" ) );
151 const QDomText xAxisText = doc.createTextNode( QStringLiteral( "x" ) );
152 xAxisElem.appendChild( xAxisText );
153 rectGridElem.appendChild( xAxisElem );
154
155 QDomElement yAxisElem = doc.createElement( QStringLiteral( "gml:axisName" ) );
156 const QDomText yAxisText = doc.createTextNode( QStringLiteral( "y" ) );
157 yAxisElem.appendChild( yAxisText );
158 rectGridElem.appendChild( yAxisElem );
159
160 QDomElement originElem = doc.createElement( QStringLiteral( "gml:origin" ) );
161 QDomElement originPosElem = doc.createElement( QStringLiteral( "gml:pos" ) );
162 originElem.appendChild( originPosElem );
163 const QDomText originPosText = doc.createTextNode( qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( layerBBox.xMinimum(), precision ), precision ) + " " + qgsDoubleToString( QgsServerProjectUtils::floorWithPrecision( layerBBox.yMinimum(), precision ), precision ) );
164 originPosElem.appendChild( originPosText );
165 rectGridElem.appendChild( originElem );
166
167 QDomElement xOffsetElem = doc.createElement( QStringLiteral( "gml:offsetVector" ) );
168 const QDomText xOffsetText = doc.createTextNode( QString::number( layer->rasterUnitsPerPixelX() ) + " 0" );
169 xOffsetElem.appendChild( xOffsetText );
170 rectGridElem.appendChild( xOffsetElem );
171
172 QDomElement yOffsetElem = doc.createElement( QStringLiteral( "gml:offsetVector" ) );
173 const QDomText yOffsetText = doc.createTextNode( "0 " + QString::number( layer->rasterUnitsPerPixelY() ) );
174 yOffsetElem.appendChild( yOffsetText );
175 rectGridElem.appendChild( yOffsetElem );
176
177 //GML property containing one RangeSet GML object.
178 QDomElement rangeSetElem = doc.createElement( QStringLiteral( "rangeSet" ) );
179 layerElem.appendChild( rangeSetElem );
180
181 //Defines the properties (categories, measures, or values) assigned to each location in the domain. Any such
182 // property may be a scalar (numeric or text) value, such as population density, or a compound (vector or tensor)
183 // value, such as incomes by race, or radiances by wavelength. The semantic of the range set is typically an
184 // observable and is referenced by a URI. A rangeSet also has a reference system that is referred by the URI in
185 // the refSys attribute. The refSys is either qualitative (classification) or quantitative (uom). The three attributes
186 // can be included either here and in each axisDescription. If included in both places, the values in the axisDescription
187 // over-ride those included in the RangeSet.
188 QDomElement RangeSetElem = doc.createElement( QStringLiteral( "RangeSet" ) );
189 rangeSetElem.appendChild( RangeSetElem );
190
191 QDomElement rsNameElem = doc.createElement( QStringLiteral( "name" ) );
192 const QDomText rsNameText = doc.createTextNode( QStringLiteral( "Bands" ) );
193 rsNameElem.appendChild( rsNameText );
194 RangeSetElem.appendChild( rsNameElem );
195
196 QDomElement rsLabelElem = doc.createElement( QStringLiteral( "label" ) );
197 const QDomText rsLabelText = doc.createTextNode( QStringLiteral( "Bands" ) );
198 rsLabelElem.appendChild( rsLabelText );
199 RangeSetElem.appendChild( rsLabelElem );
200
201 QDomElement axisDescElem = doc.createElement( QStringLiteral( "axisDescription" ) );
202 RangeSetElem.appendChild( axisDescElem );
203
204 QDomElement AxisDescElem = doc.createElement( QStringLiteral( "AxisDescription" ) );
205 axisDescElem.appendChild( AxisDescElem );
206
207 QDomElement adNameElem = doc.createElement( QStringLiteral( "name" ) );
208 const QDomText adNameText = doc.createTextNode( QStringLiteral( "bands" ) );
209 adNameElem.appendChild( adNameText );
210 AxisDescElem.appendChild( adNameElem );
211
212 QDomElement adLabelElem = doc.createElement( QStringLiteral( "label" ) );
213 const QDomText adLablelText = doc.createTextNode( QStringLiteral( "bands" ) );
214 adLabelElem.appendChild( adLablelText );
215 AxisDescElem.appendChild( adLabelElem );
216
217 QDomElement adValuesElem = doc.createElement( QStringLiteral( "values" ) );
218 for ( int idx = 0; idx < layer->bandCount(); ++idx )
219 {
220 QDomElement adValueElem = doc.createElement( QStringLiteral( "singleValue" ) );
221 const QDomText adValueText = doc.createTextNode( QString::number( idx + 1 ) );
222 adValueElem.appendChild( adValueText );
223 adValuesElem.appendChild( adValueElem );
224 }
225 AxisDescElem.appendChild( adValuesElem );
226
227 //The coordinate reference system(s) in which the server can accept requests against
228 // this coverage offering and produce coverages from it.
229 QDomElement sCRSElem = doc.createElement( QStringLiteral( "supportedCRSs" ) );
230 QDomElement rCRSElem = doc.createElement( QStringLiteral( "requestResponseCRSs" ) );
231 const QDomText rCRSText = doc.createTextNode( layerCrs.authid() );
232 rCRSElem.appendChild( rCRSText );
233 sCRSElem.appendChild( rCRSElem );
234 QDomElement nCRSElem = doc.createElement( QStringLiteral( "nativeCRSs" ) );
235 const QDomText nCRSText = doc.createTextNode( layerCrs.authid() );
236 nCRSElem.appendChild( nCRSText );
237 sCRSElem.appendChild( nCRSElem );
238 layerElem.appendChild( sCRSElem );
239
240 //The formats (file encodings) in which the server can produce coverages from this
241 // coverage offering.
242 QDomElement sFormatsElem = doc.createElement( QStringLiteral( "supportedFormats" ) );
243 sFormatsElem.setAttribute( QStringLiteral( "nativeFormat" ), QStringLiteral( "raw binary" ) );
244 QDomElement formatsElem = doc.createElement( QStringLiteral( "formats" ) );
245 const QDomText formatsText = doc.createTextNode( QStringLiteral( "GeoTIFF" ) );
246 formatsElem.appendChild( formatsText );
247 sFormatsElem.appendChild( formatsElem );
248 layerElem.appendChild( sFormatsElem );
249
250 return layerElem;
251 }
252
253
254 QString serviceUrl( const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings )
255 {
256 static const QSet< QString > sFilter
257 {
258 QStringLiteral( "REQUEST" ),
259 QStringLiteral( "VERSION" ),
260 QStringLiteral( "SERVICE" ),
261 QStringLiteral( "_DC" )
262 };
263
264 QString href = QgsServerProjectUtils::wcsServiceUrl( project ? *project : *QgsProject::instance(), request, settings );
265
266 // Build default url
267 if ( href.isEmpty() )
268 {
269 QUrl url = request.originalUrl();
270 QUrlQuery q( url );
271
272 const QList<QPair<QString, QString> > queryItems = q.queryItems();
273 for ( const QPair<QString, QString> &param : queryItems )
274 {
275 if ( sFilter.contains( param.first.toUpper() ) )
276 q.removeAllQueryItems( param.first );
277 }
278
279 url.setQuery( q );
280 href = url.toString();
281
282 }
283
284 return href;
285 }
286
287 QgsRectangle parseBbox( const QString &bboxStr )
288 {
289 QStringList lst = bboxStr.split( ',' );
290 if ( lst.count() != 4 )
291 return QgsRectangle();
292
293 double d[4];
294 bool ok;
295 for ( int i = 0; i < 4; i++ )
296 {
297 lst[i].replace( ' ', '+' );
298 d[i] = lst[i].toDouble( &ok );
299 if ( !ok )
300 return QgsRectangle();
301 }
302 return QgsRectangle( d[0], d[1], d[2], d[3] );
303 }
304
305} // namespace QgsWfs
306
307
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Class for doing transforms between two map coordinate systems.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
QString what() const
Definition: qgsexception.h:48
QString name
Definition: qgsmaplayer.h:76
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:326
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:310
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
int bandCount() const
Returns the number of bands in this layer.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
int width() const
Returns the width of the (unclipped) raster.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
QgsServerRequest Class defining request interface passed to services QgsService::executeRequest() met...
QUrl originalUrl() const
Returns the request url as seen by the web server, by default this is equal to the url seen by QGIS s...
Provides a way to retrieve settings by prioritizing according to environment variables,...
SERVER_EXPORT QString wcsServiceUrl(const QgsProject &project, const QgsServerRequest &request=QgsServerRequest(), const QgsServerSettings &settings=QgsServerSettings())
Returns the WCS service url.
SERVER_EXPORT double ceilWithPrecision(double number, int places)
Returns a double greater than number to the specified number of places.
SERVER_EXPORT double floorWithPrecision(double number, int places)
Returns a double less than number to the specified number of places.
WCS implementation.
Definition: qgswcs.cpp:30
QDomElement getCoverageOffering(QDomDocument &doc, const QgsRasterLayer *layer, const QgsProject *project, bool brief)
CoverageOffering or CoverageOfferingBrief element.
Definition: qgswcsutils.cpp:35
QgsRectangle parseBbox(const QString &bboxStr)
Parse bounding box.
QString implementationVersion()
Returns the highest version supported by this implementation.
Definition: qgswcsutils.cpp:30
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project, const QgsServerSettings &settings)
Service URL string.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2466
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:2973
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
int precision