QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 #include "qgscoordinatetransform.h"
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  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:101
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
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:1530
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:1977
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
int precision