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" 26 #include "qgscoordinatereferencesystem.h" 27 28 namespace QgsWcs 29 { implementationVersion()30 QString implementationVersion() 31 { 32 return QStringLiteral( "1.0.0" ); 33 } 34 getCoverageOffering(QDomDocument & doc,const QgsRasterLayer * layer,const QgsProject * project,bool brief)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(); 76 const QgsCoordinateReferenceSystem wgs84 = QgsCoordinateReferenceSystem::fromOgcWmsCrs( geoEpsgCrsAuthId() ); 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 serviceUrl(const QgsServerRequest & request,const QgsProject * project,const QgsServerSettings & settings)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> ¶m : 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 parseBbox(const QString & bboxStr)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 308