QGIS API Documentation  3.23.0-Master (dd0cd13a00)
qgsmimedatautils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmimedatautils.cpp
3  ---------------------
4  begin : November 2011
5  copyright : (C) 2011 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <QStringList>
16 
17 #include "qgsmimedatautils.h"
18 
19 #include "qgslayertree.h"
20 #include "qgslogger.h"
21 #include "qgspluginlayer.h"
22 #include "qgsrasterdataprovider.h"
23 #include "qgsrasterlayer.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsmeshlayer.h"
27 
28 #include <QRegularExpression>
29 
30 static const char *QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri";
31 
32 QgsMimeDataUtils::Uri::Uri( const QString &encData )
33 {
34  QgsDebugMsgLevel( "encData: " + encData, 4 );
35  const QStringList decoded = decode( encData );
36  if ( decoded.size() < 4 )
37  return;
38 
39  layerType = decoded[0];
40  providerKey = decoded[1];
41  name = decoded[2];
42  uri = decoded[3];
43 
44  if ( layerType == QLatin1String( "raster" ) && decoded.size() >= 6 )
45  {
46  supportedCrs = decode( decoded[4] );
47  supportedFormats = decode( decoded[5] );
48  }
49  else
50  {
51  supportedCrs.clear();
52  supportedFormats.clear();
53  }
54 
55  if ( decoded.size() > 6 )
56  layerId = decoded.at( 6 );
57  if ( decoded.size() > 7 )
58  pId = decoded.at( 7 );
59  if ( decoded.size() > 8 )
60  wkbType = QgsWkbTypes::parseType( decoded.at( 8 ) );
61  if ( decoded.size() > 9 )
62  filePath = decoded.at( 9 );
63 
64  QgsDebugMsgLevel( QStringLiteral( "type:%1 key:%2 name:%3 uri:%4 supportedCRS:%5 supportedFormats:%6" )
65  .arg( layerType, providerKey, name, uri,
66  supportedCrs.join( ',' ),
67  supportedFormats.join( ',' ) ), 2 );
68 }
69 
71  : providerKey( layer->providerType() )
72  , name( layer->name() )
73  , uri( layer->dataProvider() ? layer->dataProvider()->dataSourceUri() : layer->source() )
74  , layerId( layer->id() )
75  , pId( QString::number( QCoreApplication::applicationPid() ) )
76 {
77  switch ( layer->type() )
78  {
80  {
81  layerType = QStringLiteral( "vector" );
82  wkbType = qobject_cast< QgsVectorLayer *>( layer )->wkbType();
83  break;
84  }
86  {
87  layerType = QStringLiteral( "raster" );
88  break;
89  }
90 
92  {
93  layerType = QStringLiteral( "mesh" );
94  break;
95  }
97  {
98  layerType = QStringLiteral( "pointcloud" );
99  break;
100  }
102  {
103  layerType = QStringLiteral( "vector-tile" );
104  break;
105  }
106 
110  {
111  // plugin layers do not have a standard way of storing their URI...
112  return;
113  }
114  }
115 }
116 
118 {
119  return encode( { layerType,
120  providerKey,
121  name,
122  uri,
123  encode( supportedCrs ),
124  encode( supportedFormats ),
125  layerId,
126  pId,
127  QgsWkbTypes::displayString( wkbType ),
128  filePath
129  } );
130 }
131 
132 QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
133 {
134  owner = false;
135  error.clear();
136  if ( layerType != QLatin1String( "vector" ) )
137  {
138  error = QObject::tr( "%1: Not a vector layer." ).arg( name );
139  return nullptr;
140  }
141 
142  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
143  {
144  if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
145  {
146  return vectorLayer;
147  }
148  }
149  if ( providerKey == QLatin1String( "memory" ) )
150  {
151  error = QObject::tr( "Cannot get memory layer." );
152  return nullptr;
153  }
154 
155  owner = true;
157  return new QgsVectorLayer( uri, name, providerKey, options );
158 }
159 
160 QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
161 {
162  owner = false;
163  error.clear();
164  if ( layerType != QLatin1String( "raster" ) )
165  {
166  error = QObject::tr( "%1: Not a raster layer." ).arg( name );
167  return nullptr;
168  }
169 
170  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
171  {
172  if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
173  {
174  return rasterLayer;
175  }
176  }
177 
178  owner = true;
179  return new QgsRasterLayer( uri, name, providerKey );
180 }
181 
182 QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
183 {
184  owner = false;
185  error.clear();
186  if ( layerType != QLatin1String( "mesh" ) )
187  {
188  error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
189  return nullptr;
190  }
191 
192  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
193  {
194  if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
195  {
196  return meshLayer;
197  }
198  }
199 
200  owner = true;
201  return new QgsMeshLayer( uri, name, providerKey );
202 }
203 
205 {
206  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
207  {
208  return QgsProject::instance()->mapLayer( layerId );
209  }
210  return nullptr;
211 }
212 
213 // -----
214 
215 bool QgsMimeDataUtils::isUriList( const QMimeData *data )
216 {
217  return data->hasFormat( QGIS_URILIST_MIMETYPE );
218 }
219 
221 {
222  QMimeData *mimeData = new QMimeData();
223 
224  mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
225  return mimeData;
226 }
227 
228 
230 {
231  QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
232  QDataStream stream( &encodedData, QIODevice::ReadOnly );
233  QString xUri; // extended uri: layer_type:provider_key:uri
234  UriList list;
235  while ( !stream.atEnd() )
236  {
237  stream >> xUri;
238  QgsDebugMsgLevel( xUri, 4 );
239  list.append( Uri( xUri ) );
240  }
241  return list;
242 }
243 
244 
245 static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
246 {
247  if ( QgsLayerTree::isGroup( node ) )
248  {
249  const auto constChildren = QgsLayerTree::toGroup( node )->children();
250  for ( QgsLayerTreeNode *child : constChildren )
251  _addLayerTreeNodeToUriList( child, uris );
252  }
253  else if ( QgsLayerTree::isLayer( node ) )
254  {
255  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
256  QgsMapLayer *layer = nodeLayer->layer();
257  if ( !layer )
258  return;
259 
260  if ( layer->type() == QgsMapLayerType::PluginLayer )
261  return; // plugin layers do not have a standard way of storing their URI...
262 
263  uris << QgsMimeDataUtils::Uri( layer );
264  }
265 }
266 
267 QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
268 {
269  UriList uris;
270  const auto constNodes = nodes;
271  for ( QgsLayerTreeNode *node : constNodes )
272  _addLayerTreeNodeToUriList( node, uris );
273  return uriListToByteArray( uris );
274 }
275 
277 {
278  if ( uri.pId.isEmpty() )
279  return false;
280 
281  const qint64 pid = uri.pId.toLongLong();
282  return pid == QCoreApplication::applicationPid();
283 }
284 
285 QString QgsMimeDataUtils::encode( const QStringList &items )
286 {
287  QString encoded;
288  // Do not escape colon twice
289  const QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
290  const auto constItems = items;
291  for ( const QString &item : constItems )
292  {
293  QString str = item;
294  str.replace( '\\', QLatin1String( "\\\\" ) );
295  str.replace( re, QStringLiteral( "\\:" ) );
296  encoded += str + ':';
297  }
298  return encoded.left( encoded.length() - 1 );
299 }
300 
301 QStringList QgsMimeDataUtils::decode( const QString &encoded )
302 {
303  QStringList items;
304  QString item;
305  bool inEscape = false;
306  const auto constEncoded = encoded;
307  for ( const QChar c : constEncoded )
308  {
309  if ( c == '\\' && inEscape )
310  {
311  item += c;
312  }
313  else if ( c == '\\' )
314  {
315  inEscape = true;
316  }
317  else if ( c == ':' && !inEscape )
318  {
319  items.append( item );
320  item.clear();
321  }
322  else
323  {
324  item += c;
325  inEscape = false;
326  }
327  }
328  items.append( item );
329  return items;
330 }
331 
332 
333 QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
334 {
335  QByteArray encodedData;
336 
337  QDataStream stream( &encodedData, QIODevice::WriteOnly );
338  const auto constLayers = layers;
339  for ( const Uri &u : constLayers )
340  {
341  stream << u.data();
342  }
343  return encodedData;
344 }
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsMapLayerType type
Definition: qgsmaplayer.h:80
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:97
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
static bool isUriList(const QMimeData *data)
QList< QgsMimeDataUtils::Uri > UriList
static QMimeData * encodeUriList(const UriList &layers)
Encodes a URI list to a new QMimeData object.
static UriList decodeUriList(const QMimeData *data)
static bool hasOriginatedFromCurrentAppInstance(const QgsMimeDataUtils::Uri &uri)
Returns true if uri originated from the current QGIS application instance.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
Represents a raster layer.
Represents a vector layer which manages a vector based data sets.
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define str(x)
Definition: qgis.cpp:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsMeshLayer * meshLayer(bool &owner, QString &error) const
Gets mesh layer from uri if possible, otherwise returns nullptr and error is set.
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.
QString name
Human readable name to be used e.g. in layer tree.
QgsMapLayer * mapLayer() const
Returns the layer from the active project corresponding to this uri (if possible),...
QString pId
Unique ID associated with application instance.
QgsRasterLayer * rasterLayer(bool &owner, QString &error) const
Gets raster layer from uri if possible, otherwise returns nullptr and error is set.
QString providerKey
For "vector" / "raster" type: provider id.
QgsVectorLayer * vectorLayer(bool &owner, QString &error) const
Gets vector layer from uri if possible, otherwise returns nullptr and error is set.
QString layerId
Layer ID, if uri is associated with a layer from a QgsProject.
QString data() const
Returns encoded representation of the object.
QgsWkbTypes::Type wkbType
WKB type, if associated with a vector layer, or QgsWkbTypes::Unknown if not yet known.
Uri()=default
Constructs invalid URI.
QString layerType
Type of URI.
Setting options for loading vector layers.