Esempio n. 1
0
QgsFeatureList QgsOgrUtils::stringToFeatureList( const QString &string, const QgsFields &fields, QTextCodec *encoding )
{
  QgsFeatureList features;
  if ( string.isEmpty() )
    return features;

  QString randomFileName = QStringLiteral( "/vsimem/%1" ).arg( QUuid::createUuid().toString() );

  // create memory file system object from string buffer
  QByteArray ba = string.toUtf8();
  VSIFCloseL( VSIFileFromMemBuffer( randomFileName.toUtf8().constData(), reinterpret_cast< GByte * >( ba.data() ),
                                    static_cast< vsi_l_offset >( ba.size() ), FALSE ) );

  gdal::ogr_datasource_unique_ptr hDS( OGROpen( randomFileName.toUtf8().constData(), false, nullptr ) );
  if ( !hDS )
  {
    VSIUnlink( randomFileName.toUtf8().constData() );
    return features;
  }

  OGRLayerH ogrLayer = OGR_DS_GetLayer( hDS.get(), 0 );
  if ( !ogrLayer )
  {
    hDS.reset();
    VSIUnlink( randomFileName.toUtf8().constData() );
    return features;
  }

  gdal::ogr_feature_unique_ptr oFeat;
  while ( oFeat.reset( OGR_L_GetNextFeature( ogrLayer ) ), oFeat )
  {
    QgsFeature feat = readOgrFeature( oFeat.get(), fields, encoding );
    if ( feat.isValid() )
      features << feat;
  }

  hDS.reset();
  VSIUnlink( randomFileName.toUtf8().constData() );

  return features;
}
Esempio n. 2
0
QgsDataItem *QgsOgrDataItemProvider::createDataItem( const QString &pathIn, QgsDataItem *parentItem )
{
  QString path( pathIn );
  if ( path.isEmpty() )
    return nullptr;

  QgsDebugMsgLevel( "thePath: " + path, 2 );

  // zip settings + info
  QgsSettings settings;
  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
  QString vsiPrefix = QgsZipItem::vsiPrefix( path );
  bool is_vsizip = ( vsiPrefix == QLatin1String( "/vsizip/" ) );
  bool is_vsigzip = ( vsiPrefix == QLatin1String( "/vsigzip/" ) );
  bool is_vsitar = ( vsiPrefix == QLatin1String( "/vsitar/" ) );

  // should we check ext. only?
  // check if scanItemsInBrowser2 == extension or parent dir in scanItemsFastScanUris
  // TODO - do this in dir item, but this requires a way to inform which extensions are supported by provider
  // maybe a callback function or in the provider registry?
  bool scanExtSetting = false;
  if ( ( settings.value( QStringLiteral( "qgis/scanItemsInBrowser2" ),
                         "extension" ).toString() == QLatin1String( "extension" ) ) ||
       ( parentItem && settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
                                       QStringList() ).toStringList().contains( parentItem->path() ) ) ||
       ( ( is_vsizip || is_vsitar ) && parentItem && parentItem->parent() &&
         settings.value( QStringLiteral( "qgis/scanItemsFastScanUris" ),
                         QStringList() ).toStringList().contains( parentItem->parent()->path() ) ) )
  {
    scanExtSetting = true;
  }

  // get suffix, removing .gz if present
  QString tmpPath = path; //path used for testing, not for layer creation
  if ( is_vsigzip )
    tmpPath.chop( 3 );
  QFileInfo info( tmpPath );
  QString suffix = info.suffix().toLower();
  // extract basename with extension
  info.setFile( path );
  QString name = info.fileName();

  // If a .tab exists, then the corresponding .map/.dat is very likely a
  // side-car file of the .tab
  if ( suffix == QLatin1String( "map" ) || suffix == QLatin1String( "dat" ) )
  {
    if ( QFileInfo( QDir( info.path() ), info.baseName() + ".tab" ).exists() )
      return nullptr;
  }

  QgsDebugMsgLevel( "thePath= " + path + " tmpPath= " + tmpPath + " name= " + name
                    + " suffix= " + suffix + " vsiPrefix= " + vsiPrefix, 3 );

  QStringList myExtensions = fileExtensions();
  QStringList dirExtensions = directoryExtensions();

  // allow only normal files, supported directories, or VSIFILE items to continue
  bool isOgrSupportedDirectory = info.isDir() && dirExtensions.contains( suffix );
  if ( !isOgrSupportedDirectory && !info.isFile() && vsiPrefix.isEmpty() )
    return nullptr;

  // skip *.aux.xml files (GDAL auxiliary metadata files),
  // *.shp.xml files (ESRI metadata) and *.tif.xml files (TIFF metadata)
  // unless that extension is in the list (*.xml might be though)
  if ( path.endsWith( QLatin1String( ".aux.xml" ), Qt::CaseInsensitive ) &&
       !myExtensions.contains( QStringLiteral( "aux.xml" ) ) )
    return nullptr;
  if ( path.endsWith( QLatin1String( ".shp.xml" ), Qt::CaseInsensitive ) &&
       !myExtensions.contains( QStringLiteral( "shp.xml" ) ) )
    return nullptr;
  if ( path.endsWith( QLatin1String( ".tif.xml" ), Qt::CaseInsensitive ) &&
       !myExtensions.contains( QStringLiteral( "tif.xml" ) ) )
    return nullptr;

  // skip QGIS style xml files
  if ( path.endsWith( QLatin1String( ".xml" ), Qt::CaseInsensitive ) &&
       QgsStyle::isXmlStyleFile( path ) )
    return nullptr;

  // We have to filter by extensions, otherwise e.g. all Shapefile files are displayed
  // because OGR drive can open also .dbf, .shx.
  if ( myExtensions.indexOf( suffix ) < 0 && !dirExtensions.contains( suffix ) )
  {
    bool matches = false;
    const auto constWildcards = wildcards();
    for ( const QString &wildcard : constWildcards )
    {
      QRegExp rx( wildcard, Qt::CaseInsensitive, QRegExp::Wildcard );
      if ( rx.exactMatch( info.fileName() ) )
      {
        matches = true;
        break;
      }
    }
    if ( !matches )
      return nullptr;
  }

  // .dbf should probably appear if .shp is not present
  if ( suffix == QLatin1String( "dbf" ) )
  {
    QString pathShp = path.left( path.count() - 4 ) + ".shp";
    if ( QFileInfo::exists( pathShp ) )
      return nullptr;
  }

  // fix vsifile path and name
  if ( !vsiPrefix.isEmpty() )
  {
    // add vsiPrefix to path if needed
    if ( !path.startsWith( vsiPrefix ) )
      path = vsiPrefix + path;
    // if this is a /vsigzip/path_to_zip.zip/file_inside_zip remove the full path from the name
    // no need to change the name I believe
#if 0
    if ( ( is_vsizip || is_vsitar ) && ( path != vsiPrefix + parentItem->path() ) )
    {
      name = path;
      name = name.replace( vsiPrefix + parentItem->path() + '/', "" );
    }
#endif
  }

  // Filters out the OGR/GDAL supported formats that can contain multiple layers
  // and should be treated like a DB: GeoPackage and SQLite
  // NOTE: this formats are scanned for rasters too and they must
  //       be skipped by "gdal" provider or the rasters will be listed
  //       twice. ogrSupportedDbLayersExtensions must be kept in sync
  //       with the companion variable (same name) in the gdal provider
  //       class
  // TODO: add more OGR supported multiple layers formats here!
  static QStringList sOgrSupportedDbLayersExtensions { QStringLiteral( "gpkg" ),
      QStringLiteral( "sqlite" ),
      QStringLiteral( "db" ),
      QStringLiteral( "gdb" ),
      QStringLiteral( "kml" ) };
  static QStringList sOgrSupportedDbDriverNames { QStringLiteral( "GPKG" ),
      QStringLiteral( "db" ),
      QStringLiteral( "gdb" ) };

  // these extensions are trivial to read, so there's no need to rely on
  // the extension only scan here -- avoiding it always gives us the correct data type
  // and sublayer visiblity
  static QStringList sSkipFastTrackExtensions { QStringLiteral( "xlsx" ),
      QStringLiteral( "ods" ),
      QStringLiteral( "csv" ),
      QStringLiteral( "nc" ) };

  // Fast track: return item without testing if:
  // scanExtSetting or zipfile and scan zip == "Basic scan"
  // netCDF files can be both raster or vector, so fallback to opening
  if ( ( scanExtSetting ||
         ( ( is_vsizip || is_vsitar ) && scanZipSetting == QLatin1String( "basic" ) ) ) &&
       !sSkipFastTrackExtensions.contains( suffix ) )
  {
    // if this is a VRT file make sure it is vector VRT to avoid duplicates
    if ( suffix == QLatin1String( "vrt" ) )
    {
      CPLPushErrorHandler( CPLQuietErrorHandler );
      CPLErrorReset();
      GDALDriverH hDriver = GDALIdentifyDriver( path.toUtf8().constData(), nullptr );
      CPLPopErrorHandler();
      if ( !hDriver || GDALGetDriverShortName( hDriver ) == QLatin1String( "VRT" ) )
      {
        QgsDebugMsgLevel( QStringLiteral( "Skipping VRT file because root is not a OGR VRT" ), 2 );
        return nullptr;
      }
    }
    // Handle collections
    // Check if the layer has sublayers by comparing the extension
    QgsDataItem *item = nullptr;
    if ( ! sOgrSupportedDbLayersExtensions.contains( suffix ) )
    {
      item = new QgsOgrLayerItem( parentItem, name, path, path, QgsLayerItem::Vector );
    }
    else if ( suffix.compare( QLatin1String( "gpkg" ), Qt::CaseInsensitive ) == 0 )
    {
      item = new QgsGeoPackageCollectionItem( parentItem, name, path );
    }
    else
    {
      item = new QgsOgrDataCollectionItem( parentItem, name, path );
    }

    if ( item )
      return item;
  }

  // Slow track: scan file contents
  QgsDataItem *item = nullptr;

  // test that file is valid with OGR
  if ( OGRGetDriverCount() == 0 )
  {
    OGRRegisterAll();
  }
  // do not print errors, but write to debug
  CPLPushErrorHandler( CPLQuietErrorHandler );
  CPLErrorReset();
  gdal::dataset_unique_ptr hDS( GDALOpenEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr ) );
  CPLPopErrorHandler();

  if ( ! hDS )
  {
    QgsDebugMsg( QStringLiteral( "GDALOpen error # %1 : %2 on %3" ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ).arg( path ) );
    return nullptr;
  }

  GDALDriverH hDriver = GDALGetDatasetDriver( hDS.get() );
  QString driverName = GDALGetDriverShortName( hDriver );
  QgsDebugMsgLevel( QStringLiteral( "GDAL Driver : %1" ).arg( driverName ), 2 );
  int numLayers = GDALDatasetGetLayerCount( hDS.get() );

  // GeoPackage needs a specialized data item, mainly because of raster deletion not
  // yet implemented in GDAL (2.2.1)
  if ( driverName == QLatin1String( "GPKG" ) )
  {
    item = new QgsGeoPackageCollectionItem( parentItem, name, path );
  }
  else if ( numLayers > 1 || sOgrSupportedDbDriverNames.contains( driverName ) )
  {
    item = new QgsOgrDataCollectionItem( parentItem, name, path );
  }
  else
  {
    item = dataItemForLayer( parentItem, name, path, hDS.get(), 0, false, true );
  }
  return item;
}
Esempio n. 3
0
QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
  bool overwrite = parameterAsBool( parameters, QStringLiteral( "OVERWRITE" ), context );
  QString packagePath = parameterAsString( parameters, QStringLiteral( "OUTPUT" ), context );
  if ( packagePath.isEmpty() )
    throw QgsProcessingException( QObject::tr( "No output file specified." ) );

  // delete existing geopackage if it exists
  if ( overwrite && QFile::exists( packagePath ) )
  {
    feedback->pushInfo( QObject::tr( "Removing existing file '%1'" ).arg( packagePath ) );
    if ( !QFile( packagePath ).remove() )
    {
      throw QgsProcessingException( QObject::tr( "Could not remove existing file '%1'" ) );
    }
  }

  OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
  if ( !hGpkgDriver )
  {
    throw QgsProcessingException( QObject::tr( "GeoPackage driver not found." ) );
  }

  gdal::ogr_datasource_unique_ptr hDS( OGR_Dr_CreateDataSource( hGpkgDriver, packagePath.toUtf8().constData(), nullptr ) );
  if ( !hDS )
    throw QgsProcessingException( QObject::tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );

  bool errored = false;
  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );

  QgsProcessingMultiStepFeedback multiStepFeedback( layers.count(), feedback );

  int i = 0;
  for ( QgsMapLayer *layer : layers )
  {
    if ( feedback->isCanceled() )
      break;

    multiStepFeedback.setCurrentStep( i );
    i++;

    feedback->pushInfo( QObject::tr( "Packaging layer %1/%2: %3" ).arg( i ).arg( layers.count() ).arg( layer ? layer->name() : QString() ) );

    if ( !layer )
    {
      // don't throw immediately - instead do what we can and error out later
      feedback->pushDebugInfo( QObject::tr( "Error retrieving map layer." ) );
      errored = true;
      continue;
    }

    switch ( layer->type() )
    {
      case QgsMapLayer::VectorLayer:
      {
        if ( !packageVectorLayer( qobject_cast< QgsVectorLayer * >( layer ), packagePath,
                                  context, &multiStepFeedback ) )
          errored = true;
        break;
      }

      case QgsMapLayer::RasterLayer:
      {
        //not supported
        feedback->pushDebugInfo( QObject::tr( "Raster layers are not currently supported." ) );
        errored = true;
        break;
      }

      case QgsMapLayer::PluginLayer:
        //not supported
        feedback->pushDebugInfo( QObject::tr( "Packaging plugin layers is not supported." ) );
        errored = true;
        break;
    }
  }

  if ( errored )
    throw QgsProcessingException( QObject::tr( "Error obtained while packaging one or more layers." ) );

  QVariantMap outputs;
  outputs.insert( QStringLiteral( "OUTPUT" ), packagePath );
  return outputs;
}
Esempio n. 4
0
QList<QgsOgrDbLayerInfo *> QgsOgrLayerItem::subLayers( const QString &path, const QString &driver )
{

  QList<QgsOgrDbLayerInfo *> children;

  // Vector layers
  const QgsVectorLayer::LayerOptions layerOptions { QgsProject::instance()->transformContext() };
  QgsVectorLayer layer( path, QStringLiteral( "ogr_tmp" ), QStringLiteral( "ogr" ), layerOptions );
  if ( ! layer.isValid( ) )
  {
    QgsDebugMsgLevel( QStringLiteral( "Layer is not a valid %1 Vector layer %2" ).arg( path ), 3 );
  }
  else
  {
    // Collect mixed-geom layers
    QMultiMap<int, QStringList> subLayersMap;
    QgsOgrProvider *ogrProvider = qobject_cast<QgsOgrProvider *>( layer.dataProvider() );
    const QStringList subLayersList( ogrProvider->subLayersWithoutFeatureCount( ) );
    QMap< QString, int > mapLayerNameToCount;
    bool uniqueNames = true;
    int prevIdx = -1;
    for ( const QString &descriptor : subLayersList )
    {
      QStringList pieces = descriptor.split( QgsDataProvider::SUBLAYER_SEPARATOR );
      int idx = pieces[0].toInt();
      subLayersMap.insert( idx, pieces );
      if ( pieces.count() >= 4 && idx != prevIdx )
      {
        QString layerName = pieces[1];
        int count = ++mapLayerNameToCount[layerName];
        if ( count > 1 || layerName.isEmpty() )
          uniqueNames = false;
      }
      prevIdx = idx;
    }
    prevIdx = -1;
    const auto subLayerKeys = subLayersMap.keys( );
    for ( const int &idx : subLayerKeys )
    {
      if ( idx == prevIdx )
      {
        continue;
      }
      prevIdx = idx;
      QList<QStringList> values = subLayersMap.values( idx );
      for ( int i = 0; i < values.size(); ++i )
      {
        QStringList pieces = values.at( i );
        QString layerId = pieces[0];
        QString name = pieces[1];
        // QString featuresCount = pieces[2]; // Not used
        QString geometryType = pieces[3];
        QString geometryColumn = pieces[4];
        QgsLayerItem::LayerType layerType;
        layerType = QgsOgrLayerItem::layerTypeFromDb( geometryType );
        // example URI for mixed-geoms geoms:    '/path/gdal_sample_v1.2_no_extensions.gpkg|layerid=7|geometrytype=Point'
        // example URI for mixed-geoms attr table:    '/path/gdal_sample_v1.2_no_extensions.gpkg|layername=MyLayer|layerid=7'
        // example URI for single geoms:    '/path/gdal_sample_v1.2_no_extensions.gpkg|layerid=6'
        QString uri;
        if ( layerType != QgsLayerItem::LayerType::NoType )
        {
          if ( geometryType.contains( QStringLiteral( "Collection" ), Qt::CaseInsensitive ) )
          {
            QgsDebugMsgLevel( QStringLiteral( "Layer %1 is a geometry collection: skipping %2" ).arg( name, path ), 3 );
          }
          else
          {
            if ( uniqueNames )
              uri = QStringLiteral( "%1|layername=%2" ).arg( path, name );
            else
              uri = QStringLiteral( "%1|layerid=%2" ).arg( path, layerId );
            if ( values.size() > 1 )
            {
              uri += QStringLiteral( "|geometrytype=" ) + geometryType;
            }
            QgsDebugMsgLevel( QStringLiteral( "Adding %1 Vector item %2 %3 %4" ).arg( driver, name, uri, geometryType ), 3 );
            children.append( new QgsOgrDbLayerInfo( path, uri, name, geometryColumn, geometryType, layerType ) );
          }
        }
        else
        {
          QgsDebugMsgLevel( QStringLiteral( "Layer type is not a supported %1 Vector layer %2" ).arg( driver, path ), 3 );
          uri = QStringLiteral( "%1|layerid=%2|layername=%3" ).arg( path, layerId, name );
          children.append( new QgsOgrDbLayerInfo( path, uri, name, geometryColumn, geometryType, QgsLayerItem::LayerType::TableLayer ) );
        }
        QgsDebugMsgLevel( QStringLiteral( "Adding %1 Vector item %2 %3 %4" ).arg( driver, name, uri, geometryType ), 3 );
      }
    }
  }
  // Raster layers
  QgsRasterLayer::LayerOptions options;
  options.loadDefaultStyle = false;
  QgsRasterLayer rlayer( path, QStringLiteral( "gdal_tmp" ), QStringLiteral( "gdal" ), options );
  if ( !rlayer.dataProvider()->subLayers( ).empty() )
  {
    const QStringList layers( rlayer.dataProvider()->subLayers( ) );
    for ( const QString &uri : layers )
    {
      // Split on ':' since this is what comes out from the provider
      QStringList pieces = uri.split( ':' );
      QString name = pieces.value( pieces.length() - 1 );
      QgsDebugMsgLevel( QStringLiteral( "Adding GeoPackage Raster item %1 %2 %3" ).arg( name, uri ), 3 );
      children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) );
    }
  }
  else if ( rlayer.isValid( ) )
  {
    // Get the identifier
    GDALAllRegister();
    // do not print errors, but write to debug
    CPLPushErrorHandler( CPLQuietErrorHandler );
    CPLErrorReset();
    gdal::dataset_unique_ptr hDS( GDALOpen( path.toUtf8().constData(), GA_ReadOnly ) );
    CPLPopErrorHandler();

    if ( ! hDS )
    {
      QgsDebugMsg( QStringLiteral( "GDALOpen error # %1 : %2 " ).arg( CPLGetLastErrorNo() ).arg( CPLGetLastErrorMsg() ) );

    }
    else
    {
      QString uri( QStringLiteral( "%1:%2" ).arg( driver, path ) );
      QString name = GDALGetMetadataItem( hDS.get(), "IDENTIFIER", nullptr );
      hDS.reset();
      // Fallback: will not be able to delete the table
      if ( name.isEmpty() )
      {
        name = QFileInfo( path ).fileName();
      }
      else
      {
        uri += QStringLiteral( ":%1" ).arg( name );
      }

      QgsDebugMsgLevel( QStringLiteral( "Adding %1 Raster item %2 %3" ).arg( driver, name, path ), 3 );
      children.append( new QgsOgrDbLayerInfo( path, uri, name, QString(), QStringLiteral( "Raster" ), QgsLayerItem::LayerType::Raster ) );
    }
  }
  return children;
}