QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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  QgsProjectFileTransform::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 }
QgsProjectFileTransform::updateRevision
bool updateRevision(const QgsProjectVersion &version)
Definition: qgsprojectfiletransform.cpp:71
qgsrasterbandstats.h
QgsReadWriteContext::setPathResolver
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
Definition: qgsreadwritecontext.cpp:52
transform1400to1500
void transform1400to1500(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:441
QgsCoordinateTransformContext
Contains information about the context in which a coordinate transform is executed.
Definition: qgscoordinatetransformcontext.h:57
qgsrasterlayer.h
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:676
TransformItem
Definition: qgsprojectfiletransform.cpp:62
QgsRasterInterface::bandStatistics
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
Definition: qgsrasterinterface.cpp:116
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
TransformItem::from
QgsProjectVersion from
Definition: qgsprojectfiletransform.cpp:64
QgsRasterBandStats
The RasterBandStats struct is a container for statistics about a single raster band.
Definition: qgsrasterbandstats.h:34
QgsProjectFileTransform::dump
void dump()
Prints the contents via QgsDebugMsg()
Definition: qgsprojectfiletransform.cpp:123
qgsreadwritecontext.h
QgsVectorDataProvider::fields
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsProjectFileTransform::currentVersion
QgsProjectVersion currentVersion() const
The current project version.
Definition: qgsprojectfiletransform.cpp:1109
qgspathresolver.h
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsProjectVersion::majorVersion
int majorVersion() const
Returns the major version number.
Definition: qgsprojectversion.h:68
QgsProjectVersion::isNull
bool isNull() const
Returns true if this is a NULL project version.
Definition: qgsprojectversion.cpp:89
QgsMapLayer::isValid
bool isValid
Definition: qgsmaplayer.h:81
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsProjectVersion::subVersion
int subVersion() const
Returns the sub version number.
Definition: qgsprojectversion.h:78
QgsField::name
QString name
Definition: qgsfield.h:60
transform3000
void transform3000(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:654
TransformItem::to
QgsProjectVersion to
Definition: qgsprojectfiletransform.cpp:65
transform081to090
void transform081to090(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:139
QgsRasterBandStats::maximumValue
double maximumValue
The maximum cell value in the raster band.
Definition: qgsrasterbandstats.h:101
qgsprojectfiletransform.h
transform2200to2300
void transform2200to2300(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:643
QgsProjectFileTransform::dom
QDomDocument & dom()
The current dom document.
Definition: qgsprojectfiletransform.cpp:1104
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2265
transformNull
void transformNull(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:46
transform0110to1000
void transform0110to1000(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:342
transform1800to1900
void transform1800to1900(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:502
qgsvectordataprovider.h
QgsProjectVersion::minorVersion
int minorVersion() const
Returns the minor version number.
Definition: qgsprojectversion.h:73
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:76
transform0100to0110
void transform0100to0110(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:299
qgsxmlutils.h
qgsprojectproperty.h
qgsvectorlayer.h
QgsRasterBandStats::minimumValue
double minimumValue
The minimum cell value in the raster band.
Definition: qgsrasterbandstats.h:107
QgsVectorLayer::LayerOptions
Setting options for loading vector layers.
Definition: qgsvectorlayer.h:408
QgsProjectFileTransform
Class to convert from older project file versions to newer.
Definition: qgsprojectfiletransform.h:37
QgsProjectPropertyValue::writeXml
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
Definition: qgsprojectproperty.cpp:231
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsMapLayer::readLayerXml
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
Definition: qgsmaplayer.cpp:339
PFV
QgsProjectVersion PFV
Definition: qgsprojectfiletransform.cpp:41
rasterBandNumber
int rasterBandNumber(const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer)
Definition: qgsprojectfiletransform.cpp:1114
QgsProjectPropertyValue
Project property value node, contains a QgsProjectPropertyKey's value.
Definition: qgsprojectproperty.h:128
transform091to0100
void transform091to0100(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:243
Transformers
std::vector< TransformItem > Transformers
Definition: qgsprojectfiletransform.cpp:69
transform1100to1200
void transform1100to1200(QgsProjectFileTransform *pft)
Definition: qgsprojectfiletransform.cpp:407
QgsVectorDataProvider
This is the base class for vector data providers.
Definition: qgsvectordataprovider.h:58
qgslogger.h
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
qgsprojectversion.h
QgsXmlUtils::writeVariant
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
Definition: qgsxmlutils.cpp:106
qgsproject.h
QgsProjectVersion
A class to describe the version of a project.
Definition: qgsprojectversion.h:32
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:257
transformRasterTransparency
void transformRasterTransparency(QDomDocument &doc, const QDomElement &orig, QDomElement &rendererElem)
QgsProjectFileTransform::convertRasterProperties
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
Definition: qgsprojectfiletransform.cpp:889
transformContrastEnhancement
void transformContrastEnhancement(QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem)
Definition: qgsprojectfiletransform.cpp:1135
qgsrasterdataprovider.h