mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-01 00:03:42 -05:00
* fix guard header * [bugfix] Fix reading/writing of mesh layer to a qgis project #18801 * fix copy-paste error * fix copy-paste error * extract decode source to derived classes * remove raster providers from vector decode source * reset testdata to master
This commit is contained in:
parent
5c9366605a
commit
12183e98db
@ -100,6 +100,14 @@ QgsMeshLayer cannot be copied.
|
||||
|
||||
virtual bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual bool readXml( const QDomNode &layer_node, QgsReadWriteContext &context );
|
||||
|
||||
virtual bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const;
|
||||
|
||||
|
||||
QString providerType() const;
|
||||
%Docstring
|
||||
|
||||
@ -1376,6 +1376,35 @@ project files.
|
||||
%Docstring
|
||||
Called by writeLayerXML(), used by children to write state specific to them to
|
||||
project files.
|
||||
%End
|
||||
|
||||
virtual QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const;
|
||||
%Docstring
|
||||
Called by writeLayerXML(), used by derived classes to encode provider's specific data
|
||||
source to project files. Typically resolving absolute or relative paths, usernames and
|
||||
passwords or drivers prefixes ("HDF5:")
|
||||
|
||||
:param source: data source to encode, typically :py:func:`QgsMapLayer.source()`
|
||||
:param context: writing context (e.g. for conversion between relative and absolute paths)
|
||||
|
||||
:return: encoded source, typically to be written in the dom element "datasource"
|
||||
|
||||
.. versionadded:: 3.2
|
||||
%End
|
||||
|
||||
virtual QString decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const;
|
||||
%Docstring
|
||||
Called by readLayerXML(), used by derived classes to decode provider's specific data
|
||||
source from project files. Typically resolving absolute or relative paths, usernames and
|
||||
passwords or drivers prefixes ("HDF5:")
|
||||
|
||||
:param source: data source to decode, typically read from layer's dom element "datasource"
|
||||
:param dataProvider: string identification of data provider (e.g. "ogr"), typically read from layer's dom element
|
||||
:param context: reading context (e.g. for conversion between relative and absolute paths)
|
||||
|
||||
:return: decoded source, typically to be used as the layer's datasource
|
||||
|
||||
.. versionadded:: 3.2
|
||||
%End
|
||||
|
||||
void readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith = QString() );
|
||||
|
||||
@ -748,6 +748,11 @@ Write vector layer specific state to project file Dom node.
|
||||
Called by :py:func:`QgsMapLayer.writeXml()`
|
||||
%End
|
||||
|
||||
virtual QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const;
|
||||
|
||||
|
||||
virtual void resolveReferences( QgsProject *project );
|
||||
|
||||
%Docstring
|
||||
|
||||
@ -347,6 +347,9 @@ In a world file, this is normally the first row (without the sign).
|
||||
|
||||
virtual bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "qgsmeshlayer.h"
|
||||
#include "qgsmeshlayerrenderer.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgsreadwritecontext.h"
|
||||
#include "qgstriangularmesh.h"
|
||||
|
||||
QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath,
|
||||
@ -180,9 +181,84 @@ bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &e
|
||||
return true;
|
||||
}
|
||||
|
||||
QString QgsMeshLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
if ( provider == QLatin1String( "mdal" ) )
|
||||
{
|
||||
src = context.pathResolver().readPath( src );
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
QString QgsMeshLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
if ( providerType() == QLatin1String( "mdal" ) )
|
||||
{
|
||||
src = context.pathResolver().writePath( src );
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
bool QgsMeshLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
|
||||
QgsDebugMsgLevel( QStringLiteral( "Datasource in QgsMeshLayer::readXml: %1" ).arg( mDataSource.toLocal8Bit().data() ), 3 );
|
||||
|
||||
//process provider key
|
||||
QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
|
||||
|
||||
if ( pkeyNode.isNull() )
|
||||
{
|
||||
mProviderKey.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
QDomElement pkeyElt = pkeyNode.toElement();
|
||||
mProviderKey = pkeyElt.text();
|
||||
}
|
||||
|
||||
if ( !setDataProvider( mProviderKey ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return mValid; // should be true if read successfully
|
||||
}
|
||||
|
||||
bool QgsMeshLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
// first get the layer element so that we can append the type attribute
|
||||
QDomElement mapLayerNode = layer_node.toElement();
|
||||
|
||||
if ( mapLayerNode.isNull() || ( "maplayer" != mapLayerNode.nodeName() ) )
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "can't find <maplayer>" ), 2 );
|
||||
return false;
|
||||
}
|
||||
|
||||
mapLayerNode.setAttribute( QStringLiteral( "type" ), QStringLiteral( "mesh" ) );
|
||||
|
||||
// add provider node
|
||||
if ( mDataProvider )
|
||||
{
|
||||
QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
|
||||
QDomText providerText = document.createTextNode( providerType() );
|
||||
provider.appendChild( providerText );
|
||||
layer_node.appendChild( provider );
|
||||
}
|
||||
|
||||
// renderer specific settings
|
||||
QString errorMsg;
|
||||
return writeSymbology( layer_node, document, errorMsg, context );
|
||||
}
|
||||
|
||||
bool QgsMeshLayer::setDataProvider( QString const &provider )
|
||||
{
|
||||
Q_ASSERT( !mDataProvider ); //called from ctor
|
||||
if ( mDataProvider )
|
||||
delete mDataProvider;
|
||||
|
||||
mProviderKey = provider;
|
||||
QString dataSource = mDataSource;
|
||||
|
||||
@ -114,6 +114,10 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer
|
||||
virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) override SIP_FACTORY;
|
||||
bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ) override;
|
||||
bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const override;
|
||||
QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const override;
|
||||
QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const override;
|
||||
bool readXml( const QDomNode &layer_node, QgsReadWriteContext &context ) override;
|
||||
bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
|
||||
|
||||
//! Return the provider type for this layer
|
||||
QString providerType() const;
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "qgsmaplayer.h"
|
||||
#include "qgsmaplayerlegend.h"
|
||||
#include "qgsmaplayerstylemanager.h"
|
||||
#include "qgsmeshlayer.h"
|
||||
#include "qgspathresolver.h"
|
||||
#include "qgsprojectfiletransform.h"
|
||||
#include "qgsproject.h"
|
||||
@ -228,182 +229,7 @@ bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteCo
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: this should go to providers
|
||||
if ( provider == QLatin1String( "spatialite" ) )
|
||||
{
|
||||
QgsDataSourceUri uri( mDataSource );
|
||||
uri.setDatabase( context.pathResolver().readPath( uri.database() ) );
|
||||
mDataSource = uri.uri();
|
||||
}
|
||||
else if ( provider == QLatin1String( "ogr" ) )
|
||||
{
|
||||
QStringList theURIParts = mDataSource.split( '|' );
|
||||
theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
|
||||
mDataSource = theURIParts.join( QStringLiteral( "|" ) );
|
||||
}
|
||||
else if ( provider == QLatin1String( "gpx" ) )
|
||||
{
|
||||
QStringList theURIParts = mDataSource.split( '?' );
|
||||
theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
|
||||
mDataSource = theURIParts.join( QStringLiteral( "?" ) );
|
||||
}
|
||||
else if ( provider == QLatin1String( "delimitedtext" ) )
|
||||
{
|
||||
QUrl urlSource = QUrl::fromEncoded( mDataSource.toLatin1() );
|
||||
|
||||
if ( !mDataSource.startsWith( QLatin1String( "file:" ) ) )
|
||||
{
|
||||
QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( '?' ) ) );
|
||||
urlSource.setScheme( QStringLiteral( "file" ) );
|
||||
urlSource.setPath( file.path() );
|
||||
}
|
||||
|
||||
QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().readPath( urlSource.toLocalFile() ) );
|
||||
urlDest.setQueryItems( urlSource.queryItems() );
|
||||
mDataSource = QString::fromLatin1( urlDest.toEncoded() );
|
||||
}
|
||||
else if ( provider == QLatin1String( "wms" ) )
|
||||
{
|
||||
// >>> BACKWARD COMPATIBILITY < 1.9
|
||||
// For project file backward compatibility we must support old format:
|
||||
// 1. mode: <url>
|
||||
// example: http://example.org/wms?
|
||||
// 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
|
||||
// example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
|
||||
// example: featureCount=10,http://example.org/wms?
|
||||
// example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
|
||||
// This is modified version of old QgsWmsProvider::parseUri
|
||||
// The new format has always params crs,format,layers,styles and that params
|
||||
// should not appear in old format url -> use them to identify version
|
||||
// XYZ tile layers do not need to contain crs,format params, but they have type=xyz
|
||||
if ( !mDataSource.contains( QLatin1String( "type=" ) ) &&
|
||||
!mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
|
||||
{
|
||||
QgsDebugMsg( "Old WMS URI format detected -> converting to new format" );
|
||||
QgsDataSourceUri uri;
|
||||
if ( !mDataSource.startsWith( QLatin1String( "http:" ) ) )
|
||||
{
|
||||
QStringList parts = mDataSource.split( ',' );
|
||||
QStringListIterator iter( parts );
|
||||
while ( iter.hasNext() )
|
||||
{
|
||||
QString item = iter.next();
|
||||
if ( item.startsWith( QLatin1String( "username=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "username" ), item.mid( 9 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "password=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "password" ), item.mid( 9 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
|
||||
{
|
||||
// in < 1.9 tiled= may apper in to variants:
|
||||
// tiled=width;height - non tiled mode, specifies max width and max height
|
||||
// tiled=width;height;resolutions-1;resolution2;... - tile mode
|
||||
|
||||
QStringList params = item.mid( 6 ).split( ';' );
|
||||
|
||||
if ( params.size() == 2 ) // non tiled mode
|
||||
{
|
||||
uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
|
||||
uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
|
||||
}
|
||||
else if ( params.size() > 2 ) // tiled mode
|
||||
{
|
||||
// resolutions are no more needed and size limit is not used for tiles
|
||||
// we have to tell to the provider however that it is tiled
|
||||
uri.setParam( QStringLiteral( "tileMatrixSet" ), QLatin1String( "" ) );
|
||||
}
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "url=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uri.setParam( QStringLiteral( "url" ), mDataSource );
|
||||
}
|
||||
mDataSource = uri.encodedUri();
|
||||
// At this point, the URI is obviously incomplete, we add additional params
|
||||
// in QgsRasterLayer::readXml
|
||||
}
|
||||
// <<< BACKWARD COMPATIBILITY < 1.9
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if ( provider == QLatin1String( "gdal" ) )
|
||||
{
|
||||
if ( mDataSource.startsWith( QLatin1String( "NETCDF:" ) ) )
|
||||
{
|
||||
// NETCDF:filename:variable
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "NETCDF:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( mDataSource ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
mDataSource = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( mDataSource.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
|
||||
{
|
||||
// HDF4_SDS:subdataset_type:file_name:subdataset_index
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
|
||||
if ( r.exactMatch( mDataSource ) )
|
||||
{
|
||||
QString filename = r.cap( 2 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
mDataSource = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( mDataSource.startsWith( QLatin1String( "HDF5:" ) ) )
|
||||
{
|
||||
// HDF5:file_name:subdataset
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF5:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( mDataSource ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
mDataSource = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( mDataSource.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
|
||||
{
|
||||
// NITF_IM:0:filename
|
||||
// RADARSAT_2_CALIB:?:filename
|
||||
QRegExp r( "([^:]+):([^:]+):(.+)" );
|
||||
if ( r.exactMatch( mDataSource ) )
|
||||
{
|
||||
mDataSource = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !handled )
|
||||
mDataSource = context.pathResolver().readPath( mDataSource );
|
||||
}
|
||||
mDataSource = decodedSource( mDataSource, provider, context );
|
||||
|
||||
// Set the CRS from project file, asking the user if necessary.
|
||||
// Make it the saved CRS to have WMS layer projected correctly.
|
||||
@ -599,118 +425,11 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume
|
||||
|
||||
// data source
|
||||
QDomElement dataSource = document.createElement( QStringLiteral( "datasource" ) );
|
||||
|
||||
QString src = source();
|
||||
|
||||
const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
|
||||
// TODO: what about postgres, mysql and others, they should not go through writePath()
|
||||
if ( vlayer && vlayer->providerType() == QLatin1String( "spatialite" ) )
|
||||
{
|
||||
QgsDataSourceUri uri( src );
|
||||
QString database = context.pathResolver().writePath( uri.database() );
|
||||
uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
|
||||
src = uri.uri();
|
||||
}
|
||||
else if ( vlayer && vlayer->providerType() == QLatin1String( "ogr" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '|' );
|
||||
theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "|" ) );
|
||||
}
|
||||
else if ( vlayer && vlayer->providerType() == QLatin1String( "gpx" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '?' );
|
||||
theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "?" ) );
|
||||
}
|
||||
else if ( vlayer && vlayer->providerType() == QLatin1String( "delimitedtext" ) )
|
||||
{
|
||||
QUrl urlSource = QUrl::fromEncoded( src.toLatin1() );
|
||||
QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().writePath( urlSource.toLocalFile() ) );
|
||||
urlDest.setQueryItems( urlSource.queryItems() );
|
||||
src = QString::fromLatin1( urlDest.toEncoded() );
|
||||
}
|
||||
else if ( vlayer && vlayer->providerType() == QLatin1String( "memory" ) )
|
||||
{
|
||||
// Refetch the source from the provider, because adding fields actually changes the source for this provider.
|
||||
src = vlayer->dataProvider()->dataSourceUri();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if ( !vlayer )
|
||||
{
|
||||
const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
|
||||
// Update path for subdataset
|
||||
if ( rlayer && rlayer->providerType() == QLatin1String( "gdal" ) )
|
||||
{
|
||||
if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
|
||||
{
|
||||
// NETCDF:filename:variable
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "NETCDF:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
|
||||
{
|
||||
// HDF4_SDS:subdataset_type:file_name:subdataset_index
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 2 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
|
||||
{
|
||||
// HDF5:file_name:subdataset
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF5:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
|
||||
{
|
||||
// NITF_IM:0:filename
|
||||
// RADARSAT_2_CALIB:?:filename
|
||||
QRegExp r( "([^:]+):([^:]+):(.+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !handled )
|
||||
src = context.pathResolver().writePath( src );
|
||||
}
|
||||
|
||||
QString src = encodedSource( source(), context );
|
||||
QDomText dataSourceText = document.createTextNode( src );
|
||||
dataSource.appendChild( dataSourceText );
|
||||
|
||||
layerElement.appendChild( dataSource );
|
||||
|
||||
|
||||
// layer name
|
||||
QDomElement layerName = document.createElement( QStringLiteral( "layername" ) );
|
||||
QDomText layerNameText = document.createTextNode( name() );
|
||||
@ -846,7 +565,20 @@ bool QgsMapLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const
|
||||
// NOP by default; children will over-ride with behavior specific to them
|
||||
|
||||
return true;
|
||||
} // void QgsMapLayer::writeXml
|
||||
}
|
||||
|
||||
QString QgsMapLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
return source;
|
||||
}
|
||||
|
||||
QString QgsMapLayer::decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
Q_UNUSED( context );
|
||||
Q_UNUSED( dataProvider );
|
||||
return source;
|
||||
}
|
||||
|
||||
void QgsMapLayer::resolveReferences( QgsProject *project )
|
||||
{
|
||||
|
||||
@ -1197,6 +1197,33 @@ class CORE_EXPORT QgsMapLayer : public QObject
|
||||
*/
|
||||
virtual bool writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Called by writeLayerXML(), used by derived classes to encode provider's specific data
|
||||
* source to project files. Typically resolving absolute or relative paths, usernames and
|
||||
* passwords or drivers prefixes ("HDF5:")
|
||||
*
|
||||
* \param source data source to encode, typically QgsMapLayer::source()
|
||||
* \param context writing context (e.g. for conversion between relative and absolute paths)
|
||||
* \return encoded source, typically to be written in the dom element "datasource"
|
||||
*
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
virtual QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Called by readLayerXML(), used by derived classes to decode provider's specific data
|
||||
* source from project files. Typically resolving absolute or relative paths, usernames and
|
||||
* passwords or drivers prefixes ("HDF5:")
|
||||
*
|
||||
* \param source data source to decode, typically read from layer's dom element "datasource"
|
||||
* \param dataProvider string identification of data provider (e.g. "ogr"), typically read from layer's dom element
|
||||
* \param context reading context (e.g. for conversion between relative and absolute paths)
|
||||
* \return decoded source, typically to be used as the layer's datasource
|
||||
*
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
virtual QString decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Read custom properties from project file.
|
||||
\param layerNode note to read from
|
||||
|
||||
@ -48,6 +48,7 @@
|
||||
#include "qgsprojectbadlayerhandler.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsmaplayerlistutils.h"
|
||||
#include "qgsmeshlayer.h"
|
||||
#include "qgslayoutmanager.h"
|
||||
#include "qgsmaplayerstore.h"
|
||||
#include "qgsziputils.h"
|
||||
@ -820,6 +821,10 @@ bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &broken
|
||||
{
|
||||
mapLayer = new QgsRasterLayer;
|
||||
}
|
||||
else if ( type == QLatin1String( "mesh" ) )
|
||||
{
|
||||
mapLayer = new QgsMeshLayer;
|
||||
}
|
||||
else if ( type == QLatin1String( "plugin" ) )
|
||||
{
|
||||
QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
|
||||
|
||||
@ -1718,7 +1718,97 @@ bool QgsVectorLayer::writeXml( QDomNode &layer_node,
|
||||
// renderer specific settings
|
||||
QString errorMsg;
|
||||
return writeSymbology( layer_node, document, errorMsg, context );
|
||||
} // bool QgsVectorLayer::writeXml
|
||||
}
|
||||
|
||||
QString QgsVectorLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
|
||||
// TODO: what about postgres, mysql and others, they should not go through writePath()
|
||||
if ( providerType() == QLatin1String( "spatialite" ) )
|
||||
{
|
||||
QgsDataSourceUri uri( src );
|
||||
QString database = context.pathResolver().writePath( uri.database() );
|
||||
uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
|
||||
src = uri.uri();
|
||||
}
|
||||
else if ( providerType() == QLatin1String( "ogr" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '|' );
|
||||
theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "|" ) );
|
||||
}
|
||||
else if ( providerType() == QLatin1String( "gpx" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '?' );
|
||||
theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "?" ) );
|
||||
}
|
||||
else if ( providerType() == QLatin1String( "delimitedtext" ) )
|
||||
{
|
||||
QUrl urlSource = QUrl::fromEncoded( src.toLatin1() );
|
||||
QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().writePath( urlSource.toLocalFile() ) );
|
||||
urlDest.setQueryItems( urlSource.queryItems() );
|
||||
src = QString::fromLatin1( urlDest.toEncoded() );
|
||||
}
|
||||
else if ( providerType() == QLatin1String( "memory" ) )
|
||||
{
|
||||
// Refetch the source from the provider, because adding fields actually changes the source for this provider.
|
||||
src = dataProvider()->dataSourceUri();
|
||||
}
|
||||
else
|
||||
{
|
||||
src = context.pathResolver().writePath( src );
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
QString QgsVectorLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
|
||||
if ( provider == QLatin1String( "spatialite" ) )
|
||||
{
|
||||
QgsDataSourceUri uri( src );
|
||||
uri.setDatabase( context.pathResolver().readPath( uri.database() ) );
|
||||
src = uri.uri();
|
||||
}
|
||||
else if ( provider == QLatin1String( "ogr" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '|' );
|
||||
theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "|" ) );
|
||||
}
|
||||
else if ( provider == QLatin1String( "gpx" ) )
|
||||
{
|
||||
QStringList theURIParts = src.split( '?' );
|
||||
theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
|
||||
src = theURIParts.join( QStringLiteral( "?" ) );
|
||||
}
|
||||
else if ( provider == QLatin1String( "delimitedtext" ) )
|
||||
{
|
||||
QUrl urlSource = QUrl::fromEncoded( src.toLatin1() );
|
||||
|
||||
if ( !src.startsWith( QLatin1String( "file:" ) ) )
|
||||
{
|
||||
QUrl file = QUrl::fromLocalFile( src.left( src.indexOf( '?' ) ) );
|
||||
urlSource.setScheme( QStringLiteral( "file" ) );
|
||||
urlSource.setPath( file.path() );
|
||||
}
|
||||
|
||||
QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().readPath( urlSource.toLocalFile() ) );
|
||||
urlDest.setQueryItems( urlSource.queryItems() );
|
||||
src = QString::fromLatin1( urlDest.toEncoded() );
|
||||
}
|
||||
else
|
||||
{
|
||||
src = context.pathResolver().readPath( src );
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void QgsVectorLayer::resolveReferences( QgsProject *project )
|
||||
|
||||
@ -764,6 +764,9 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
|
||||
*/
|
||||
bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
|
||||
|
||||
QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const override;
|
||||
QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const override;
|
||||
|
||||
/**
|
||||
* Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
|
||||
* \since QGIS 3.0
|
||||
|
||||
@ -29,6 +29,7 @@ email : tim at linfiniti.com
|
||||
#include "qgsmultibandcolorrenderer.h"
|
||||
#include "qgspainting.h"
|
||||
#include "qgspalettedrasterrenderer.h"
|
||||
#include "qgspathresolver.h"
|
||||
#include "qgsprojectfiletransform.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgsrasterdataprovider.h"
|
||||
@ -41,6 +42,7 @@ email : tim at linfiniti.com
|
||||
#include "qgsrasterrendererregistry.h"
|
||||
#include "qgsrasterresamplefilter.h"
|
||||
#include "qgsrastershader.h"
|
||||
#include "qgsreadwritecontext.h"
|
||||
#include "qgsrectangle.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgssinglebandcolordatarenderer.h"
|
||||
@ -1621,6 +1623,225 @@ bool QgsRasterLayer::writeXml( QDomNode &layer_node,
|
||||
return writeSymbology( layer_node, document, errorMsg, context );
|
||||
}
|
||||
|
||||
QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
bool handled = false;
|
||||
|
||||
// Update path for subdataset
|
||||
if ( providerType() == QLatin1String( "gdal" ) )
|
||||
{
|
||||
if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
|
||||
{
|
||||
// NETCDF:filename:variable
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "NETCDF:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
|
||||
{
|
||||
// HDF4_SDS:subdataset_type:file_name:subdataset_index
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 2 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
|
||||
{
|
||||
// HDF5:file_name:subdataset
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF5:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
|
||||
{
|
||||
// NITF_IM:0:filename
|
||||
// RADARSAT_2_CALIB:?:filename
|
||||
QRegExp r( "([^:]+):([^:]+):(.+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !handled )
|
||||
src = context.pathResolver().writePath( src );
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QString src( source );
|
||||
|
||||
if ( provider == QLatin1String( "wms" ) )
|
||||
{
|
||||
// >>> BACKWARD COMPATIBILITY < 1.9
|
||||
// For project file backward compatibility we must support old format:
|
||||
// 1. mode: <url>
|
||||
// example: http://example.org/wms?
|
||||
// 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
|
||||
// example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
|
||||
// example: featureCount=10,http://example.org/wms?
|
||||
// example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
|
||||
// This is modified version of old QgsWmsProvider::parseUri
|
||||
// The new format has always params crs,format,layers,styles and that params
|
||||
// should not appear in old format url -> use them to identify version
|
||||
// XYZ tile layers do not need to contain crs,format params, but they have type=xyz
|
||||
if ( !src.contains( QLatin1String( "type=" ) ) &&
|
||||
!src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
|
||||
{
|
||||
QgsDebugMsg( "Old WMS URI format detected -> converting to new format" );
|
||||
QgsDataSourceUri uri;
|
||||
if ( !src.startsWith( QLatin1String( "http:" ) ) )
|
||||
{
|
||||
QStringList parts = src.split( ',' );
|
||||
QStringListIterator iter( parts );
|
||||
while ( iter.hasNext() )
|
||||
{
|
||||
QString item = iter.next();
|
||||
if ( item.startsWith( QLatin1String( "username=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "username" ), item.mid( 9 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "password=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "password" ), item.mid( 9 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
|
||||
{
|
||||
// in < 1.9 tiled= may apper in to variants:
|
||||
// tiled=width;height - non tiled mode, specifies max width and max height
|
||||
// tiled=width;height;resolutions-1;resolution2;... - tile mode
|
||||
|
||||
QStringList params = item.mid( 6 ).split( ';' );
|
||||
|
||||
if ( params.size() == 2 ) // non tiled mode
|
||||
{
|
||||
uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
|
||||
uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
|
||||
}
|
||||
else if ( params.size() > 2 ) // tiled mode
|
||||
{
|
||||
// resolutions are no more needed and size limit is not used for tiles
|
||||
// we have to tell to the provider however that it is tiled
|
||||
uri.setParam( QStringLiteral( "tileMatrixSet" ), QLatin1String( "" ) );
|
||||
}
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "url=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
|
||||
}
|
||||
else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
|
||||
{
|
||||
uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uri.setParam( QStringLiteral( "url" ), src );
|
||||
}
|
||||
src = uri.encodedUri();
|
||||
// At this point, the URI is obviously incomplete, we add additional params
|
||||
// in QgsRasterLayer::readXml
|
||||
}
|
||||
// <<< BACKWARD COMPATIBILITY < 1.9
|
||||
}
|
||||
else
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if ( provider == QLatin1String( "gdal" ) )
|
||||
{
|
||||
if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
|
||||
{
|
||||
// NETCDF:filename:variable
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "NETCDF:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
|
||||
{
|
||||
// HDF4_SDS:subdataset_type:file_name:subdataset_index
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 2 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
|
||||
{
|
||||
// HDF5:file_name:subdataset
|
||||
// filename can be quoted with " as it can contain colons
|
||||
QRegExp r( "HDF5:(.+):([^:]+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
QString filename = r.cap( 1 );
|
||||
if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
|
||||
filename = filename.mid( 1, filename.length() - 2 );
|
||||
src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
|
||||
{
|
||||
// NITF_IM:0:filename
|
||||
// RADARSAT_2_CALIB:?:filename
|
||||
QRegExp r( "([^:]+):([^:]+):(.+)" );
|
||||
if ( r.exactMatch( src ) )
|
||||
{
|
||||
src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !handled )
|
||||
src = context.pathResolver().readPath( src );
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
int QgsRasterLayer::width() const
|
||||
{
|
||||
if ( !mDataProvider ) return 0;
|
||||
|
||||
@ -388,7 +388,8 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer
|
||||
bool writeSymbology( QDomNode &, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const override;
|
||||
bool writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const override;
|
||||
bool writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
|
||||
|
||||
QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const override;
|
||||
QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const override;
|
||||
private:
|
||||
//! \brief Initialize default values
|
||||
void init();
|
||||
|
||||
@ -60,4 +60,3 @@ class QgsMdalProvider : public QgsMeshDataProvider
|
||||
};
|
||||
|
||||
#endif //QGSMDALPROVIDER_H
|
||||
|
||||
|
||||
@ -47,10 +47,12 @@ class TestQgsMeshLayer : public QObject
|
||||
void init() {} // will be called before each testfunction is executed.
|
||||
void cleanup() {} // will be called after every testfunction.
|
||||
|
||||
void test_write_read_project();
|
||||
void test_data_provider();
|
||||
void test_extent();
|
||||
};
|
||||
|
||||
|
||||
void TestQgsMeshLayer::initTestCase()
|
||||
{
|
||||
// init QGIS's paths - true means that all path will be inited from prefix
|
||||
@ -120,5 +122,24 @@ void TestQgsMeshLayer::test_extent()
|
||||
QCOMPARE( mMdalLayer->dataProvider()->extent(), mMdalLayer->extent() );
|
||||
}
|
||||
|
||||
void TestQgsMeshLayer::test_write_read_project()
|
||||
{
|
||||
QgsProject prj;
|
||||
prj.addMapLayer( mMemoryLayer->clone() );
|
||||
prj.addMapLayer( mMdalLayer->clone() );
|
||||
|
||||
QTemporaryFile f;
|
||||
QVERIFY( f.open() );
|
||||
f.close();
|
||||
prj.setFileName( f.fileName() );
|
||||
prj.write();
|
||||
|
||||
QgsProject prj2;
|
||||
prj2.setFileName( f.fileName() );
|
||||
QVERIFY( prj2.read() );
|
||||
QVector<QgsMapLayer *> layers = prj2.layers<QgsMapLayer *>();
|
||||
QVERIFY( layers.size() == 2 );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsMeshLayer )
|
||||
#include "testqgsmeshlayer.moc"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user