mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Fix incorrectly loaded sublayers if they had the same name (fixes #15168)
Use "layerid=N" instead of "layername=XYZ" for OGR sublayers
This commit is contained in:
parent
3cba1aa263
commit
5f6627624e
@ -12,14 +12,39 @@ class QgsSublayersDialog : QDialog
|
||||
Vsifile
|
||||
};
|
||||
|
||||
//! A structure that defines layers for the purpose of this dialog
|
||||
//! @note added in 2.16
|
||||
struct LayerDefinition
|
||||
{
|
||||
LayerDefinition();
|
||||
|
||||
int layerId; //!< identifier of the layer (one unique layer id may have multiple types though)
|
||||
QString layerName; //!< name of the layer (not necessarily unique)
|
||||
int count; //!< number of features (might be unused)
|
||||
QString type; //!< extra type depending on the use (e.g. geometry type for vector sublayers)
|
||||
};
|
||||
|
||||
//! List of layer definitions for the purpose of this dialog
|
||||
//! @note added in 2.16
|
||||
typedef QList<QgsSublayersDialog::LayerDefinition> LayerDefinitionList;
|
||||
|
||||
QgsSublayersDialog( ProviderType providerType, const QString& name, QWidget* parent /TransferThis/ = 0, const Qt::WindowFlags& fl = 0 );
|
||||
~QgsSublayersDialog();
|
||||
|
||||
void populateLayerTable( const QStringList& theList, const QString& delim = ":" );
|
||||
// Returns list of selected layers, if there are more layers with the same name,
|
||||
// geometry type is appended separated by semicolon, example: <layer>:<geometryType>
|
||||
QStringList selectionNames();
|
||||
QList<int> selectionIndexes();
|
||||
//! Populate the table with layers
|
||||
//! @note added in 2.16
|
||||
void populateLayerTable( const QgsSublayersDialog::LayerDefinitionList& list );
|
||||
|
||||
//! Returns list of selected layers
|
||||
//! @note added in 2.16
|
||||
QgsSublayersDialog::LayerDefinitionList selection();
|
||||
|
||||
//! @deprecated since 2.16 - use other populateLayerTable() variant
|
||||
void populateLayerTable( const QStringList& theList, const QString& delim = ":" ) /Deprecated/;
|
||||
//! @deprecated since 2.16 - use selection()
|
||||
QStringList selectionNames() /Deprecated/;
|
||||
//! @deprecated since 2.16 - use selection()
|
||||
QList<int> selectionIndexes() /Deprecated/;
|
||||
|
||||
public slots:
|
||||
void on_buttonBox_helpRequested();
|
||||
|
@ -3667,33 +3667,38 @@ bool QgisApp::askUserForZipItemLayers( QString path )
|
||||
{
|
||||
// We initialize a selection dialog and display it.
|
||||
QgsSublayersDialog chooseSublayersDialog( QgsSublayersDialog::Vsifile, "vsi", this );
|
||||
QgsSublayersDialog::LayerDefinitionList layers;
|
||||
|
||||
QStringList layers;
|
||||
for ( int i = 0; i < zipItem->children().size(); i++ )
|
||||
{
|
||||
QgsDataItem *item = zipItem->children().at( i );
|
||||
QgsLayerItem *layerItem = dynamic_cast<QgsLayerItem *>( item );
|
||||
if ( layerItem )
|
||||
if ( !layerItem )
|
||||
continue;
|
||||
|
||||
QgsDebugMsgLevel( QString( "item path=%1 provider=%2" ).arg( item->path(), layerItem->providerKey() ), 2 );
|
||||
|
||||
QgsSublayersDialog::LayerDefinition def;
|
||||
def.layerId = i;
|
||||
def.layerName = item->name();
|
||||
if ( layerItem->providerKey() == "gdal" )
|
||||
{
|
||||
QgsDebugMsgLevel( QString( "item path=%1 provider=%2" ).arg( item->path(), layerItem->providerKey() ), 2 );
|
||||
def.type = tr( "Raster" );
|
||||
}
|
||||
if ( layerItem && layerItem->providerKey() == "gdal" )
|
||||
else if ( layerItem->providerKey() == "ogr" )
|
||||
{
|
||||
layers << QString( "%1|%2|%3" ).arg( i ).arg( item->name(), "Raster" );
|
||||
}
|
||||
else if ( layerItem && layerItem->providerKey() == "ogr" )
|
||||
{
|
||||
layers << QString( "%1|%2|%3" ).arg( i ).arg( item->name(), tr( "Vector" ) );
|
||||
def.type = tr( "Vector" );
|
||||
}
|
||||
layers << def;
|
||||
}
|
||||
|
||||
chooseSublayersDialog.populateLayerTable( layers, "|" );
|
||||
chooseSublayersDialog.populateLayerTable( layers );
|
||||
|
||||
if ( chooseSublayersDialog.exec() )
|
||||
{
|
||||
Q_FOREACH ( int i, chooseSublayersDialog.selectionIndexes() )
|
||||
Q_FOREACH ( const QgsSublayersDialog::LayerDefinition& def, chooseSublayersDialog.selection() )
|
||||
{
|
||||
childItems << zipItem->children().at( i );
|
||||
childItems << zipItem->children().at( def.layerId );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3751,7 +3756,7 @@ void QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
|
||||
// We initialize a selection dialog and display it.
|
||||
QgsSublayersDialog chooseSublayersDialog( QgsSublayersDialog::Gdal, "gdal", this );
|
||||
|
||||
QStringList layers;
|
||||
QgsSublayersDialog::LayerDefinitionList layers;
|
||||
QStringList names;
|
||||
for ( int i = 0; i < sublayers.size(); i++ )
|
||||
{
|
||||
@ -3784,10 +3789,14 @@ void QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
|
||||
name.chop( 1 );
|
||||
|
||||
names << name;
|
||||
layers << QString( "%1|%2" ).arg( i ).arg( name );
|
||||
|
||||
QgsSublayersDialog::LayerDefinition def;
|
||||
def.layerId = i;
|
||||
def.layerName = name;
|
||||
layers << def;
|
||||
}
|
||||
|
||||
chooseSublayersDialog.populateLayerTable( layers, "|" );
|
||||
chooseSublayersDialog.populateLayerTable( layers );
|
||||
|
||||
if ( chooseSublayersDialog.exec() )
|
||||
{
|
||||
@ -3795,8 +3804,9 @@ void QgisApp::askUserForGDALSublayers( QgsRasterLayer *layer )
|
||||
QRegExp rx( "\"(.*)\"" );
|
||||
QString uri, name;
|
||||
|
||||
Q_FOREACH ( int i, chooseSublayersDialog.selectionIndexes() )
|
||||
Q_FOREACH ( const QgsSublayersDialog::LayerDefinition& def, chooseSublayersDialog.selection() )
|
||||
{
|
||||
int i = def.layerId;
|
||||
if ( rx.indexIn( sublayers[i] ) != -1 )
|
||||
{
|
||||
uri = rx.cap( 1 );
|
||||
@ -3874,79 +3884,75 @@ void QgisApp::askUserForOGRSublayers( QgsVectorLayer *layer )
|
||||
QStringList sublayers = layer->dataProvider()->subLayers();
|
||||
QString layertype = layer->dataProvider()->storageType();
|
||||
|
||||
QgsSublayersDialog::LayerDefinitionList list;
|
||||
Q_FOREACH ( const QString& sublayer, sublayers )
|
||||
{
|
||||
// OGR provider returns items in this format:
|
||||
// <layer_index>:<name>:<feature_count>:<geom_type>
|
||||
|
||||
QStringList elements = sublayer.split( ":" );
|
||||
// merge back parts of the name that may have been split
|
||||
while ( elements.size() > 4 )
|
||||
{
|
||||
elements[1] += ":" + elements[2];
|
||||
elements.removeAt( 2 );
|
||||
}
|
||||
|
||||
if ( elements.count() == 4 )
|
||||
{
|
||||
QgsSublayersDialog::LayerDefinition def;
|
||||
def.layerId = elements[0].toInt();
|
||||
def.layerName = elements[1];
|
||||
def.count = elements[2].toInt();
|
||||
def.type = elements[3];
|
||||
list << def;
|
||||
}
|
||||
else
|
||||
{
|
||||
QgsDebugMsg( "Unexpected output from OGR provider's subLayers()! " + sublayer );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We initialize a selection dialog and display it.
|
||||
QgsSublayersDialog chooseSublayersDialog( QgsSublayersDialog::Ogr, "ogr", this );
|
||||
chooseSublayersDialog.populateLayerTable( sublayers );
|
||||
chooseSublayersDialog.populateLayerTable( list );
|
||||
|
||||
if ( chooseSublayersDialog.exec() )
|
||||
if ( !chooseSublayersDialog.exec() )
|
||||
return;
|
||||
|
||||
QString uri = layer->source();
|
||||
//the separator char & was changed to | to be compatible
|
||||
//with url for protocol drivers
|
||||
if ( uri.contains( '|', Qt::CaseSensitive ) )
|
||||
{
|
||||
QString uri = layer->source();
|
||||
//the separator char & was changed to | to be compatible
|
||||
//with url for protocol drivers
|
||||
if ( uri.contains( '|', Qt::CaseSensitive ) )
|
||||
{
|
||||
// If we get here, there are some options added to the filename.
|
||||
// A valid uri is of the form: filename&option1=value1&option2=value2,...
|
||||
// We want only the filename here, so we get the first part of the split.
|
||||
QStringList theURIParts = uri.split( '|' );
|
||||
uri = theURIParts.at( 0 );
|
||||
}
|
||||
QgsDebugMsg( "Layer type " + layertype );
|
||||
// the user has done his choice
|
||||
loadOGRSublayers( layertype, uri, chooseSublayersDialog.selectionNames() );
|
||||
// If we get here, there are some options added to the filename.
|
||||
// A valid uri is of the form: filename&option1=value1&option2=value2,...
|
||||
// We want only the filename here, so we get the first part of the split.
|
||||
QStringList theURIParts = uri.split( '|' );
|
||||
uri = theURIParts.at( 0 );
|
||||
}
|
||||
}
|
||||
QgsDebugMsg( "Layer type " + layertype );
|
||||
|
||||
// This method will load with OGR the layers in parameter.
|
||||
// This method has been conceived to use the new URI
|
||||
// format of the ogrprovider so as to give precisions about which
|
||||
// sublayer to load into QGIS. It is normally triggered by the
|
||||
// sublayer selection dialog.
|
||||
void QgisApp::loadOGRSublayers( const QString& layertype, const QString& uri, const QStringList& list )
|
||||
{
|
||||
// The uri must contain the actual uri of the vectorLayer from which we are
|
||||
// going to load the sublayers.
|
||||
QString fileName = QFileInfo( uri ).baseName();
|
||||
QList<QgsMapLayer *> myList;
|
||||
for ( int i = 0; i < list.size(); i++ )
|
||||
Q_FOREACH ( const QgsSublayersDialog::LayerDefinition& def, chooseSublayersDialog.selection() )
|
||||
{
|
||||
QString composedURI;
|
||||
QStringList elements = list.at( i ).split( ':' );
|
||||
while ( elements.size() > 2 )
|
||||
{
|
||||
elements[0] += ':' + elements[1];
|
||||
elements.removeAt( 1 );
|
||||
}
|
||||
|
||||
QString layerName = elements.value( 0 );
|
||||
QString layerGeometryType = elements.value( 1 );
|
||||
if ( layerGeometryType == "any" )
|
||||
{
|
||||
layerGeometryType = "";
|
||||
elements.removeAt( 1 );
|
||||
}
|
||||
|
||||
if ( layertype != "GRASS" )
|
||||
{
|
||||
composedURI = uri + "|layername=" + layerName;
|
||||
}
|
||||
else
|
||||
{
|
||||
composedURI = uri + "|layerindex=" + layerName;
|
||||
}
|
||||
QString layerGeometryType = def.type;
|
||||
QString composedURI = uri + "|layerid=" + QString::number( def.layerId );
|
||||
|
||||
if ( !layerGeometryType.isEmpty() )
|
||||
{
|
||||
composedURI += "|geometrytype=" + layerGeometryType;
|
||||
}
|
||||
|
||||
// addVectorLayer( composedURI, list.at( i ), "ogr" );
|
||||
|
||||
QgsDebugMsg( "Creating new vector layer using " + composedURI );
|
||||
QString name = layerName;
|
||||
QString name = fileName + " " + def.layerName;
|
||||
if ( !layerGeometryType.isEmpty() )
|
||||
name += " " + layerGeometryType;
|
||||
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, fileName + " " + name, "ogr", false );
|
||||
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, name, "ogr", false );
|
||||
if ( layer && layer->isValid() )
|
||||
{
|
||||
myList << layer;
|
||||
|
@ -633,7 +633,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
|
||||
//! copies features to internal clipboard
|
||||
void copyFeatures( QgsFeatureStore & featureStore );
|
||||
void loadOGRSublayers( const QString& layertype, const QString& uri, const QStringList& list );
|
||||
void loadGDALSublayers( const QString& uri, const QStringList& list );
|
||||
|
||||
/** Deletes the selected attributes for the currently selected vector layer*/
|
||||
|
@ -26,6 +26,8 @@ QgsSublayersDialog::QgsSublayersDialog( ProviderType providerType, const QString
|
||||
QWidget* parent, const Qt::WindowFlags& fl )
|
||||
: QDialog( parent, fl )
|
||||
, mName( name )
|
||||
, mShowCount( false )
|
||||
, mShowType( false )
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
@ -34,6 +36,8 @@ QgsSublayersDialog::QgsSublayersDialog( ProviderType providerType, const QString
|
||||
setWindowTitle( tr( "Select vector layers to add..." ) );
|
||||
layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
|
||||
<< tr( "Number of features" ) << tr( "Geometry type" ) );
|
||||
mShowCount = true;
|
||||
mShowType = true;
|
||||
}
|
||||
else if ( providerType == QgsSublayersDialog::Gdal )
|
||||
{
|
||||
@ -45,6 +49,7 @@ QgsSublayersDialog::QgsSublayersDialog( ProviderType providerType, const QString
|
||||
setWindowTitle( tr( "Select layers to add..." ) );
|
||||
layersTable->setHeaderLabels( QStringList() << tr( "Layer ID" ) << tr( "Layer name" )
|
||||
<< tr( "Type" ) );
|
||||
mShowType = true;
|
||||
}
|
||||
|
||||
// add a "Select All" button - would be nicer with an icon
|
||||
@ -65,6 +70,72 @@ QgsSublayersDialog::~QgsSublayersDialog()
|
||||
layersTable->header()->saveState() );
|
||||
}
|
||||
|
||||
static bool _isLayerIdUnique( int layerId, QTreeWidget* layersTable )
|
||||
{
|
||||
int count = 0;
|
||||
for ( int j = 0; j < layersTable->topLevelItemCount(); j++ )
|
||||
{
|
||||
if ( layersTable->topLevelItem( j )->text( 0 ).toInt() == layerId )
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count == 1;
|
||||
}
|
||||
|
||||
QgsSublayersDialog::LayerDefinitionList QgsSublayersDialog::selection()
|
||||
{
|
||||
LayerDefinitionList list;
|
||||
for ( int i = 0; i < layersTable->selectedItems().size(); i++ )
|
||||
{
|
||||
QTreeWidgetItem* item = layersTable->selectedItems().at( i );
|
||||
|
||||
LayerDefinition def;
|
||||
def.layerId = item->text( 0 ).toInt();
|
||||
def.layerName = item->text( 1 );
|
||||
if ( mShowType )
|
||||
{
|
||||
// If there are more sub layers of the same name (virtual for geometry types),
|
||||
// add geometry type
|
||||
if ( !_isLayerIdUnique( def.layerId, layersTable ) )
|
||||
def.type = item->text( mShowCount ? 3 : 2 );
|
||||
}
|
||||
|
||||
list << def;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
void QgsSublayersDialog::populateLayerTable( const QgsSublayersDialog::LayerDefinitionList& list )
|
||||
{
|
||||
Q_FOREACH ( const LayerDefinition& item, list )
|
||||
{
|
||||
QStringList elements;
|
||||
elements << QString::number( item.layerId ) << item.layerName;
|
||||
if ( mShowCount )
|
||||
elements << QString::number( item.count );
|
||||
if ( mShowType )
|
||||
elements << item.type;
|
||||
layersTable->addTopLevelItem( new QTreeWidgetItem( elements ) );
|
||||
}
|
||||
|
||||
// resize columns
|
||||
QSettings settings;
|
||||
QByteArray ba = settings.value( "/Windows/" + mName + "SubLayers/headerState" ).toByteArray();
|
||||
if ( ! ba.isNull() )
|
||||
{
|
||||
layersTable->header()->restoreState( ba );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < layersTable->columnCount(); i++ )
|
||||
layersTable->resizeColumnToContents( i );
|
||||
layersTable->setColumnWidth( 1, layersTable->columnWidth( 1 ) + 10 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QStringList QgsSublayersDialog::selectionNames()
|
||||
{
|
||||
QStringList list;
|
||||
|
@ -33,14 +33,39 @@ class GUI_EXPORT QgsSublayersDialog : public QDialog, private Ui::QgsSublayersDi
|
||||
Vsifile
|
||||
};
|
||||
|
||||
//! A structure that defines layers for the purpose of this dialog
|
||||
//! @note added in 2.16
|
||||
typedef struct LayerDefinition
|
||||
{
|
||||
LayerDefinition() : layerId( -1 ), count( -1 ) {}
|
||||
|
||||
int layerId; //!< identifier of the layer (one unique layer id may have multiple types though)
|
||||
QString layerName; //!< name of the layer (not necessarily unique)
|
||||
int count; //!< number of features (might be unused)
|
||||
QString type; //!< extra type depending on the use (e.g. geometry type for vector sublayers)
|
||||
} LayerDefinition;
|
||||
|
||||
//! List of layer definitions for the purpose of this dialog
|
||||
//! @note added in 2.16
|
||||
typedef QList<LayerDefinition> LayerDefinitionList;
|
||||
|
||||
QgsSublayersDialog( ProviderType providerType, const QString& name, QWidget* parent = nullptr, const Qt::WindowFlags& fl = nullptr );
|
||||
~QgsSublayersDialog();
|
||||
|
||||
void populateLayerTable( const QStringList& theList, const QString& delim = ":" );
|
||||
// Returns list of selected layers, if there are more layers with the same name,
|
||||
// geometry type is appended separated by semicolon, example: <layer>:<geometryType>
|
||||
QStringList selectionNames();
|
||||
QList<int> selectionIndexes();
|
||||
//! Populate the table with layers
|
||||
//! @note added in 2.16
|
||||
void populateLayerTable( const LayerDefinitionList& list );
|
||||
|
||||
//! Returns list of selected layers
|
||||
//! @note added in 2.16
|
||||
LayerDefinitionList selection();
|
||||
|
||||
//! @deprecated since 2.16 - use other populateLayerTable() variant
|
||||
Q_DECL_DEPRECATED void populateLayerTable( const QStringList& theList, const QString& delim = ":" );
|
||||
//! @deprecated since 2.16 - use selection()
|
||||
Q_DECL_DEPRECATED QStringList selectionNames();
|
||||
//! @deprecated since 2.16 - use selection()
|
||||
Q_DECL_DEPRECATED QList<int> selectionIndexes();
|
||||
|
||||
public slots:
|
||||
void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
|
||||
@ -49,6 +74,8 @@ class GUI_EXPORT QgsSublayersDialog : public QDialog, private Ui::QgsSublayersDi
|
||||
protected:
|
||||
QString mName;
|
||||
QStringList mSelectedSubLayers;
|
||||
bool mShowCount; //!< whether to show number of features in the table
|
||||
bool mShowType; //!< whether to show type in the table
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user