QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 
62  QgsDebugMsgLevel( QStringLiteral( "type:%1 key:%2 name:%3 uri:%4 supportedCRS:%5 supportedFormats:%6" )
63  .arg( layerType, providerKey, name, uri,
64  supportedCrs.join( ',' ),
65  supportedFormats.join( ',' ) ), 2 );
66 }
67 
69  : providerKey( layer->providerType() )
70  , name( layer->name() )
71  , uri( layer->dataProvider() ? layer->dataProvider()->dataSourceUri() : layer->source() )
72  , layerId( layer->id() )
73  , pId( QString::number( QCoreApplication::applicationPid() ) )
74 {
75  switch ( layer->type() )
76  {
78  {
79  layerType = QStringLiteral( "vector" );
80  wkbType = qobject_cast< QgsVectorLayer *>( layer )->wkbType();
81  break;
82  }
84  {
85  layerType = QStringLiteral( "raster" );
86  break;
87  }
88 
90  {
91  layerType = QStringLiteral( "mesh" );
92  break;
93  }
95  {
96  layerType = QStringLiteral( "pointcloud" );
97  break;
98  }
100  {
101  layerType = QStringLiteral( "vector-tile" );
102  break;
103  }
104 
107  {
108  // plugin layers do not have a standard way of storing their URI...
109  return;
110  }
111  }
112 }
113 
115 {
116  return encode( QStringList() << layerType << providerKey << name << uri << encode( supportedCrs ) << encode( supportedFormats ) << layerId << pId << QgsWkbTypes::displayString( wkbType ) );
117 }
118 
119 QgsVectorLayer *QgsMimeDataUtils::Uri::vectorLayer( bool &owner, QString &error ) const
120 {
121  owner = false;
122  error.clear();
123  if ( layerType != QLatin1String( "vector" ) )
124  {
125  error = QObject::tr( "%1: Not a vector layer." ).arg( name );
126  return nullptr;
127  }
128 
129  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
130  {
131  if ( QgsVectorLayer *vectorLayer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( layerId ) )
132  {
133  return vectorLayer;
134  }
135  }
136  if ( providerKey == QLatin1String( "memory" ) )
137  {
138  error = QObject::tr( "Cannot get memory layer." );
139  return nullptr;
140  }
141 
142  owner = true;
144  return new QgsVectorLayer( uri, name, providerKey, options );
145 }
146 
147 QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error ) const
148 {
149  owner = false;
150  error.clear();
151  if ( layerType != QLatin1String( "raster" ) )
152  {
153  error = QObject::tr( "%1: Not a raster layer." ).arg( name );
154  return nullptr;
155  }
156 
157  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
158  {
159  if ( QgsRasterLayer *rasterLayer = QgsProject::instance()->mapLayer<QgsRasterLayer *>( layerId ) )
160  {
161  return rasterLayer;
162  }
163  }
164 
165  owner = true;
166  return new QgsRasterLayer( uri, name, providerKey );
167 }
168 
169 QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const
170 {
171  owner = false;
172  error.clear();
173  if ( layerType != QLatin1String( "mesh" ) )
174  {
175  error = QObject::tr( "%1: Not a mesh layer." ).arg( name );
176  return nullptr;
177  }
178 
179  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
180  {
181  if ( QgsMeshLayer *meshLayer = QgsProject::instance()->mapLayer<QgsMeshLayer *>( layerId ) )
182  {
183  return meshLayer;
184  }
185  }
186 
187  owner = true;
188  return new QgsMeshLayer( uri, name, providerKey );
189 }
190 
192 {
193  if ( !layerId.isEmpty() && QgsMimeDataUtils::hasOriginatedFromCurrentAppInstance( *this ) )
194  {
195  return QgsProject::instance()->mapLayer( layerId );
196  }
197  return nullptr;
198 }
199 
200 // -----
201 
202 bool QgsMimeDataUtils::isUriList( const QMimeData *data )
203 {
204  return data->hasFormat( QGIS_URILIST_MIMETYPE );
205 }
206 
208 {
209  QMimeData *mimeData = new QMimeData();
210 
211  mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
212  return mimeData;
213 }
214 
215 
217 {
218  QByteArray encodedData = data->data( QGIS_URILIST_MIMETYPE );
219  QDataStream stream( &encodedData, QIODevice::ReadOnly );
220  QString xUri; // extended uri: layer_type:provider_key:uri
221  UriList list;
222  while ( !stream.atEnd() )
223  {
224  stream >> xUri;
225  QgsDebugMsgLevel( xUri, 4 );
226  list.append( Uri( xUri ) );
227  }
228  return list;
229 }
230 
231 
232 static void _addLayerTreeNodeToUriList( QgsLayerTreeNode *node, QgsMimeDataUtils::UriList &uris )
233 {
234  if ( QgsLayerTree::isGroup( node ) )
235  {
236  const auto constChildren = QgsLayerTree::toGroup( node )->children();
237  for ( QgsLayerTreeNode *child : constChildren )
238  _addLayerTreeNodeToUriList( child, uris );
239  }
240  else if ( QgsLayerTree::isLayer( node ) )
241  {
242  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
243  QgsMapLayer *layer = nodeLayer->layer();
244  if ( !layer )
245  return;
246 
247  if ( layer->type() == QgsMapLayerType::PluginLayer )
248  return; // plugin layers do not have a standard way of storing their URI...
249 
250  uris << QgsMimeDataUtils::Uri( layer );
251  }
252 }
253 
254 QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *> &nodes )
255 {
256  UriList uris;
257  const auto constNodes = nodes;
258  for ( QgsLayerTreeNode *node : constNodes )
259  _addLayerTreeNodeToUriList( node, uris );
260  return uriListToByteArray( uris );
261 }
262 
264 {
265  if ( uri.pId.isEmpty() )
266  return false;
267 
268  const qint64 pid = uri.pId.toLongLong();
269  return pid == QCoreApplication::applicationPid();
270 }
271 
272 QString QgsMimeDataUtils::encode( const QStringList &items )
273 {
274  QString encoded;
275  // Do not escape colon twice
276  QRegularExpression re( QStringLiteral( "(?<!\\\\):" ) );
277  const auto constItems = items;
278  for ( const QString &item : constItems )
279  {
280  QString str = item;
281  str.replace( '\\', QLatin1String( "\\\\" ) );
282  str.replace( re, QStringLiteral( "\\:" ) );
283  encoded += str + ':';
284  }
285  return encoded.left( encoded.length() - 1 );
286 }
287 
288 QStringList QgsMimeDataUtils::decode( const QString &encoded )
289 {
290  QStringList items;
291  QString item;
292  bool inEscape = false;
293  const auto constEncoded = encoded;
294  for ( QChar c : constEncoded )
295  {
296  if ( c == '\\' && inEscape )
297  {
298  item += c;
299  }
300  else if ( c == '\\' )
301  {
302  inEscape = true;
303  }
304  else if ( c == ':' && !inEscape )
305  {
306  items.append( item );
307  item.clear();
308  }
309  else
310  {
311  item += c;
312  inEscape = false;
313  }
314  }
315  items.append( item );
316  return items;
317 }
318 
319 
320 QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList &layers )
321 {
322  QByteArray encodedData;
323 
324  QDataStream stream( &encodedData, QIODevice::WriteOnly );
325  const auto constLayers = layers;
326  for ( const Uri &u : constLayers )
327  {
328  stream << u.data();
329  }
330  return encodedData;
331 }
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:70
QgsMapLayerType type
Definition: qgsmaplayer.h:77
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:95
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:467
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
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
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
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 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.