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