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