QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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"
20 #include "qgsserverprojectutils.h"
21 
22 #include "qgsproject.h"
23 #include "qgsexception.h"
24 #include "qgsrasterlayer.h"
25 #include "qgsmapserviceexception.h"
27 
28 namespace 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  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  QDomText labelText = doc.createTextNode( title );
61  labelElem.appendChild( labelText );
62  layerElem.appendChild( labelElem );
63 
64  //create description
65  QString abstract = layer->abstract();
66  if ( !abstract.isEmpty() )
67  {
68  QDomElement descriptionElem = doc.createElement( QStringLiteral( "description" ) );
69  QDomText descriptionText = doc.createTextNode( abstract );
70  descriptionElem.appendChild( descriptionText );
71  layerElem.appendChild( descriptionElem );
72  }
73 
74  //lonLatEnvelope
75  QgsCoordinateReferenceSystem layerCrs = layer->crs();
77  int wgs84precision = 6;
78  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  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  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  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  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  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  QDomText lowText = doc.createTextNode( QStringLiteral( "0 0" ) );
142  lowElem.appendChild( lowText );
143  gridEnvElem.appendChild( lowElem );
144  QDomElement highElem = doc.createElement( QStringLiteral( "gml:high" ) );
145  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  QDomText xAxisText = doc.createTextNode( QStringLiteral( "x" ) );
152  xAxisElem.appendChild( xAxisText );
153  rectGridElem.appendChild( xAxisElem );
154 
155  QDomElement yAxisElem = doc.createElement( QStringLiteral( "gml:axisName" ) );
156  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  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  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  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  QDomText rsNameText = doc.createTextNode( QStringLiteral( "Bands" ) );
193  rsNameElem.appendChild( rsNameText );
194  RangeSetElem.appendChild( rsNameElem );
195 
196  QDomElement rsLabelElem = doc.createElement( QStringLiteral( "label" ) );
197  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  QDomText adNameText = doc.createTextNode( QStringLiteral( "bands" ) );
209  adNameElem.appendChild( adNameText );
210  AxisDescElem.appendChild( adNameElem );
211 
212  QDomElement adLabelElem = doc.createElement( QStringLiteral( "label" ) );
213  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  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  QDomText rCRSText = doc.createTextNode( layerCrs.authid() );
232  rCRSElem.appendChild( rCRSText );
233  sCRSElem.appendChild( rCRSElem );
234  QDomElement nCRSElem = doc.createElement( QStringLiteral( "nativeCRSs" ) );
235  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  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 )
255  {
256  static QSet< QString > sFilter
257  {
258  QStringLiteral( "REQUEST" ),
259  QStringLiteral( "VERSION" ),
260  QStringLiteral( "SERVICE" ),
261  QStringLiteral( "_DC" )
262  };
263 
264  QString href;
265  if ( project )
266  {
267  href = QgsServerProjectUtils::wcsServiceUrl( *project );
268  }
269 
270  // Build default url
271  if ( href.isEmpty() )
272  {
273  QUrl url = request.originalUrl();
274  QUrlQuery q( url );
275 
276  for ( auto param : q.queryItems() )
277  {
278  if ( sFilter.contains( param.first.toUpper() ) )
279  q.removeAllQueryItems( param.first );
280  }
281 
282  url.setQuery( q );
283  href = url.toString();
284 
285  }
286 
287  return href;
288  }
289 
290  QgsRectangle parseBbox( const QString &bboxStr )
291  {
292  QStringList lst = bboxStr.split( ',' );
293  if ( lst.count() != 4 )
294  return QgsRectangle();
295 
296  double d[4];
297  bool ok;
298  for ( int i = 0; i < 4; i++ )
299  {
300  lst[i].replace( ' ', '+' );
301  d[i] = lst[i].toDouble( &ok );
302  if ( !ok )
303  return QgsRectangle();
304  }
305  return QgsRectangle( d[0], d[1], d[2], d[3] );
306  }
307 
308 } // namespace QgsWfs
309 
310 
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.
QString authid() const
Returns the authority identifier for the CRS.
Class for doing transforms between two map coordinate systems.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, 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:88
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:317
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:301
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
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:172
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
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...
SERVER_EXPORT QString wcsServiceUrl(const QgsProject &project)
Returns the WCS service url defined in a QGIS project.
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
QString serviceUrl(const QgsServerRequest &request, const QgsProject *project)
Service URL string.
QgsRectangle parseBbox(const QString &bboxStr)
Parse bounding box.
QString implementationVersion()
Returns the highest version supported by this implementation.
Definition: qgswcsutils.cpp:30
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:710
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
int precision