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