QgsRectangle QgsServerProjectParser::layerBoundingBoxInProjectCrs( const QDomElement& layerElem, const QDomDocument &doc ) const { QgsRectangle BBox; if ( layerElem.isNull() ) { return BBox; } //read box coordinates and layer auth. id QDomElement boundingBoxElem = layerElem.firstChildElement( "BoundingBox" ); if ( boundingBoxElem.isNull() ) { return BBox; } double minx, miny, maxx, maxy; bool conversionOk; minx = boundingBoxElem.attribute( "minx" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } miny = boundingBoxElem.attribute( "miny" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } maxx = boundingBoxElem.attribute( "maxx" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } maxy = boundingBoxElem.attribute( "maxy" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } QString version = doc.documentElement().attribute( "version" ); //create layer crs QgsCoordinateReferenceSystem layerCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( boundingBoxElem.attribute( version == "1.1.1" ? "SRS" : "CRS" ) ); if ( !layerCrs.isValid() ) { return BBox; } BBox.setXMinimum( minx ); BBox.setXMaximum( maxx ); BBox.setYMinimum( miny ); BBox.setYMaximum( maxy ); if ( version != "1.1.1" && layerCrs.hasAxisInverted() ) { BBox.invert(); } //get project crs QgsCoordinateTransform t( layerCrs, projectCrs() ); //transform BBox = t.transformBoundingBox( BBox ); return BBox; }
QList< tileMatrixSetDef > getTileMatrixSetList( const QgsProject *project, const QString &tms_ref ) { QList< tileMatrixSetDef > tmsList; bool gridsDefined = false; QStringList wmtsGridList = project->readListEntry( QStringLiteral( "WMTSGrids" ), QStringLiteral( "CRS" ), QStringList(), &gridsDefined ); if ( gridsDefined ) { if ( !tms_ref.isEmpty() && !wmtsGridList.contains( tms_ref ) ) { throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrixSet is unknown" ) ); } QStringList wmtsGridConfigList = project->readListEntry( QStringLiteral( "WMTSGrids" ), QStringLiteral( "Config" ) ); for ( const QString &c : wmtsGridConfigList ) { QStringList config = c.split( ',' ); QString crsStr = config[0]; if ( !tms_ref.isEmpty() && tms_ref != crsStr ) { continue; } tileMatrixInfo tmi; double fixedTop = 0.0; double fixedLeft = 0.0; double resolution = -1.0; int col = -1; int row = -1; // Does the CRS have fixed tile matrices if ( fixedTileMatrixInfoMap.contains( crsStr ) ) { tmi = fixedTileMatrixInfoMap[crsStr]; // Calculate resolution based on scale denominator resolution = tmi.resolution; // Get fixed corner QgsRectangle extent = tmi.extent; fixedTop = extent.yMaximum(); fixedLeft = extent.xMinimum(); // Get numbers of column and row for the resolution to cover the extent col = std::ceil( ( extent.xMaximum() - extent.xMinimum() ) / ( tileSize * resolution ) ); row = std::ceil( ( extent.yMaximum() - extent.yMinimum() ) / ( tileSize * resolution ) ); } else { tmi.ref = crsStr; fixedTop = QVariant( config[1] ).toDouble(); fixedLeft = QVariant( config[2] ).toDouble(); double minScale = QVariant( config[3] ).toDouble(); tmi.scaleDenominator = minScale; QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsStr ); tmi.unit = crs.mapUnits(); tmi.hasAxisInverted = crs.hasAxisInverted(); QgsCoordinateTransform crsTransform( QgsCoordinateReferenceSystem::fromOgcWmsCrs( GEO_EPSG_CRS_AUTHID ), crs, project ); try { // firstly transform CRS bounds expressed in WGS84 to CRS QgsRectangle extent = crsTransform.transformBoundingBox( crs.bounds() ); // Calculate resolution based on scale denominator resolution = POINTS_TO_M * minScale / QgsUnitTypes::fromUnitToUnitFactor( tmi.unit, QgsUnitTypes::DistanceMeters ); // Get numbers of column and row for the resolution to cover the extent col = std::ceil( ( extent.xMaximum() - extent.xMinimum() ) / ( tileSize * resolution ) ); row = std::ceil( ( extent.yMaximum() - extent.yMinimum() ) / ( tileSize * resolution ) ); // Calculate extent double bottom = fixedTop - row * tileSize * resolution; double right = fixedLeft + col * tileSize * resolution; tmi.extent = QgsRectangle( fixedLeft, bottom, right, fixedTop ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ) continue; } } // get lastLevel tmi.lastLevel = QVariant( config[4] ).toInt(); QList< tileMatrixDef > tileMatrixList; for ( int i = 0; i <= tmi.lastLevel; ++i ) { double scale = tmi.scaleDenominator / std::pow( 2, i ); double res = resolution / std::pow( 2, i ); tileMatrixDef tm; tm.resolution = res; tm.scaleDenominator = scale; tm.col = col * std::pow( 2, i ); tm.row = row * std::pow( 2, i ); tm.left = fixedLeft; tm.top = fixedTop; tileMatrixList.append( tm ); } tileMatrixSetDef tms; tms.ref = tmi.ref; tms.extent = tmi.extent; tms.unit = tmi.unit; tms.hasAxisInverted = tmi.hasAxisInverted; tms.tileMatrixList = tileMatrixList; tmsList.append( tms ); } return tmsList; } double minScale = project->readNumEntry( QStringLiteral( "WMTSMinScale" ), QStringLiteral( "/" ), -1.0 ); if ( minScale == -1.0 ) { minScale = getProjectMinScale( project ); } QStringList crsList = QgsServerProjectUtils::wmsOutputCrsList( *project ); if ( !tms_ref.isEmpty() && !crsList.contains( tms_ref ) ) { throw QgsRequestNotWellFormedException( QStringLiteral( "TileMatrixSet is unknown" ) ); } for ( const QString &crsStr : crsList ) { if ( !tms_ref.isEmpty() && tms_ref != crsStr ) { continue; } tileMatrixInfo tmi = calculateTileMatrixInfo( crsStr, project ); if ( tmi.scaleDenominator > 0.0 ) { tmsList.append( calculateTileMatrixSet( tmi, minScale ) ); } } return tmsList; }
tileMatrixInfo calculateTileMatrixInfo( const QString &crsStr, const QgsProject *project ) { // Does the CRS have fixed tile matrices if ( fixedTileMatrixInfoMap.contains( crsStr ) ) return fixedTileMatrixInfoMap[crsStr]; // Does the CRS have already calculated tile matrices if ( calculatedTileMatrixInfoMap.contains( crsStr ) ) return calculatedTileMatrixInfoMap[crsStr]; tileMatrixInfo tmi; tmi.ref = crsStr; QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsStr ); QgsCoordinateTransform crsTransform( wgs84, crs, project ); try { tmi.extent = crsTransform.transformBoundingBox( crs.bounds() ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ) } tmi.unit = crs.mapUnits(); tmi.hasAxisInverted = crs.hasAxisInverted(); // calculate tile matrix scale denominator double scaleDenominator = 0.0; int colRes = ( tmi.extent.xMaximum() - tmi.extent.xMinimum() ) / tileSize; int rowRes = ( tmi.extent.yMaximum() - tmi.extent.yMinimum() ) / tileSize; double UNIT_TO_M = QgsUnitTypes::fromUnitToUnitFactor( tmi.unit, QgsUnitTypes::DistanceMeters ); if ( colRes > rowRes ) scaleDenominator = std::ceil( colRes * UNIT_TO_M / POINTS_TO_M ); else scaleDenominator = std::ceil( rowRes * UNIT_TO_M / POINTS_TO_M ); // Update extent to get a square one QgsRectangle extent = tmi.extent; double res = POINTS_TO_M * scaleDenominator / UNIT_TO_M; int col = std::ceil( ( extent.xMaximum() - extent.xMinimum() ) / ( tileSize * res ) ); int row = std::ceil( ( extent.yMaximum() - extent.yMinimum() ) / ( tileSize * res ) ); if ( col > 1 || row > 1 ) { // Update scale if ( col > row ) { res = col * res; scaleDenominator = col * scaleDenominator; } else { res = row * res; scaleDenominator = row * scaleDenominator; } // set col and row to 1 for the square col = 1; row = 1; } // Calculate extent double left = ( extent.xMinimum() + ( extent.xMaximum() - extent.xMinimum() ) / 2.0 ) - ( col / 2.0 ) * ( tileSize * res ); double bottom = ( extent.yMinimum() + ( extent.yMaximum() - extent.yMinimum() ) / 2.0 ) - ( row / 2.0 ) * ( tileSize * res ); double right = ( extent.xMinimum() + ( extent.xMaximum() - extent.xMinimum() ) / 2.0 ) + ( col / 2.0 ) * ( tileSize * res ); double top = ( extent.yMinimum() + ( extent.yMaximum() - extent.yMinimum() ) / 2.0 ) + ( row / 2.0 ) * ( tileSize * res ); tmi.extent = QgsRectangle( left, bottom, right, top ); tmi.resolution = res; tmi.scaleDenominator = scaleDenominator; calculatedTileMatrixInfoMap[crsStr] = tmi; return tmi; }
// ------------------------ 1.1 ---------------------------------------------- bool QgsWcsCapabilities::parseDescribeCoverageDom11( QByteArray const &xml, QgsWcsCoverageSummary *coverage ) { QgsDebugMsg( "coverage->identifier = " + coverage->identifier ); if ( ! convertToDom( xml ) ) return false; QDomElement docElem = mCapabilitiesDom.documentElement(); QgsDebugMsg( "testing tagName " + docElem.tagName() ); QString tagName = stripNS( docElem.tagName() ); if ( tagName != QLatin1String( "CoverageDescriptions" ) ) { mErrorTitle = tr( "Dom Exception" ); mErrorFormat = QStringLiteral( "text/plain" ); mError = tr( "Could not get WCS capabilities in the expected format (DTD): no %1 found.\nThis might be due to an incorrect WCS Server URL.\nTag: %3\nResponse was:\n%4" ) .arg( QStringLiteral( "CoverageDescriptions" ), docElem.tagName(), QString( xml ) ); QgsLogger::debug( "Dom Exception: " + mError ); return false; } // Get image size, we can get it from BoundingBox with crs=urn:ogc:def:crs:OGC::imageCRS // but while at least one BoundingBox is mandatory, it does not have to be urn:ogc:def:crs:OGC::imageCRS // TODO: if BoundingBox with crs=urn:ogc:def:crs:OGC::imageCRS is not found, // we could calculate image size from GridCRS.GridOffsets (if available) QList<QDomElement> boundingBoxElements = domElements( docElem, QStringLiteral( "CoverageDescription.Domain.SpatialDomain.BoundingBox" ) ); QgsDebugMsg( QStringLiteral( "%1 BoundingBox found" ).arg( boundingBoxElements.size() ) ); const auto constBoundingBoxElements = boundingBoxElements; for ( const QDomElement &el : constBoundingBoxElements ) { QString authid = crsUrnToAuthId( el.attribute( QStringLiteral( "crs" ) ) ); QList<double> low = parseDoubles( domElementText( el, QStringLiteral( "LowerCorner" ) ) ); QList<double> high = parseDoubles( domElementText( el, QStringLiteral( "UpperCorner" ) ) ); if ( low.size() != 2 && high.size() != 2 ) continue; if ( el.attribute( QStringLiteral( "crs" ) ) == QLatin1String( "urn:ogc:def:crs:OGC::imageCRS" ) ) { coverage->width = ( int )( high[0] - low[0] + 1 ); coverage->height = ( int )( high[1] - low[1] + 1 ); coverage->hasSize = true; } else { QgsRectangle box; QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( authid ); if ( crs.isValid() && crs.hasAxisInverted() ) { box = QgsRectangle( low[1], low[0], high[1], high[0] ); } else { box = QgsRectangle( low[0], low[1], high[0], high[1] ); } coverage->boundingBoxes.insert( authid, box ); QgsDebugMsg( "crs: " + crs.authid() + ' ' + crs.description() + QString( " axisInverted = %1" ).arg( crs.hasAxisInverted() ) ); QgsDebugMsg( "BoundingBox: " + authid + " : " + box.toString() ); } } QgsDebugMsg( QStringLiteral( "width = %1 height = %2" ).arg( coverage->width ).arg( coverage->height ) ); // Each georectified coverage should have GridCRS QDomElement gridCRSElement = domElement( docElem, QStringLiteral( "CoverageDescription.Domain.SpatialDomain.GridCRS" ) ); if ( !gridCRSElement.isNull() ) { QString crsUrn = firstChildText( gridCRSElement, QStringLiteral( "GridBaseCRS" ) ); coverage->nativeCrs = crsUrnToAuthId( crsUrn ); QgsDebugMsg( "nativeCrs = " + coverage->nativeCrs ); // TODO: consider getting coverage size from GridOffsets (resolution) // if urn:ogc:def:crs:OGC::imageCRS BoundingBox was not found } coverage->times = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.Domain.TemporalDomain.timePosition" ) ); QList<QDomElement> timePeriodElements = domElements( docElem, QStringLiteral( "CoverageDescription.Domain.TemporalDomain.timePeriod" ) ); QgsDebugMsg( QStringLiteral( "%1 timePeriod found" ).arg( timePeriodElements.size() ) ); const auto constTimePeriodElements = timePeriodElements; for ( const QDomElement &el : constTimePeriodElements ) { QString beginPosition = domElementText( el, QStringLiteral( "beginTime" ) ); QString endPosition = domElementText( el, QStringLiteral( "endTime" ) ); QString timeResolution = domElementText( el, QStringLiteral( "timeResolution" ) ); // Format used in request QString time = beginPosition + '/' + endPosition; if ( !timeResolution.isEmpty() ) { time += '/' + timeResolution; } coverage->times << time; } // NULL / no data values // TODO: handle multiple fields / ranges (?) Q_FOREACH ( const QString &text, domElementsTexts( docElem, "CoverageDescription.Range.Field.NullValue" ) ) { bool ok; double val = text.toDouble( &ok ); if ( ok ) { coverage->nullValues.append( val ); } } QStringList formats = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.SupportedFormat" ) ); // There could be formats from GetCapabilities if ( !formats.isEmpty() ) { coverage->supportedFormat = formats; } QStringList crss = domElementsTexts( docElem, QStringLiteral( "CoverageDescription.SupportedCRS" ) ); QSet<QString> authids; // Set, in case one CRS is in more formats (URN, non URN) const auto constCrss = crss; for ( const QString &crs : constCrss ) { authids.insert( crsUrnToAuthId( crs ) ); } if ( !authids.isEmpty() ) { coverage->supportedCrs = authids.toList(); } coverage->described = true; return true; }