QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsprojectfiletransform.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprojectfiletransform.cpp - description
3  -------------------
4  begin : Sun 15 dec 2007
5  copyright : (C) 2007 by Magnus Homann
6  email : magnus at homann.se
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
20 #include "qgsprojectversion.h"
21 #include "qgslogger.h"
22 #include "qgsrasterlayer.h"
23 #include "qgsreadwritecontext.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsvectorlayer.h"
26 #include "qgspathresolver.h"
27 #include "qgsproject.h"
28 #include "qgsprojectproperty.h"
29 #include "qgsrasterbandstats.h"
30 #include "qgsrasterdataprovider.h"
31 #include "qgsxmlutils.h"
32 
33 #include <QTextStream>
34 #include <QDomDocument>
35 #include <QRegularExpression>
36 #ifndef QT_NO_PRINTER
37 #include <QPrinter> //to find out screen resolution
38 #endif
39 #include <cstdlib>
40 
42 
43 // Transformer functions below. Declare functions here,
44 // define them in qgsprojectfiletransform.cpp and add them
45 // to the transformArray with proper version number
46 void transformNull( QgsProjectFileTransform *pft ) { Q_UNUSED( pft ) } // Do absolutely nothing
56 
57 //helper functions
58 int rasterBandNumber( const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer );
59 void transformContrastEnhancement( QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem );
60 void transformRasterTransparency( QDomDocument &doc, const QDomElement &orig, QDomElement &rendererElem );
61 
62 typedef struct
63 {
66  void ( * transformFunc )( QgsProjectFileTransform * );
68 
69 typedef std::vector<TransformItem> Transformers;
70 
72 {
73  Q_UNUSED( newVersion )
74  bool returnValue = false;
75 
76  static const Transformers transformers(
77  {
78  {PFV( 0, 8, 0 ), PFV( 0, 8, 1 ), &transformNull},
79  {PFV( 0, 8, 1 ), PFV( 0, 9, 0 ), &transform081to090},
80  {PFV( 0, 9, 0 ), PFV( 0, 9, 1 ), &transformNull},
81  {PFV( 0, 9, 1 ), PFV( 0, 10, 0 ), &transform091to0100},
82  // Following line is a hack that takes us straight from 0.9.2 to 0.11.0
83  // due to an unknown bug in migrating 0.9.2 files which we didn't pursue (TS & GS)
84  {PFV( 0, 9, 2 ), PFV( 0, 11, 0 ), &transformNull},
85  {PFV( 0, 10, 0 ), PFV( 0, 11, 0 ), &transform0100to0110},
86  {PFV( 0, 11, 0 ), PFV( 1, 0, 0 ), &transform0110to1000},
87  {PFV( 1, 0, 0 ), PFV( 1, 1, 0 ), &transformNull},
88  {PFV( 1, 0, 2 ), PFV( 1, 1, 0 ), &transformNull},
89  {PFV( 1, 1, 0 ), PFV( 1, 2, 0 ), &transform1100to1200},
90  {PFV( 1, 2, 0 ), PFV( 1, 3, 0 ), &transformNull},
91  {PFV( 1, 3, 0 ), PFV( 1, 4, 0 ), &transformNull},
92  {PFV( 1, 4, 0 ), PFV( 1, 5, 0 ), &transform1400to1500},
93  {PFV( 1, 5, 0 ), PFV( 1, 6, 0 ), &transformNull},
94  {PFV( 1, 6, 0 ), PFV( 1, 7, 0 ), &transformNull},
95  {PFV( 1, 7, 0 ), PFV( 1, 8, 0 ), &transformNull},
96  {PFV( 1, 8, 0 ), PFV( 1, 9, 0 ), &transform1800to1900},
97  {PFV( 1, 9, 0 ), PFV( 2, 0, 0 ), &transformNull},
98  {PFV( 2, 0, 0 ), PFV( 2, 1, 0 ), &transformNull},
99  {PFV( 2, 1, 0 ), PFV( 2, 2, 0 ), &transformNull},
100  {PFV( 2, 2, 0 ), PFV( 2, 3, 0 ), &transform2200to2300},
101  // A transformer with a NULL from version means that it should be run when upgrading
102  // from any version and will take care that it's not going to cause trouble if it's
103  // run several times on the same file.
104  {PFV(), PFV( 3, 0, 0 ), &transform3000},
105  } );
106 
107  if ( !mDom.isNull() )
108  {
109  for ( const TransformItem &transformer : transformers )
110  {
111  if ( transformer.to >= mCurrentVersion && ( transformer.from == mCurrentVersion || transformer.from.isNull() ) )
112  {
113  // Run the transformer, and update the revision in every case
114  ( *( transformer.transformFunc ) )( this );
115  mCurrentVersion = transformer.to;
116  returnValue = true;
117  }
118  }
119  }
120  return returnValue;
121 }
122 
124 {
125  QgsDebugMsg( QStringLiteral( "Current project file version is %1.%2.%3" )
126  .arg( mCurrentVersion.majorVersion() )
127  .arg( mCurrentVersion.minorVersion() )
128  .arg( mCurrentVersion.subVersion() ) );
129 #ifdef QGISDEBUG
130  // Using QgsDebugMsg() didn't print the entire pft->dom()...
131  std::cout << mDom.toString( 2 ).toLatin1().constData(); // OK
132 #endif
133 }
134 
135 /*
136  * Transformers below!
137  */
138 
140 {
141  QgsDebugMsg( QStringLiteral( "Entering..." ) );
142  if ( ! pft->dom().isNull() )
143  {
144  // Start with inserting a mapcanvas element and populate it
145 
146  QDomElement mapCanvas; // A null element.
147 
148  // there should only be one <qgis>
149  QDomNode qgis = pft->dom().firstChildElement( QStringLiteral( "qgis" ) );
150  if ( ! qgis.isNull() )
151  {
152  QgsDebugMsg( QStringLiteral( "Populating new mapcanvas" ) );
153 
154  // Create a mapcanvas
155  mapCanvas = pft->dom().createElement( QStringLiteral( "mapcanvas" ) );
156  // Append mapcanvas to parent 'qgis'.
157  qgis.appendChild( mapCanvas );
158  // Re-parent units
159  mapCanvas.appendChild( qgis.namedItem( QStringLiteral( "units" ) ) );
160  // Re-parent extent
161  mapCanvas.appendChild( qgis.namedItem( QStringLiteral( "extent" ) ) );
162 
163  // See if we can find if projection is on.
164 
165  const QDomElement properties = qgis.firstChildElement( QStringLiteral( "properties" ) );
166  const QDomElement spatial = properties.firstChildElement( QStringLiteral( "SpatialRefSys" ) );
167  const QDomElement hasCrsTransformEnabled = spatial.firstChildElement( QStringLiteral( "ProjectionsEnabled" ) );
168  // Type is 'int', and '1' if on.
169  // Create an element
170  QDomElement projection = pft->dom().createElement( QStringLiteral( "projections" ) );
171  QgsDebugMsg( QStringLiteral( "Projection flag: " ) + hasCrsTransformEnabled.text() );
172  // Set flag from ProjectionsEnabled
173  projection.appendChild( pft->dom().createTextNode( hasCrsTransformEnabled.text() ) );
174  // Set new element as child of <mapcanvas>
175  mapCanvas.appendChild( projection );
176 
177  }
178 
179 
180  // Transforming coordinate-transforms
181  // Create a list of all map layers
182  const QDomNodeList mapLayers = pft->dom().elementsByTagName( QStringLiteral( "maplayer" ) );
183  bool doneDestination = false;
184  for ( int i = 0; i < mapLayers.count(); i++ )
185  {
186  QDomNode mapLayer = mapLayers.item( i );
187  // Find the coordinatetransform
188  const QDomNode coordinateTransform = mapLayer.namedItem( QStringLiteral( "coordinatetransform" ) );
189  // Find the sourcesrs
190  const QDomNode sourceCrs = coordinateTransform.namedItem( QStringLiteral( "sourcesrs" ) );
191  // Rename to srs
192  sourceCrs.toElement().setTagName( QStringLiteral( "srs" ) );
193  // Re-parent to maplayer
194  mapLayer.appendChild( sourceCrs );
195  // Re-move coordinatetransform
196  // Take the destination CRS of the first layer and use for mapcanvas projection
197  if ( ! doneDestination )
198  {
199  // Use destination CRS from the last layer
200  const QDomNode destinationCRS = coordinateTransform.namedItem( QStringLiteral( "destinationsrs" ) );
201  // Re-parent the destination CRS to the mapcanvas
202  // If mapcanvas wasn't set, nothing will happen.
203  mapCanvas.appendChild( destinationCRS );
204  // Only do this once
205  doneDestination = true;
206  }
207  mapLayer.removeChild( coordinateTransform );
208  //QDomNode id = mapLayer.namedItem("id");
209  //QgsDebugMsg(QString("Found maplayer ") + id.toElement().text());
210 
211  }
212 
213  // Set the flag 'visible' to match the status of 'checked'
214  const QDomNodeList legendLayerFiles = pft->dom().elementsByTagName( QStringLiteral( "legendlayerfile" ) );
215  QgsDebugMsg( QStringLiteral( "Legend layer file entries: " ) + QString::number( legendLayerFiles.count() ) );
216  for ( int i = 0; i < mapLayers.count(); i++ )
217  {
218  // Get one maplayer element from list
219  const QDomElement mapLayer = mapLayers.item( i ).toElement();
220  // Find it's id.
221  const QString id = mapLayer.firstChildElement( QStringLiteral( "id" ) ).text();
222  QgsDebugMsg( QStringLiteral( "Handling layer %1" ).arg( id ) );
223  // Now, look it up in legend
224  for ( int j = 0; j < legendLayerFiles.count(); j++ )
225  {
226  QDomElement legendLayerFile = legendLayerFiles.item( j ).toElement();
227  if ( id == legendLayerFile.attribute( QStringLiteral( "layerid" ) ) )
228  {
229  // Found a the legend layer that matches the maplayer
230  QgsDebugMsg( QStringLiteral( "Found matching id" ) );
231 
232  // Set visible flag from maplayer to legendlayer
233  legendLayerFile.setAttribute( QStringLiteral( "visible" ), mapLayer.attribute( QStringLiteral( "visible" ) ) );
234 
235  // Set overview flag from maplayer to legendlayer
236  legendLayerFile.setAttribute( QStringLiteral( "isInOverview" ), mapLayer.attribute( QStringLiteral( "showInOverviewFlag" ) ) );
237  }
238  }
239  }
240  }
241 }
242 
244 {
245  if ( ! pft->dom().isNull() )
246  {
247  // Insert transforms here!
248  const QDomNodeList rasterPropertyList = pft->dom().elementsByTagName( QStringLiteral( "rasterproperties" ) );
249  QgsDebugMsg( QStringLiteral( "Raster properties file entries: " ) + QString::number( rasterPropertyList.count() ) );
250  for ( int i = 0; i < rasterPropertyList.count(); i++ )
251  {
252  // Get one rasterproperty element from list, and rename the sub-properties.
253  const QDomNode rasterProperty = rasterPropertyList.item( i );
254  // rasterProperty.namedItem("").toElement().setTagName("");
255 
256  rasterProperty.namedItem( QStringLiteral( "stdDevsToPlotDouble" ) ).toElement().setTagName( QStringLiteral( "mStandardDeviations" ) );
257 
258  rasterProperty.namedItem( QStringLiteral( "invertHistogramFlag" ) ).toElement().setTagName( QStringLiteral( "mInvertPixelsFlag" ) );
259  rasterProperty.namedItem( QStringLiteral( "showDebugOverLayFlag" ) ).toElement().setTagName( QStringLiteral( "mDebugOverLayFlag" ) );
260 
261  rasterProperty.namedItem( QStringLiteral( "redBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mRedBandName" ) );
262  rasterProperty.namedItem( QStringLiteral( "blueBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mBlueBandName" ) );
263  rasterProperty.namedItem( QStringLiteral( "greenBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mGreenBandName" ) );
264  rasterProperty.namedItem( QStringLiteral( "grayBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mGrayBandName" ) );
265  }
266 
267  // Changing symbol size for hard: symbols
268  const QDomNodeList symbolPropertyList = pft->dom().elementsByTagName( QStringLiteral( "symbol" ) );
269  for ( int i = 0; i < symbolPropertyList.count(); i++ )
270  {
271  // Get the <poinmtsymbol> to check for 'hard:' for each <symbol>
272  QDomNode symbolProperty = symbolPropertyList.item( i );
273 
274  const QDomElement pointSymbol = symbolProperty.firstChildElement( QStringLiteral( "pointsymbol" ) );
275  if ( pointSymbol.text().startsWith( QLatin1String( "hard:" ) ) )
276  {
277  // Get pointsize and line width
278  const int lineWidth = symbolProperty.firstChildElement( QStringLiteral( "outlinewidth" ) ).text().toInt();
279  int pointSize = symbolProperty.firstChildElement( QStringLiteral( "pointsize" ) ).text().toInt();
280  // Just a precaution, checking for 0
281  if ( pointSize != 0 )
282  {
283  // int r = (s-2*lw)/2-1 --> 2r = (s-2*lw)-2 --> 2r+2 = s-2*lw
284  // --> 2r+2+2*lw = s
285  // where '2r' is the old size.
286  pointSize = pointSize + 2 + 2 * lineWidth;
287  QgsDebugMsg( QStringLiteral( "Setting point size to %1" ).arg( pointSize ) );
288  QDomElement newPointSizeProperty = pft->dom().createElement( QStringLiteral( "pointsize" ) );
289  const QDomText newPointSizeTxt = pft->dom().createTextNode( QString::number( pointSize ) );
290  newPointSizeProperty.appendChild( newPointSizeTxt );
291  symbolProperty.replaceChild( newPointSizeProperty, pointSymbol );
292  }
293  }
294  }
295 
296  }
297 }
298 
300 {
301  if ( ! pft->dom().isNull() )
302  {
303 #ifndef QT_NO_PRINTER
304  //Change 'outlinewidth' in QgsSymbol
305  const QPrinter myPrinter( QPrinter::ScreenResolution );
306  const int screenDpi = myPrinter.resolution();
307  const double widthScaleFactor = 25.4 / screenDpi;
308 
309  const QDomNodeList outlineWidthList = pft->dom().elementsByTagName( QStringLiteral( "outlinewidth" ) );
310  for ( int i = 0; i < outlineWidthList.size(); ++i )
311  {
312  //calculate new width
313  QDomElement currentOutlineElem = outlineWidthList.at( i ).toElement();
314  double outlineWidth = currentOutlineElem.text().toDouble();
315  outlineWidth *= widthScaleFactor;
316 
317  //replace old text node
318  const QDomNode outlineTextNode = currentOutlineElem.firstChild();
319  const QDomText newOutlineText = pft->dom().createTextNode( QString::number( outlineWidth ) );
320  currentOutlineElem.replaceChild( newOutlineText, outlineTextNode );
321 
322  }
323 
324  //Change 'pointsize' in QgsSymbol
325  const QDomNodeList pointSizeList = pft->dom().elementsByTagName( QStringLiteral( "pointsize" ) );
326  for ( int i = 0; i < pointSizeList.size(); ++i )
327  {
328  //calculate new size
329  QDomElement currentPointSizeElem = pointSizeList.at( i ).toElement();
330  double pointSize = currentPointSizeElem.text().toDouble();
331  pointSize *= widthScaleFactor;
332 
333  //replace old text node
334  const QDomNode pointSizeTextNode = currentPointSizeElem.firstChild();
335  const QDomText newPointSizeText = pft->dom().createTextNode( QString::number( static_cast< int >( pointSize ) ) );
336  currentPointSizeElem.replaceChild( newPointSizeText, pointSizeTextNode );
337  }
338 #endif
339  }
340 }
341 
343 {
344  if ( ! pft->dom().isNull() )
345  {
346  const QDomNodeList layerList = pft->dom().elementsByTagName( QStringLiteral( "maplayer" ) );
347  for ( int i = 0; i < layerList.size(); ++i )
348  {
349  const QDomElement layerElem = layerList.at( i ).toElement();
350  const QString typeString = layerElem.attribute( QStringLiteral( "type" ) );
351  if ( typeString != QLatin1String( "vector" ) )
352  {
353  continue;
354  }
355 
356  //datasource
357  const QDomNode dataSourceNode = layerElem.namedItem( QStringLiteral( "datasource" ) );
358  if ( dataSourceNode.isNull() )
359  {
360  return;
361  }
362  const QString dataSource = dataSourceNode.toElement().text();
363 
364  //provider key
365  const QDomNode providerNode = layerElem.namedItem( QStringLiteral( "provider" ) );
366  if ( providerNode.isNull() )
367  {
368  return;
369  }
370  const QString providerKey = providerNode.toElement().text();
371 
372  //create the layer to get the provider for int->fieldName conversion
374  options.loadDefaultStyle = false;
375  QgsVectorLayer *layer = new QgsVectorLayer( dataSource, QString(), providerKey, options );
376  if ( !layer->isValid() )
377  {
378  delete layer;
379  return;
380  }
381 
382  QgsVectorDataProvider *provider = layer->dataProvider();
383  if ( !provider )
384  {
385  return;
386  }
387  const QgsFields fields = provider->fields();
388 
389  //read classificationfield
390  const QDomNodeList classificationFieldList = layerElem.elementsByTagName( QStringLiteral( "classificationfield" ) );
391  for ( int j = 0; j < classificationFieldList.size(); ++j )
392  {
393  QDomElement classificationFieldElem = classificationFieldList.at( j ).toElement();
394  const int fieldNumber = classificationFieldElem.text().toInt();
395  if ( fieldNumber >= 0 && fieldNumber < fields.count() )
396  {
397  const QDomText fieldName = pft->dom().createTextNode( fields.at( fieldNumber ).name() );
398  const QDomNode nameNode = classificationFieldElem.firstChild();
399  classificationFieldElem.replaceChild( fieldName, nameNode );
400  }
401  }
402 
403  }
404  }
405 }
406 
408 {
409  QgsDebugMsg( QStringLiteral( "Entering..." ) );
410  if ( pft->dom().isNull() )
411  return;
412 
413  const QDomNode qgis = pft->dom().firstChildElement( QStringLiteral( "qgis" ) );
414  if ( qgis.isNull() )
415  return;
416 
417  const QDomElement properties = qgis.firstChildElement( QStringLiteral( "properties" ) );
418  if ( properties.isNull() )
419  return;
420 
421  QDomElement digitizing = properties.firstChildElement( QStringLiteral( "Digitizing" ) );
422  if ( digitizing.isNull() )
423  return;
424 
425  const QDomElement tolList = digitizing.firstChildElement( QStringLiteral( "LayerSnappingToleranceList" ) );
426  if ( tolList.isNull() )
427  return;
428 
429  const QDomElement tolUnitList = digitizing.firstChildElement( QStringLiteral( "LayerSnappingToleranceUnitList" ) );
430  if ( !tolUnitList.isNull() )
431  return;
432 
433  QStringList units;
434  for ( int i = 0; i < tolList.childNodes().count(); i++ )
435  units << QStringLiteral( "0" );
436 
437  QgsProjectPropertyValue value( units );
438  value.writeXml( QStringLiteral( "LayerSnappingToleranceUnitList" ), digitizing, pft->dom() );
439 }
440 
442 {
443  //Adapt the XML description of the composer legend model to version 1.5
444  if ( pft->dom().isNull() )
445  {
446  return;
447  }
448  //Add layer id to <VectorClassificationItem>
449  const QDomNodeList layerItemList = pft->dom().elementsByTagName( QStringLiteral( "LayerItem" ) );
450  QDomElement currentLayerItemElem;
451  QString currentLayerId;
452 
453  for ( int i = 0; i < layerItemList.size(); ++i )
454  {
455  currentLayerItemElem = layerItemList.at( i ).toElement();
456  if ( currentLayerItemElem.isNull() )
457  {
458  continue;
459  }
460  currentLayerId = currentLayerItemElem.attribute( QStringLiteral( "layerId" ) );
461 
462  const QDomNodeList vectorClassificationList = currentLayerItemElem.elementsByTagName( QStringLiteral( "VectorClassificationItem" ) );
463  QDomElement currentClassificationElem;
464  for ( int j = 0; j < vectorClassificationList.size(); ++j )
465  {
466  currentClassificationElem = vectorClassificationList.at( j ).toElement();
467  if ( !currentClassificationElem.isNull() )
468  {
469  currentClassificationElem.setAttribute( QStringLiteral( "layerId" ), currentLayerId );
470  }
471  }
472 
473  //replace the text items with VectorClassification or RasterClassification items
474  const QDomNodeList textItemList = currentLayerItemElem.elementsByTagName( QStringLiteral( "TextItem" ) );
475  QDomElement currentTextItem;
476 
477  for ( int j = 0; j < textItemList.size(); ++j )
478  {
479  currentTextItem = textItemList.at( j ).toElement();
480  if ( currentTextItem.isNull() )
481  {
482  continue;
483  }
484 
485  QDomElement classificationElement;
486  if ( !vectorClassificationList.isEmpty() ) //we guess it is a vector layer
487  {
488  classificationElement = pft->dom().createElement( QStringLiteral( "VectorClassificationItem" ) );
489  }
490  else
491  {
492  classificationElement = pft->dom().createElement( QStringLiteral( "RasterClassificationItem" ) );
493  }
494 
495  classificationElement.setAttribute( QStringLiteral( "layerId" ), currentLayerId );
496  classificationElement.setAttribute( QStringLiteral( "text" ), currentTextItem.attribute( QStringLiteral( "text" ) ) );
497  currentLayerItemElem.replaceChild( classificationElement, currentTextItem );
498  }
499  }
500 }
501 
503 {
504  if ( pft->dom().isNull() )
505  {
506  return;
507  }
508 
509  QgsReadWriteContext context;
510  context.setPathResolver( QgsProject::instance()->pathResolver() );
511 
512  const QDomNodeList layerItemList = pft->dom().elementsByTagName( QStringLiteral( "rasterproperties" ) );
513  for ( int i = 0; i < layerItemList.size(); ++i )
514  {
515  QDomElement rasterPropertiesElem = layerItemList.at( i ).toElement();
516  QDomNode layerNode = rasterPropertiesElem.parentNode();
517  const QDomElement dataSourceElem = layerNode.firstChildElement( QStringLiteral( "datasource" ) );
518  const QDomElement layerNameElem = layerNode.firstChildElement( QStringLiteral( "layername" ) );
519  QgsRasterLayer rasterLayer;
520  // TODO: We have to use more data from project file to read the layer it correctly,
521  // OTOH, we should not read it until it was converted
522  rasterLayer.readLayerXml( layerNode.toElement(), context );
523  pft->convertRasterProperties( pft->dom(), layerNode, rasterPropertiesElem, &rasterLayer );
524  }
525 
526  //composer: replace mGridAnnotationPosition with mLeftGridAnnotationPosition & co.
527  // and mGridAnnotationDirection with mLeftGridAnnotationDirection & co.
528  const QDomNodeList composerMapList = pft->dom().elementsByTagName( QStringLiteral( "ComposerMap" ) );
529  for ( int i = 0; i < composerMapList.size(); ++i )
530  {
531  const QDomNodeList gridList = composerMapList.at( i ).toElement().elementsByTagName( QStringLiteral( "Grid" ) );
532  for ( int j = 0; j < gridList.size(); ++j )
533  {
534  const QDomNodeList annotationList = gridList.at( j ).toElement().elementsByTagName( QStringLiteral( "Annotation" ) );
535  for ( int k = 0; k < annotationList.size(); ++k )
536  {
537  QDomElement annotationElem = annotationList.at( k ).toElement();
538 
539  //position
540  if ( annotationElem.hasAttribute( QStringLiteral( "position" ) ) )
541  {
542  const int pos = annotationElem.attribute( QStringLiteral( "position" ) ).toInt();
543  annotationElem.setAttribute( QStringLiteral( "leftPosition" ), pos );
544  annotationElem.setAttribute( QStringLiteral( "rightPosition" ), pos );
545  annotationElem.setAttribute( QStringLiteral( "topPosition" ), pos );
546  annotationElem.setAttribute( QStringLiteral( "bottomPosition" ), pos );
547  annotationElem.removeAttribute( QStringLiteral( "position" ) );
548  }
549 
550  //direction
551  if ( annotationElem.hasAttribute( QStringLiteral( "direction" ) ) )
552  {
553  const int dir = annotationElem.attribute( QStringLiteral( "direction" ) ).toInt();
554  if ( dir == 2 )
555  {
556  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), 0 );
557  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), 0 );
558  annotationElem.setAttribute( QStringLiteral( "topDirection" ), 1 );
559  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), 1 );
560  }
561  else if ( dir == 3 )
562  {
563  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), 1 );
564  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), 1 );
565  annotationElem.setAttribute( QStringLiteral( "topDirection" ), 0 );
566  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), 0 );
567  }
568  else
569  {
570  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), dir );
571  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), dir );
572  annotationElem.setAttribute( QStringLiteral( "topDirection" ), dir );
573  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), dir );
574  }
575  annotationElem.removeAttribute( QStringLiteral( "direction" ) );
576  }
577  }
578  }
579  }
580 
581  //Composer: move all items under Composition element
582  const QDomNodeList composerList = pft->dom().elementsByTagName( QStringLiteral( "Composer" ) );
583  for ( int i = 0; i < composerList.size(); ++i )
584  {
585  QDomElement composerElem = composerList.at( i ).toElement();
586 
587  //find <QgsComposition element
588  QDomElement compositionElem = composerElem.firstChildElement( QStringLiteral( "Composition" ) );
589  if ( compositionElem.isNull() )
590  {
591  continue;
592  }
593 
594  const QDomNodeList composerChildren = composerElem.childNodes();
595 
596  if ( composerChildren.size() < 1 )
597  {
598  continue;
599  }
600 
601  for ( int j = composerChildren.size() - 1; j >= 0; --j )
602  {
603  const QDomElement childElem = composerChildren.at( j ).toElement();
604  if ( childElem.tagName() == QLatin1String( "Composition" ) )
605  {
606  continue;
607  }
608 
609  composerElem.removeChild( childElem );
610  compositionElem.appendChild( childElem );
611 
612  }
613  }
614 
615  // SimpleFill symbol layer v2: avoid double transparency
616  // replacing alpha value of symbol layer's color with 255 (the
617  // transparency value is already stored as symbol transparency).
618  const QDomNodeList rendererList = pft->dom().elementsByTagName( QStringLiteral( "renderer-v2" ) );
619  for ( int i = 0; i < rendererList.size(); ++i )
620  {
621  const QDomNodeList layerList = rendererList.at( i ).toElement().elementsByTagName( QStringLiteral( "layer" ) );
622  for ( int j = 0; j < layerList.size(); ++j )
623  {
624  const QDomElement layerElem = layerList.at( j ).toElement();
625  if ( layerElem.attribute( QStringLiteral( "class" ) ) == QLatin1String( "SimpleFill" ) )
626  {
627  const QDomNodeList propList = layerElem.elementsByTagName( QStringLiteral( "prop" ) );
628  for ( int k = 0; k < propList.size(); ++k )
629  {
630  QDomElement propElem = propList.at( k ).toElement();
631  if ( propElem.attribute( QStringLiteral( "k" ) ) == QLatin1String( "color" ) || propElem.attribute( QStringLiteral( "k" ) ) == QLatin1String( "color_border" ) )
632  {
633  propElem.setAttribute( QStringLiteral( "v" ), propElem.attribute( QStringLiteral( "v" ) ).section( ',', 0, 2 ) + ",255" );
634  }
635  }
636  }
637  }
638  }
639 
640  QgsDebugMsgLevel( pft->dom().toString(), 2 );
641 }
642 
644 {
645  //composer: set placement for all picture items to middle, to mimic <=2.2 behavior
646  const QDomNodeList composerPictureList = pft->dom().elementsByTagName( QStringLiteral( "ComposerPicture" ) );
647  for ( int i = 0; i < composerPictureList.size(); ++i )
648  {
649  QDomElement picture = composerPictureList.at( i ).toElement();
650  picture.setAttribute( QStringLiteral( "anchorPoint" ), QString::number( 4 ) );
651  }
652 }
653 
655 {
656  // transform OTF off to "no projection" for project
657  QDomElement propsElem = pft->dom().firstChildElement( QStringLiteral( "qgis" ) ).toElement().firstChildElement( QStringLiteral( "properties" ) );
658  if ( !propsElem.isNull() )
659  {
660  const QDomNodeList srsNodes = propsElem.elementsByTagName( QStringLiteral( "SpatialRefSys" ) );
661  QDomElement srsElem;
662  QDomElement projElem;
663  if ( srsNodes.count() > 0 )
664  {
665  srsElem = srsNodes.at( 0 ).toElement();
666  const QDomNodeList projNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectionsEnabled" ) );
667  if ( projNodes.count() == 0 )
668  {
669  projElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
670  projElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
671  const QDomText projText = pft->dom().createTextNode( QStringLiteral( "0" ) );
672  projElem.appendChild( projText );
673  srsElem.appendChild( projElem );
674  }
675  }
676  else
677  {
678  srsElem = pft->dom().createElement( QStringLiteral( "SpatialRefSys" ) );
679  projElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
680  projElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
681  const QDomText projText = pft->dom().createTextNode( QStringLiteral( "0" ) );
682  projElem.appendChild( projText );
683  srsElem.appendChild( projElem );
684  propsElem.appendChild( srsElem );
685  }
686 
687  // transform map canvas CRS to project CRS - this is because project CRS was inconsistently used
688  // prior to 3.0. In >= 3.0 main canvas CRS is forced to match project CRS, so we need to make
689  // sure we can read the project CRS correctly
690  const QDomNodeList canvasNodes = pft->dom().elementsByTagName( QStringLiteral( "mapcanvas" ) );
691  if ( canvasNodes.count() > 0 )
692  {
693  const QDomElement canvasElem = canvasNodes.at( 0 ).toElement();
694  const QDomNodeList canvasSrsNodes = canvasElem.elementsByTagName( QStringLiteral( "spatialrefsys" ) );
695  if ( canvasSrsNodes.count() > 0 )
696  {
697  const QDomElement canvasSrsElem = canvasSrsNodes.at( 0 ).toElement();
698  QString proj;
699  QString authid;
700  QString srsid;
701 
702  const QDomNodeList proj4Nodes = canvasSrsElem.elementsByTagName( QStringLiteral( "proj4" ) );
703  if ( proj4Nodes.count() > 0 )
704  {
705  const QDomElement proj4Node = proj4Nodes.at( 0 ).toElement();
706  proj = proj4Node.text();
707  }
708  const QDomNodeList authidNodes = canvasSrsElem.elementsByTagName( QStringLiteral( "authid" ) );
709  if ( authidNodes.count() > 0 )
710  {
711  const QDomElement authidNode = authidNodes.at( 0 ).toElement();
712  authid = authidNode.text();
713  }
714  const QDomNodeList srsidNodes = canvasSrsElem.elementsByTagName( QStringLiteral( "srsid" ) );
715  if ( srsidNodes.count() > 0 )
716  {
717  const QDomElement srsidNode = srsidNodes.at( 0 ).toElement();
718  srsid = srsidNode.text();
719  }
720 
721  // clear existing project CRS nodes
722  const QDomNodeList oldProjectProj4Nodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCRSProj4String" ) );
723  for ( int i = oldProjectProj4Nodes.count(); i >= 0; --i )
724  {
725  srsElem.removeChild( oldProjectProj4Nodes.at( i ) );
726  }
727  const QDomNodeList oldProjectCrsNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCrs" ) );
728  for ( int i = oldProjectCrsNodes.count(); i >= 0; --i )
729  {
730  srsElem.removeChild( oldProjectCrsNodes.at( i ) );
731  }
732  const QDomNodeList oldProjectCrsIdNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCRSID" ) );
733  for ( int i = oldProjectCrsIdNodes.count(); i >= 0; --i )
734  {
735  srsElem.removeChild( oldProjectCrsIdNodes.at( i ) );
736  }
737  const QDomNodeList projectionsEnabledNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectionsEnabled" ) );
738  for ( int i = projectionsEnabledNodes.count(); i >= 0; --i )
739  {
740  srsElem.removeChild( projectionsEnabledNodes.at( i ) );
741  }
742 
743  QDomElement proj4Elem = pft->dom().createElement( QStringLiteral( "ProjectCRSProj4String" ) );
744  proj4Elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QString" ) );
745  const QDomText proj4Text = pft->dom().createTextNode( proj );
746  proj4Elem.appendChild( proj4Text );
747  QDomElement projectCrsElem = pft->dom().createElement( QStringLiteral( "ProjectCrs" ) );
748  projectCrsElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QString" ) );
749  const QDomText projectCrsText = pft->dom().createTextNode( authid );
750  projectCrsElem.appendChild( projectCrsText );
751  QDomElement projectCrsIdElem = pft->dom().createElement( QStringLiteral( "ProjectCRSID" ) );
752  projectCrsIdElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
753  const QDomText srsidText = pft->dom().createTextNode( srsid );
754  projectCrsIdElem.appendChild( srsidText );
755  QDomElement projectionsEnabledElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
756  projectionsEnabledElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
757  const QDomText projectionsEnabledText = pft->dom().createTextNode( QStringLiteral( "1" ) );
758  projectionsEnabledElem.appendChild( projectionsEnabledText );
759  srsElem.appendChild( proj4Elem );
760  srsElem.appendChild( projectCrsElem );
761  srsElem.appendChild( projectCrsIdElem );
762  srsElem.appendChild( projectionsEnabledElem );
763 
764  const QDomNodeList srsNodes = propsElem.elementsByTagName( QStringLiteral( "SpatialRefSys" ) );
765  for ( int i = srsNodes.count(); i >= 0; --i )
766  {
767  propsElem.removeChild( srsNodes.at( i ) );
768  }
769  propsElem.appendChild( srsElem );
770  }
771  }
772  }
773 
774 
775  const QDomNodeList mapLayers = pft->dom().elementsByTagName( QStringLiteral( "maplayer" ) );
776 
777  for ( int mapLayerIndex = 0; mapLayerIndex < mapLayers.count(); ++mapLayerIndex )
778  {
779  QDomElement layerElem = mapLayers.at( mapLayerIndex ).toElement();
780 
781  // The newly added fieldConfiguration element
782  QDomElement fieldConfigurationElement = pft->dom().createElement( QStringLiteral( "fieldConfiguration" ) );
783  layerElem.appendChild( fieldConfigurationElement );
784 
785  const QDomNodeList editTypeNodes = layerElem.namedItem( QStringLiteral( "edittypes" ) ).childNodes();
786  QDomElement constraintExpressionsElem = pft->dom().createElement( QStringLiteral( "constraintExpressions" ) );
787  layerElem.appendChild( constraintExpressionsElem );
788 
789  for ( int i = 0; i < editTypeNodes.size(); ++i )
790  {
791  const QDomNode editTypeNode = editTypeNodes.at( i );
792  const QDomElement editTypeElement = editTypeNode.toElement();
793 
794  QDomElement fieldElement = pft->dom().createElement( QStringLiteral( "field" ) );
795  fieldConfigurationElement.appendChild( fieldElement );
796 
797  const QString name = editTypeElement.attribute( QStringLiteral( "name" ) );
798  fieldElement.setAttribute( QStringLiteral( "name" ), name );
799  QDomElement constraintExpressionElem = pft->dom().createElement( QStringLiteral( "constraint" ) );
800  constraintExpressionElem.setAttribute( QStringLiteral( "field" ), name );
801  constraintExpressionsElem.appendChild( constraintExpressionElem );
802 
803  QDomElement editWidgetElement = pft->dom().createElement( QStringLiteral( "editWidget" ) );
804  fieldElement.appendChild( editWidgetElement );
805 
806  const QString ewv2Type = editTypeElement.attribute( QStringLiteral( "widgetv2type" ) );
807  editWidgetElement.setAttribute( QStringLiteral( "type" ), ewv2Type );
808 
809  const QDomElement ewv2CfgElem = editTypeElement.namedItem( QStringLiteral( "widgetv2config" ) ).toElement();
810 
811  if ( !ewv2CfgElem.isNull() )
812  {
813  QDomElement editWidgetConfigElement = pft->dom().createElement( QStringLiteral( "config" ) );
814  editWidgetElement.appendChild( editWidgetConfigElement );
815 
816  QVariantMap editWidgetConfiguration;
817 
818  const QDomNamedNodeMap configAttrs = ewv2CfgElem.attributes();
819  for ( int configIndex = 0; configIndex < configAttrs.count(); ++configIndex )
820  {
821  const QDomAttr configAttr = configAttrs.item( configIndex ).toAttr();
822  if ( configAttr.name() == QLatin1String( "fieldEditable" ) )
823  {
824  editWidgetConfigElement.setAttribute( QStringLiteral( "fieldEditable" ), configAttr.value() );
825  }
826  else if ( configAttr.name() == QLatin1String( "labelOnTop" ) )
827  {
828  editWidgetConfigElement.setAttribute( QStringLiteral( "labelOnTop" ), configAttr.value() );
829  }
830  else if ( configAttr.name() == QLatin1String( "notNull" ) )
831  {
832  editWidgetConfigElement.setAttribute( QStringLiteral( "notNull" ), configAttr.value() );
833  }
834  else if ( configAttr.name() == QLatin1String( "constraint" ) )
835  {
836  constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), configAttr.value() );
837  }
838  else if ( configAttr.name() == QLatin1String( "constraintDescription" ) )
839  {
840  constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), configAttr.value() );
841  }
842  else
843  {
844  editWidgetConfiguration.insert( configAttr.name(), configAttr.value() );
845  }
846  }
847 
848  if ( ewv2Type == QLatin1String( "ValueMap" ) )
849  {
850  const QDomNodeList configElements = ewv2CfgElem.childNodes();
851  QVariantMap map;
852  for ( int configIndex = 0; configIndex < configElements.count(); ++configIndex )
853  {
854  const QDomElement configElem = configElements.at( configIndex ).toElement();
855  map.insert( configElem.attribute( QStringLiteral( "key" ) ), configElem.attribute( QStringLiteral( "value" ) ) );
856  }
857  editWidgetConfiguration.insert( QStringLiteral( "map" ), map );
858  }
859  else if ( ewv2Type == QLatin1String( "Photo" ) )
860  {
861  editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
862 
863  editWidgetConfiguration.insert( QStringLiteral( "DocumentViewer" ), 1 );
864  editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerHeight" ), editWidgetConfiguration.value( QStringLiteral( "Height" ) ) );
865  editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerWidth" ), editWidgetConfiguration.value( QStringLiteral( "Width" ) ) );
866  editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
867  }
868  else if ( ewv2Type == QLatin1String( "FileName" ) )
869  {
870  editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
871 
872  editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
873  }
874  else if ( ewv2Type == QLatin1String( "WebView" ) )
875  {
876  editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
877 
878  editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerHeight" ), editWidgetConfiguration.value( QStringLiteral( "Height" ) ) );
879  editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerWidth" ), editWidgetConfiguration.value( QStringLiteral( "Width" ) ) );
880  editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
881  }
882 
883  editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( editWidgetConfiguration, pft->dom() ) );
884  }
885  }
886  }
887 }
888 
889 void QgsProjectFileTransform::convertRasterProperties( QDomDocument &doc, QDomNode &parentNode,
890  QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer )
891 {
892  //no data
893  //TODO: We would need to set no data on all bands, but we don't know number of bands here
894  const QDomNode noDataNode = rasterPropertiesElem.namedItem( QStringLiteral( "mNoDataValue" ) );
895  const QDomElement noDataElement = noDataNode.toElement();
896  if ( !noDataElement.text().isEmpty() )
897  {
898  QgsDebugMsg( "mNoDataValue = " + noDataElement.text() );
899  QDomElement noDataElem = doc.createElement( QStringLiteral( "noData" ) );
900 
901  QDomElement noDataRangeList = doc.createElement( QStringLiteral( "noDataRangeList" ) );
902  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), 1 );
903 
904  QDomElement noDataRange = doc.createElement( QStringLiteral( "noDataRange" ) );
905  noDataRange.setAttribute( QStringLiteral( "min" ), noDataElement.text() );
906  noDataRange.setAttribute( QStringLiteral( "max" ), noDataElement.text() );
907  noDataRangeList.appendChild( noDataRange );
908 
909  noDataElem.appendChild( noDataRangeList );
910 
911  parentNode.appendChild( noDataElem );
912  }
913 
914  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
915  //convert general properties
916 
917  //invert color
918  rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "0" ) );
919  const QDomElement invertColorElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "mInvertColor" ) );
920  if ( !invertColorElem.isNull() )
921  {
922  if ( invertColorElem.text() == QLatin1String( "true" ) )
923  {
924  rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "1" ) );
925  }
926  }
927 
928  //opacity
929  rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) );
930  const QDomElement transparencyElem = parentNode.firstChildElement( QStringLiteral( "transparencyLevelInt" ) );
931  if ( !transparencyElem.isNull() )
932  {
933  const double transparency = transparencyElem.text().toInt();
934  rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QString::number( transparency / 255.0 ) );
935  }
936 
937  //alphaBand was not saved until now (bug)
938  rasterRendererElem.setAttribute( QStringLiteral( "alphaBand" ), -1 );
939 
940  //gray band is used for several renderers
941  const int grayBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGrayBandName" ), rlayer );
942 
943  //convert renderer specific properties
944  QString drawingStyle = rasterPropertiesElem.firstChildElement( QStringLiteral( "mDrawingStyle" ) ).text();
945 
946  // While PalettedColor should normally contain only integer values, usually
947  // color palette 0-255, it may happen (Tim, issue #7023) that it contains
948  // colormap classification with double values and text labels
949  // (which should normally only appear in SingleBandPseudoColor drawingStyle)
950  // => we have to check first the values and change drawingStyle if necessary
951  if ( drawingStyle == QLatin1String( "PalettedColor" ) )
952  {
953  const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
954  const QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
955 
956  for ( int i = 0; i < colorRampEntryList.size(); ++i )
957  {
958  const QDomElement colorRampEntryElem = colorRampEntryList.at( i ).toElement();
959  const QString strValue = colorRampEntryElem.attribute( QStringLiteral( "value" ) );
960  const double value = strValue.toDouble();
961  if ( value < 0 || value > 10000 || !qgsDoubleNear( value, static_cast< int >( value ) ) )
962  {
963  QgsDebugMsg( QStringLiteral( "forcing SingleBandPseudoColor value = %1" ).arg( value ) );
964  drawingStyle = QStringLiteral( "SingleBandPseudoColor" );
965  break;
966  }
967  }
968  }
969 
970  if ( drawingStyle == QLatin1String( "SingleBandGray" ) )
971  {
972  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandgray" ) );
973  rasterRendererElem.setAttribute( QStringLiteral( "grayBand" ), grayBand );
974  transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
975  }
976  else if ( drawingStyle == QLatin1String( "SingleBandPseudoColor" ) )
977  {
978  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandpseudocolor" ) );
979  rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
980  QDomElement newRasterShaderElem = doc.createElement( QStringLiteral( "rastershader" ) );
981  QDomElement newColorRampShaderElem = doc.createElement( QStringLiteral( "colorrampshader" ) );
982  newRasterShaderElem.appendChild( newColorRampShaderElem );
983  rasterRendererElem.appendChild( newRasterShaderElem );
984 
985  //switch depending on mColorShadingAlgorithm
986  const QString colorShadingAlgorithm = rasterPropertiesElem.firstChildElement( QStringLiteral( "mColorShadingAlgorithm" ) ).text();
987  if ( colorShadingAlgorithm == QLatin1String( "PseudoColorShader" ) || colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
988  {
989  newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), QStringLiteral( "INTERPOLATED" ) );
990 
991  //get minmax from rasterlayer
992  const QgsRasterBandStats rasterBandStats = rlayer->dataProvider()->bandStatistics( grayBand );
993  const double minValue = rasterBandStats.minimumValue;
994  const double maxValue = rasterBandStats.maximumValue;
995  const double breakSize = ( maxValue - minValue ) / 3;
996 
997  QStringList colorList;
998  if ( colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
999  {
1000  colorList << QStringLiteral( "#ff00ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ff0000" ) << QStringLiteral( "#00ff00" );
1001  }
1002  else //pseudocolor
1003  {
1004  colorList << QStringLiteral( "#0000ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ffff00" ) << QStringLiteral( "#ff0000" );
1005  }
1006  QStringList::const_iterator colorIt = colorList.constBegin();
1007  double boundValue = minValue;
1008  for ( ; colorIt != colorList.constEnd(); ++colorIt )
1009  {
1010  QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
1011  newItemElem.setAttribute( QStringLiteral( "value" ), QString::number( boundValue ) );
1012  newItemElem.setAttribute( QStringLiteral( "label" ), QString::number( boundValue ) );
1013  newItemElem.setAttribute( QStringLiteral( "color" ), *colorIt );
1014  newColorRampShaderElem.appendChild( newItemElem );
1015  boundValue += breakSize;
1016  }
1017  }
1018  else if ( colorShadingAlgorithm == QLatin1String( "ColorRampShader" ) )
1019  {
1020  const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
1021  const QString type = customColorRampElem.firstChildElement( QStringLiteral( "colorRampType" ) ).text();
1022  newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), type );
1023  const QDomNodeList colorNodeList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
1024 
1025  QString value, label;
1026  QColor newColor;
1027  int red, green, blue;
1028  QDomElement currentItemElem;
1029  for ( int i = 0; i < colorNodeList.size(); ++i )
1030  {
1031  currentItemElem = colorNodeList.at( i ).toElement();
1032  value = currentItemElem.attribute( QStringLiteral( "value" ) );
1033  label = currentItemElem.attribute( QStringLiteral( "label" ) );
1034  red = currentItemElem.attribute( QStringLiteral( "red" ) ).toInt();
1035  green = currentItemElem.attribute( QStringLiteral( "green" ) ).toInt();
1036  blue = currentItemElem.attribute( QStringLiteral( "blue" ) ).toInt();
1037  newColor = QColor( red, green, blue );
1038  QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
1039  newItemElem.setAttribute( QStringLiteral( "value" ), value );
1040  newItemElem.setAttribute( QStringLiteral( "label" ), label );
1041  newItemElem.setAttribute( QStringLiteral( "color" ), newColor.name() );
1042  newColorRampShaderElem.appendChild( newItemElem );
1043  }
1044  }
1045  }
1046  else if ( drawingStyle == QLatin1String( "PalettedColor" ) )
1047  {
1048  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "paletted" ) );
1049  rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
1050  const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
1051  const QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
1052  QDomElement newColorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
1053 
1054  int red = 0;
1055  int green = 0;
1056  int blue = 0;
1057  int value = 0;
1058  QDomElement colorRampEntryElem;
1059  for ( int i = 0; i < colorRampEntryList.size(); ++i )
1060  {
1061  colorRampEntryElem = colorRampEntryList.at( i ).toElement();
1062  QDomElement newPaletteElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
1063  value = static_cast< int >( colorRampEntryElem.attribute( QStringLiteral( "value" ) ).toDouble() );
1064  newPaletteElem.setAttribute( QStringLiteral( "value" ), value );
1065  red = colorRampEntryElem.attribute( QStringLiteral( "red" ) ).toInt();
1066  green = colorRampEntryElem.attribute( QStringLiteral( "green" ) ).toInt();
1067  blue = colorRampEntryElem.attribute( QStringLiteral( "blue" ) ).toInt();
1068  newPaletteElem.setAttribute( QStringLiteral( "color" ), QColor( red, green, blue ).name() );
1069  const QString label = colorRampEntryElem.attribute( QStringLiteral( "label" ) );
1070  if ( !label.isEmpty() )
1071  {
1072  newPaletteElem.setAttribute( QStringLiteral( "label" ), label );
1073  }
1074  newColorPaletteElem.appendChild( newPaletteElem );
1075  }
1076  rasterRendererElem.appendChild( newColorPaletteElem );
1077  }
1078  else if ( drawingStyle == QLatin1String( "MultiBandColor" ) )
1079  {
1080  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "multibandcolor" ) );
1081 
1082  //red band, green band, blue band
1083  const int redBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mRedBandName" ), rlayer );
1084  const int greenBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGreenBandName" ), rlayer );
1085  const int blueBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mBlueBandName" ), rlayer );
1086  rasterRendererElem.setAttribute( QStringLiteral( "redBand" ), redBand );
1087  rasterRendererElem.setAttribute( QStringLiteral( "greenBand" ), greenBand );
1088  rasterRendererElem.setAttribute( QStringLiteral( "blueBand" ), blueBand );
1089 
1090  transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
1091  }
1092  else
1093  {
1094  return;
1095  }
1096 
1097  //replace rasterproperties element with rasterrenderer element
1098  if ( !parentNode.isNull() )
1099  {
1100  parentNode.replaceChild( rasterRendererElem, rasterPropertiesElem );
1101  }
1102 }
1103 
1105 {
1106  return mDom;
1107 }
1108 
1110 {
1111  return mCurrentVersion;
1112 }
1113 
1114 int rasterBandNumber( const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer )
1115 {
1116  if ( !rlayer )
1117  {
1118  return -1;
1119  }
1120 
1121  const int band = -1;
1122  const QDomElement rasterBandElem = rasterPropertiesElem.firstChildElement( bandName );
1123  if ( !rasterBandElem.isNull() )
1124  {
1125  const thread_local QRegularExpression re( "(\\d+)" );
1126  const QRegularExpressionMatch match = re.match( rasterBandElem.text() );
1127  if ( match.hasMatch() )
1128  {
1129  return match.captured( 1 ).toInt();
1130  }
1131  }
1132  return band;
1133 }
1134 
1135 void transformContrastEnhancement( QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem )
1136 {
1137  if ( rasterproperties.isNull() || rendererElem.isNull() )
1138  {
1139  return;
1140  }
1141 
1142  double minimumValue = 0;
1143  double maximumValue = 0;
1144  const QDomElement contrastMinMaxElem = rasterproperties.firstChildElement( QStringLiteral( "contrastEnhancementMinMaxValues" ) );
1145  if ( contrastMinMaxElem.isNull() )
1146  {
1147  return;
1148  }
1149 
1150  const QDomElement contrastEnhancementAlgorithmElem = rasterproperties.firstChildElement( QStringLiteral( "mContrastEnhancementAlgorithm" ) );
1151  if ( contrastEnhancementAlgorithmElem.isNull() )
1152  {
1153  return;
1154  }
1155 
1156  //convert enhancement name to enumeration
1157  int algorithmEnum = 0;
1158  const QString algorithmString = contrastEnhancementAlgorithmElem.text();
1159  if ( algorithmString == QLatin1String( "StretchToMinimumMaximum" ) )
1160  {
1161  algorithmEnum = 1;
1162  }
1163  else if ( algorithmString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
1164  {
1165  algorithmEnum = 2;
1166  }
1167  else if ( algorithmString == QLatin1String( "ClipToMinimumMaximum" ) )
1168  {
1169  algorithmEnum = 3;
1170  }
1171  else if ( algorithmString == QLatin1String( "UserDefinedEnhancement" ) )
1172  {
1173  algorithmEnum = 4;
1174  }
1175 
1176  const QDomNodeList minMaxEntryList = contrastMinMaxElem.elementsByTagName( QStringLiteral( "minMaxEntry" ) );
1177  QStringList enhancementNameList;
1178  if ( minMaxEntryList.size() == 1 )
1179  {
1180  enhancementNameList << QStringLiteral( "contrastEnhancement" );
1181  }
1182  if ( minMaxEntryList.size() == 3 )
1183  {
1184  enhancementNameList << QStringLiteral( "redContrastEnhancement" ) << QStringLiteral( "greenContrastEnhancement" ) << QStringLiteral( "blueContrastEnhancement" );
1185  }
1186  if ( minMaxEntryList.size() > enhancementNameList.size() )
1187  {
1188  return;
1189  }
1190 
1191  QDomElement minMaxEntryElem;
1192  for ( int i = 0; i < minMaxEntryList.size(); ++i )
1193  {
1194  minMaxEntryElem = minMaxEntryList.at( i ).toElement();
1195  const QDomElement minElem = minMaxEntryElem.firstChildElement( QStringLiteral( "min" ) );
1196  if ( minElem.isNull() )
1197  {
1198  return;
1199  }
1200  minimumValue = minElem.text().toDouble();
1201 
1202  const QDomElement maxElem = minMaxEntryElem.firstChildElement( QStringLiteral( "max" ) );
1203  if ( maxElem.isNull() )
1204  {
1205  return;
1206  }
1207  maximumValue = maxElem.text().toDouble();
1208 
1209  QDomElement newContrastEnhancementElem = doc.createElement( enhancementNameList.at( i ) );
1210  QDomElement newMinValElem = doc.createElement( QStringLiteral( "minValue" ) );
1211  const QDomText minText = doc.createTextNode( QString::number( minimumValue ) );
1212  newMinValElem.appendChild( minText );
1213  newContrastEnhancementElem.appendChild( newMinValElem );
1214  QDomElement newMaxValElem = doc.createElement( QStringLiteral( "maxValue" ) );
1215  const QDomText maxText = doc.createTextNode( QString::number( maximumValue ) );
1216  newMaxValElem.appendChild( maxText );
1217  newContrastEnhancementElem.appendChild( newMaxValElem );
1218 
1219  QDomElement newAlgorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
1220  const QDomText newAlgorithmText = doc.createTextNode( QString::number( algorithmEnum ) );
1221  newAlgorithmElem.appendChild( newAlgorithmText );
1222  newContrastEnhancementElem.appendChild( newAlgorithmElem );
1223 
1224  rendererElem.appendChild( newContrastEnhancementElem );
1225  }
1226 }
Contains information about the context in which a coordinate transform is executed.
QString name
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
bool isValid
Definition: qgsmaplayer.h:81
Class to convert from older project file versions to newer.
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
QgsProjectVersion currentVersion() const
The current project version.
void dump()
Prints the contents via QgsDebugMsg()
QDomDocument & dom()
The current dom document.
bool updateRevision(const QgsProjectVersion &version)
Project property value node, contains a QgsProjectPropertyKey's value.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
A class to describe the version of a project.
int subVersion() const
Returns the sub version number.
int majorVersion() const
Returns the major version number.
int minorVersion() const
Returns the minor version number.
bool isNull() const
Returns true if this is a NULL project version.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
The RasterBandStats struct is a container for statistics about a single raster band.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
The class is used as a container of context for various read/write operations on other objects.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
This is the base class for vector data providers.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
Represents a vector layer which manages a vector based data sets.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void transform1800to1900(QgsProjectFileTransform *pft)
void transformContrastEnhancement(QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem)
void transform0110to1000(QgsProjectFileTransform *pft)
void transform1100to1200(QgsProjectFileTransform *pft)
void transform0100to0110(QgsProjectFileTransform *pft)
void transform3000(QgsProjectFileTransform *pft)
void transform2200to2300(QgsProjectFileTransform *pft)
void transform1400to1500(QgsProjectFileTransform *pft)
void transformNull(QgsProjectFileTransform *pft)
QgsProjectVersion PFV
void transform091to0100(QgsProjectFileTransform *pft)
std::vector< TransformItem > Transformers
void transform081to090(QgsProjectFileTransform *pft)
int rasterBandNumber(const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer)
void transformRasterTransparency(QDomDocument &doc, const QDomElement &orig, QDomElement &rendererElem)
Setting options for loading vector layers.
QgsProjectVersion from
QgsProjectVersion to