Ejemplo n.º 1
0
QList<QDomElement> QgsWcsCapabilities::domElements( const QDomElement &element, const QString &path )
{
  QList<QDomElement> list;

  QStringList names = path.split( '.' );
  if ( names.isEmpty() ) return list;
  QString name = names.value( 0 );
  names.removeFirst();

  QDomNode n1 = element.firstChild();
  while ( !n1.isNull() )
  {
    QDomElement el = n1.toElement();
    if ( !el.isNull() )
    {
      QString tagName = stripNS( el.tagName() );
      if ( tagName == name )
      {
        if ( names.isEmpty() )
        {
          list.append( el );
        }
        else
        {
          list.append( domElements( el, names.join( QStringLiteral( "." ) ) ) );
        }
      }
    }
    n1 = n1.nextSibling();
  }

  return list;
}
Ejemplo n.º 2
0
QStringList QgsWcsCapabilities::domElementsTexts( const QDomElement &element, const QString &path )
{
  QStringList list;
  QList<QDomElement> elems = domElements( element, path );

  const auto constElems = elems;
  for ( const QDomElement &el : constElems )
  {
    list << el.text();
  }
  return list;
}
Ejemplo n.º 3
0
bool QgsGmlSchema::parseXSD( const QByteArray &xml )
{
  QDomDocument dom;
  QString errorMsg;
  int errorLine;
  int errorColumn;
  if ( !dom.setContent( xml, false, &errorMsg, &errorLine, &errorColumn ) )
  {
    // TODO: error
    return false;
  }

  QDomElement docElem = dom.documentElement();

  QList<QDomElement> elementElements = domElements( docElem, "element" );

  //QgsDebugMsg( QString( "%1 elemets read" ).arg( elementElements.size() ) );

  foreach ( QDomElement elementElement, elementElements )
  {
    QString name = elementElement.attribute( "name" );
    QString type = elementElement.attribute( "type" );

    QString gmlBaseType = xsdComplexTypeGmlBaseType( docElem, stripNS( type ) );
    //QgsDebugMsg( QString( "gmlBaseType = %1" ).arg( gmlBaseType ) );
    //QgsDebugMsg( QString( "name = %1 gmlBaseType = %2" ).arg( name ).arg( gmlBaseType ) );
    // We should only use gml:AbstractFeatureType descendants which have
    // ancestor listed in gml:FeatureAssociationType (featureMember) descendant
    // But we could only loose some data if XSD was not correct, I think.

    if ( gmlBaseType == "AbstractFeatureType" )
    {
      // Get feature type definition
      QgsGmlFeatureClass featureClass( name, "" );
      xsdFeatureClass( docElem, stripNS( type ), featureClass );
      mFeatureClassMap.insert( name, featureClass );
    }
    // A feature may have more geometries, we take just the first one
  }
Ejemplo n.º 4
0
void QgsWcsCapabilities::parseCoverageOfferingBrief( QDomElement const &e, QgsWcsCoverageSummary &coverageSummary, QgsWcsCoverageSummary *parent )
{
  Q_UNUSED( parent );
  coverageSummary.orderId = ++mCoverageCount;

  coverageSummary.identifier = firstChildText( e, QStringLiteral( "name" ) );
  coverageSummary.title = firstChildText( e, QStringLiteral( "label" ) );
  coverageSummary.abstract = firstChildText( e, QStringLiteral( "description" ) );

  QList<QDomElement> posElements = domElements( e, QStringLiteral( "lonLatEnvelope.pos" ) );
  if ( posElements.size() != 2 )
  {
    QgsDebugMsg( QStringLiteral( "Wrong number of pos elements" ) );
  }
  else
  {
    QList<double> low = parseDoubles( posElements.value( 0 ).text() );
    QList<double> high = parseDoubles( posElements.value( 1 ).text() );
    if ( low.size() == 2 && high.size() == 2 )
    {
      coverageSummary.wgs84BoundingBox = QgsRectangle( low[0], low[1], high[0], high[1] );
      QgsDebugMsg( "wgs84BoundingBox = " + coverageSummary.wgs84BoundingBox.toString() );
    }
  }

  if ( !coverageSummary.identifier.isEmpty() )
  {
    QgsDebugMsg( "add coverage " + coverageSummary.identifier + " to supported" );
    mCoveragesSupported.push_back( coverageSummary );
  }

  if ( !coverageSummary.coverageSummary.empty() )
  {
    mCoverageParentIdentifiers[ coverageSummary.orderId ] = QStringList() << coverageSummary.identifier << coverageSummary.title << coverageSummary.abstract;
  }
  QgsDebugMsg( QStringLiteral( "coverage orderId = %1 identifier = %2" ).arg( coverageSummary.orderId ).arg( coverageSummary.identifier ) );
}
Ejemplo n.º 5
0
bool QgsWcsCapabilities::parseCapabilitiesDom( QByteArray const &xml, QgsWcsCapabilitiesProperty &capabilities )
{
  QgsDebugMsg( "Entered." );
#ifdef QGISDEBUG
  QFile file( QDir::tempPath() + "/qgis-wcs-capabilities.xml" );
  if ( file.open( QIODevice::WriteOnly ) )
  {
    file.write( xml );
    file.close();
  }
#endif

  if ( ! convertToDom( xml ) ) return false;

  QDomElement docElem = mCapabilitiesDom.documentElement();

  // Assert that the DTD is what we expected (i.e. a WCS Capabilities document)
  QgsDebugMsg( "testing tagName " + docElem.tagName() );

  QString tagName = stripNS( docElem.tagName() );
  if (
    // We don't support 1.0, but try WCS_Capabilities tag to get version
    tagName != "WCS_Capabilities" && // 1.0
    tagName != "Capabilities"  // 1.1, tags seen: Capabilities, wcs:Capabilities
  )
  {
    if ( tagName == "ExceptionReport" )
    {
      mErrorTitle = tr( "Exception" );
      mErrorFormat = "text/plain";
      mError = tr( "Could not get WCS capabilities: %1" ).arg( domElementText( docElem, "Exception.ExceptionText" ) );
    }
    else
    {
      mErrorTitle = tr( "Dom Exception" );
      mErrorFormat = "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( "Capabilities" )
               .arg( docElem.tagName() )
               .arg( QString( xml ) );
    }

    QgsLogger::debug( "Dom Exception: " + mError );

    return false;
  }

  capabilities.version = docElem.attribute( "version" );
  mVersion = capabilities.version;

  if ( !mVersion.startsWith( "1.0" ) && !mVersion.startsWith( "1.1" ) )
  {
    mErrorTitle = tr( "Version not supported" );
    mErrorFormat = "text/plain";
    mError = tr( "WCS server version %1 is not supported by QGIS (supported versions: 1.0.0, 1.1.0, 1.1.2)" )
             .arg( mVersion );

    QgsLogger::debug( "WCS version: " + mError );

    return false;
  }

  if ( mVersion.startsWith( "1.0" ) )
  {
    capabilities.title = domElementText( docElem, "Service.name" );
    capabilities.abstract = domElementText( docElem, "Service.description" );
    // There is also "label" in 1.0

    capabilities.getCoverageGetUrl = domElement( docElem, "Capability.Request.GetCoverage.DCPType.HTTP.Get.OnlineResource" ).attribute( "xlink:href" );

    parseContentMetadata( domElement( docElem, "ContentMetadata" ), capabilities.contents );
  }
  else if ( mVersion.startsWith( "1.1" ) )
  {
    capabilities.title = domElementText( docElem, "ServiceIdentification.Title" );
    capabilities.abstract = domElementText( docElem, "ServiceIdentification.Abstract" );

    QList<QDomElement> operationElements = domElements( docElem, "OperationsMetadata.Operation" );
    foreach ( QDomElement el, operationElements )
    {
      if ( el.attribute( "name" ) == "GetCoverage" )
      {
        capabilities.getCoverageGetUrl = domElement( el, "DCP.HTTP.Get" ).attribute( "xlink:href" );
      }
    }

    parseCoverageSummary( domElement( docElem, "Contents" ), capabilities.contents );
  }
Ejemplo n.º 6
0
// ------------------------ 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;
}
Ejemplo n.º 7
0
bool QgsWcsCapabilities::parseDescribeCoverageDom10( 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( "CoverageDescription" ) )
  {
    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( "CoverageDescription" ),
                   docElem.tagName(),
                   QString( xml ) );

    QgsLogger::debug( "Dom Exception: " + mError );

    return false;
  }

  QDomElement coverageOfferingElement = firstChild( docElem, QStringLiteral( "CoverageOffering" ) );

  if ( coverageOfferingElement.isNull() ) return false;
  QDomElement supportedCRSsElement = firstChild( coverageOfferingElement, QStringLiteral( "supportedCRSs" ) );

  // requestResponseCRSs and requestCRSs + responseCRSs are alternatives
  // we try to parse one or the other
  QStringList crsList;
  crsList = domElementsTexts( coverageOfferingElement, QStringLiteral( "supportedCRSs.requestResponseCRSs" ) );
  if ( crsList.isEmpty() )
  {
    crsList = domElementsTexts( coverageOfferingElement, QStringLiteral( "supportedCRSs.requestCRSs" ) );
    crsList << domElementsTexts( coverageOfferingElement, QStringLiteral( "supportedCRSs.responseCRSs" ) );
  }

  // exclude invalid CRSs from the lists
  for ( const QString &crsid : qgis::as_const( crsList ) )
  {
    if ( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsid ).isValid() )
    {
      coverage->supportedCrs << crsid;
    }
  }

  // TODO: requestCRSs, responseCRSs - must be then implemented also in provider
  //QgsDebugMsg( "supportedCrs = " + coverage->supportedCrs.join( "," ) );

  coverage->nativeCrs = domElementText( coverageOfferingElement, QStringLiteral( "supportedCRSs.nativeCRSs" ) );

  // may be GTiff, GeoTIFF, TIFF, GIF, ....
  coverage->supportedFormat = domElementsTexts( coverageOfferingElement, QStringLiteral( "supportedFormats.formats" ) );
  QgsDebugMsg( "supportedFormat = " + coverage->supportedFormat.join( "," ) );

  // spatialDomain and Grid/RectifiedGrid are optional according to specificationi.
  // If missing, we cannot get native resolution and size.
  QDomElement gridElement = domElement( coverageOfferingElement, QStringLiteral( "domainSet.spatialDomain.RectifiedGrid" ) );

  if ( gridElement.isNull() )
  {
    // Grid has also GridEnvelope from which we can get coverage size but it does not
    gridElement = domElement( coverageOfferingElement, QStringLiteral( "domainSet.spatialDomain.Grid" ) );
  }

  // If supportedCRSs.nativeCRSs is not defined we try to get it from RectifiedGrid
  if ( coverage->nativeCrs.isEmpty() )
  {
    QString crs = gridElement.attribute( QStringLiteral( "srsName" ) );
    if ( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crs ).isValid() )
    {
      coverage->nativeCrs = crs;
    }
  }

  if ( !gridElement.isNull() )
  {
    QList<int> low = parseInts( domElementText( gridElement, QStringLiteral( "limits.GridEnvelope.low" ) ) );
    QList<int> high = parseInts( domElementText( gridElement, QStringLiteral( "limits.GridEnvelope.high" ) ) );
    if ( low.size() == 2 && high.size() == 2 )
    {
      // low/high are indexes in grid -> size is height - low + 1
      double width = high[0] - low[0] + 1;
      double height = high[1] - low[1] + 1;
      if ( width > 0 && height > 0 )
      {
        coverage->width = width;
        coverage->height = height;
        coverage->hasSize = true;
      }
    }
    // RectifiedGrid has also gml:origin which we don't need I think (attention however
    // it should contain gml:Point but mapserver 6.0.3 / WCS 1.0.0 is using gml:pos instead)
    // RectifiedGrid also contains 2 gml:offsetVector which could be used to get resolution
    // but it should be sufficient to calc resolution from size

    // TODO: check if coverage is rotated, in that case probably treat as without size
    //       or recalc resolution from rotated grid to base CRS
  }

  QList<QDomElement> envelopeElements = domElements( coverageOfferingElement, QStringLiteral( "domainSet.spatialDomain.Envelope" ) );

  QgsDebugMsg( QStringLiteral( "%1 envelopeElements found" ).arg( envelopeElements.size() ) );

  const auto constEnvelopeElements = envelopeElements;
  for ( const QDomElement &el : constEnvelopeElements )
  {
    QString srsName = el.attribute( QStringLiteral( "srsName" ) );

    QList<QDomElement> posElements = domElements( el, QStringLiteral( "pos" ) );
    if ( posElements.size() != 2 )
    {
      QgsDebugMsg( QStringLiteral( "Wrong number of pos elements" ) );
      continue;
    }

    QList<double> low = parseDoubles( posElements.value( 0 ).text() );
    QList<double> high = parseDoubles( posElements.value( 1 ).text() );
    if ( low.size() == 2 && high.size() == 2 )
    {
      QgsRectangle box( low[0], low[1], high[0], high[1] );
      coverage->boundingBoxes.insert( srsName, box );
      QgsDebugMsg( "Envelope: " + srsName + " : " + box.toString() );
    }
  }

  coverage->times = domElementsTexts( coverageOfferingElement, QStringLiteral( "domainSet.temporalDomain.timePosition" ) );

  QList<QDomElement> timePeriodElements = domElements( coverageOfferingElement, QStringLiteral( "domainSet.temporalDomain.timePeriod" ) );

  QgsDebugMsg( QStringLiteral( "%1 timePeriod found" ).arg( timePeriodElements.size() ) );

  const auto constTimePeriodElements = timePeriodElements;
  for ( const QDomElement &el : constTimePeriodElements )
  {
    QString beginPosition = domElementText( el, QStringLiteral( "beginPosition" ) );
    QString endPosition = domElementText( el, QStringLiteral( "endPosition" ) );
    QString timeResolution = domElementText( el, QStringLiteral( "timeResolution" ) );
    // Format used in request
    QString time = beginPosition + '/' + endPosition;
    if ( !timeResolution.isEmpty() )
    {
      time += '/' + timeResolution;
    }
    coverage->times << time;
  }

  // Find native bounding box
  if ( !coverage->nativeCrs.isEmpty() )
  {
    const auto boundingBoxes = coverage->boundingBoxes;
    for ( auto it = boundingBoxes.constBegin(); it != boundingBoxes.constEnd(); ++it )
    {
      if ( it.key() == coverage->nativeCrs )
      {
        coverage->nativeBoundingBox = it.value();
      }
    }
  }

  // NULL / no data values
  // TODO: handle multiple range sets
  Q_FOREACH ( const QString &text, domElementsTexts( coverageOfferingElement, "rangeSet.RangeSet.nullValue.singleValue" ) )
  {
    bool ok;
    double val = text.toDouble( &ok );
    if ( ok )
    {
      coverage->nullValues.append( val );
    }
  }

  coverage->described = true;

  return true;
}