QGIS API Documentation  2.99.0-Master (6a61179)
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 "qgsvectordataprovider.h"
24 #include "qgsvectorlayer.h"
25 #include <QTextStream>
26 #include <QDomDocument>
27 #include <QPrinter> //to find out screen resolution
28 #include <cstdlib>
29 #include "qgsproject.h"
30 #include "qgsprojectproperty.h"
31 #include "qgsrasterbandstats.h"
32 #include "qgsrasterdataprovider.h"
33 
35 
36 
37 QgsProjectFileTransform::transform QgsProjectFileTransform::transformers[] =
38 {
39  {PFV( 0, 8, 0 ), PFV( 0, 8, 1 ), &QgsProjectFileTransform::transformNull},
40  {PFV( 0, 8, 1 ), PFV( 0, 9, 0 ), &QgsProjectFileTransform::transform081to090},
41  {PFV( 0, 9, 0 ), PFV( 0, 9, 1 ), &QgsProjectFileTransform::transformNull},
42  {PFV( 0, 9, 1 ), PFV( 0, 10, 0 ), &QgsProjectFileTransform::transform091to0100},
43  // Following line is a hack that takes us straight from 0.9.2 to 0.11.0
44  // due to an unknown bug in migrating 0.9.2 files which we didnt pursue (TS & GS)
45  {PFV( 0, 9, 2 ), PFV( 0, 11, 0 ), &QgsProjectFileTransform::transformNull},
46  {PFV( 0, 10, 0 ), PFV( 0, 11, 0 ), &QgsProjectFileTransform::transform0100to0110},
47  {PFV( 0, 11, 0 ), PFV( 1, 0, 0 ), &QgsProjectFileTransform::transform0110to1000},
48  {PFV( 1, 0, 0 ), PFV( 1, 1, 0 ), &QgsProjectFileTransform::transformNull},
49  {PFV( 1, 0, 2 ), PFV( 1, 1, 0 ), &QgsProjectFileTransform::transformNull},
50  {PFV( 1, 1, 0 ), PFV( 1, 2, 0 ), &QgsProjectFileTransform::transform1100to1200},
51  {PFV( 1, 2, 0 ), PFV( 1, 3, 0 ), &QgsProjectFileTransform::transformNull},
52  {PFV( 1, 3, 0 ), PFV( 1, 4, 0 ), &QgsProjectFileTransform::transformNull},
53  {PFV( 1, 4, 0 ), PFV( 1, 5, 0 ), &QgsProjectFileTransform::transform1400to1500},
54  {PFV( 1, 5, 0 ), PFV( 1, 6, 0 ), &QgsProjectFileTransform::transformNull},
55  {PFV( 1, 6, 0 ), PFV( 1, 7, 0 ), &QgsProjectFileTransform::transformNull},
56  {PFV( 1, 7, 0 ), PFV( 1, 8, 0 ), &QgsProjectFileTransform::transformNull},
57  {PFV( 1, 8, 0 ), PFV( 1, 9, 0 ), &QgsProjectFileTransform::transform1800to1900},
58  {PFV( 1, 9, 0 ), PFV( 2, 0, 0 ), &QgsProjectFileTransform::transformNull},
59  {PFV( 2, 0, 0 ), PFV( 2, 1, 0 ), &QgsProjectFileTransform::transformNull},
60  {PFV( 2, 1, 0 ), PFV( 2, 2, 0 ), &QgsProjectFileTransform::transformNull},
61  {PFV( 2, 2, 0 ), PFV( 2, 3, 0 ), &QgsProjectFileTransform::transform2200to2300},
62 };
63 
65 {
66  Q_UNUSED( newVersion );
67  bool returnValue = false;
68 
69  if ( ! mDom.isNull() )
70  {
71  for ( std::size_t i = 0; i < sizeof( transformers ) / sizeof( transform ); i++ )
72  {
73  if ( transformers[i].from == mCurrentVersion )
74  {
75  // Run the transformer, and update the revision in every case
76  ( this->*( transformers[i].transformFunc ) )();
77  mCurrentVersion = transformers[i].to;
78  returnValue = true;
79  }
80  }
81  }
82  return returnValue;
83 }
84 
86 {
87  QgsDebugMsg( QString( "Current project file version is %1.%2.%3" )
88  .arg( mCurrentVersion.majorVersion() )
89  .arg( mCurrentVersion.minorVersion() )
90  .arg( mCurrentVersion.subVersion() ) );
91 #ifdef QGISDEBUG
92  // Using QgsDebugMsg() didn't print the entire mDom...
93  std::cout << mDom.toString( 2 ).toLatin1().constData(); // OK
94 #endif
95 }
96 
97 /*
98  * Transformers below!
99  */
100 
101 void QgsProjectFileTransform::transform081to090()
102 {
103  QgsDebugMsg( "Entering..." );
104  if ( ! mDom.isNull() )
105  {
106  // Start with inserting a mapcanvas element and populate it
107 
108  QDomElement mapCanvas; // A null element.
109 
110  // there should only be one <qgis>
111  QDomNode qgis = mDom.firstChildElement( QStringLiteral( "qgis" ) );
112  if ( ! qgis.isNull() )
113  {
114  QgsDebugMsg( "Populating new mapcanvas" );
115 
116  // Create a mapcanvas
117  mapCanvas = mDom.createElement( QStringLiteral( "mapcanvas" ) );
118  // Append mapcanvas to parent 'qgis'.
119  qgis.appendChild( mapCanvas );
120  // Re-parent units
121  mapCanvas.appendChild( qgis.namedItem( QStringLiteral( "units" ) ) );
122  // Re-parent extent
123  mapCanvas.appendChild( qgis.namedItem( QStringLiteral( "extent" ) ) );
124 
125  // See if we can find if projection is on.
126 
127  QDomElement properties = qgis.firstChildElement( QStringLiteral( "properties" ) );
128  QDomElement spatial = properties.firstChildElement( QStringLiteral( "SpatialRefSys" ) );
129  QDomElement hasCrsTransformEnabled = spatial.firstChildElement( QStringLiteral( "ProjectionsEnabled" ) );
130  // Type is 'int', and '1' if on.
131  // Create an element
132  QDomElement projection = mDom.createElement( QStringLiteral( "projections" ) );
133  QgsDebugMsg( QString( "Projection flag: " ) + hasCrsTransformEnabled.text() );
134  // Set flag from ProjectionsEnabled
135  projection.appendChild( mDom.createTextNode( hasCrsTransformEnabled.text() ) );
136  // Set new element as child of <mapcanvas>
137  mapCanvas.appendChild( projection );
138 
139  }
140 
141 
142  // Transforming coordinate-transforms
143  // Create a list of all map layers
144  QDomNodeList mapLayers = mDom.elementsByTagName( QStringLiteral( "maplayer" ) );
145  bool doneDestination = false;
146  for ( int i = 0; i < mapLayers.count(); i++ )
147  {
148  QDomNode mapLayer = mapLayers.item( i );
149  // Find the coordinatetransform
150  QDomNode coordinateTransform = mapLayer.namedItem( QStringLiteral( "coordinatetransform" ) );
151  // Find the sourcesrs
152  QDomNode sourceCrs = coordinateTransform.namedItem( QStringLiteral( "sourcesrs" ) );
153  // Rename to srs
154  sourceCrs.toElement().setTagName( QStringLiteral( "srs" ) );
155  // Re-parent to maplayer
156  mapLayer.appendChild( sourceCrs );
157  // Re-move coordinatetransform
158  // Take the destination CRS of the first layer and use for mapcanvas projection
159  if ( ! doneDestination )
160  {
161  // Use destination CRS from the last layer
162  QDomNode destinationCRS = coordinateTransform.namedItem( QStringLiteral( "destinationsrs" ) );
163  // Re-parent the destination CRS to the mapcanvas
164  // If mapcanvas wasn't set, nothing will happen.
165  mapCanvas.appendChild( destinationCRS );
166  // Only do this once
167  doneDestination = true;
168  }
169  mapLayer.removeChild( coordinateTransform );
170  //QDomNode id = mapLayer.namedItem("id");
171  //QgsDebugMsg(QString("Found maplayer ") + id.toElement().text());
172 
173  }
174 
175  // Set the flag 'visible' to match the status of 'checked'
176  QDomNodeList legendLayerFiles = mDom.elementsByTagName( QStringLiteral( "legendlayerfile" ) );
177  QgsDebugMsg( QString( "Legend layer file entries: " ) + QString::number( legendLayerFiles.count() ) );
178  for ( int i = 0; i < mapLayers.count(); i++ )
179  {
180  // Get one maplayer element from list
181  QDomElement mapLayer = mapLayers.item( i ).toElement();
182  // Find it's id.
183  QString id = mapLayer.firstChildElement( QStringLiteral( "id" ) ).text();
184  QgsDebugMsg( QString( "Handling layer " + id ) );
185  // Now, look it up in legend
186  for ( int j = 0; j < legendLayerFiles.count(); j++ )
187  {
188  QDomElement legendLayerFile = legendLayerFiles.item( j ).toElement();
189  if ( id == legendLayerFile.attribute( QStringLiteral( "layerid" ) ) )
190  {
191  // Found a the legend layer that matches the maplayer
192  QgsDebugMsg( "Found matching id" );
193 
194  // Set visible flag from maplayer to legendlayer
195  legendLayerFile.setAttribute( QStringLiteral( "visible" ), mapLayer.attribute( QStringLiteral( "visible" ) ) );
196 
197  // Set overview flag from maplayer to legendlayer
198  legendLayerFile.setAttribute( QStringLiteral( "isInOverview" ), mapLayer.attribute( QStringLiteral( "showInOverviewFlag" ) ) );
199  }
200  }
201  }
202  }
203  return;
204 
205 }
206 
207 void QgsProjectFileTransform::transform091to0100()
208 {
209  if ( ! mDom.isNull() )
210  {
211  // Insert transforms here!
212  QDomNodeList rasterPropertyList = mDom.elementsByTagName( QStringLiteral( "rasterproperties" ) );
213  QgsDebugMsg( QString( "Raster properties file entries: " ) + QString::number( rasterPropertyList.count() ) );
214  for ( int i = 0; i < rasterPropertyList.count(); i++ )
215  {
216  // Get one rasterproperty element from list, and rename the sub-properties.
217  QDomNode rasterProperty = rasterPropertyList.item( i );
218  // rasterProperty.namedItem("").toElement().setTagName("");
219 
220  rasterProperty.namedItem( QStringLiteral( "stdDevsToPlotDouble" ) ).toElement().setTagName( QStringLiteral( "mStandardDeviations" ) );
221 
222  rasterProperty.namedItem( QStringLiteral( "invertHistogramFlag" ) ).toElement().setTagName( QStringLiteral( "mInvertPixelsFlag" ) );
223  rasterProperty.namedItem( QStringLiteral( "showDebugOverLayFlag" ) ).toElement().setTagName( QStringLiteral( "mDebugOverLayFlag" ) );
224 
225  rasterProperty.namedItem( QStringLiteral( "redBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mRedBandName" ) );
226  rasterProperty.namedItem( QStringLiteral( "blueBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mBlueBandName" ) );
227  rasterProperty.namedItem( QStringLiteral( "greenBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mGreenBandName" ) );
228  rasterProperty.namedItem( QStringLiteral( "grayBandNameQString" ) ).toElement().setTagName( QStringLiteral( "mGrayBandName" ) );
229  }
230 
231  // Changing symbol size for hard: symbols
232  QDomNodeList symbolPropertyList = mDom.elementsByTagName( QStringLiteral( "symbol" ) );
233  for ( int i = 0; i < symbolPropertyList.count(); i++ )
234  {
235  // Get the <poinmtsymbol> to check for 'hard:' for each <symbol>
236  QDomNode symbolProperty = symbolPropertyList.item( i );
237 
238  QDomElement pointSymbol = symbolProperty.firstChildElement( QStringLiteral( "pointsymbol" ) );
239  if ( pointSymbol.text().startsWith( QLatin1String( "hard:" ) ) )
240  {
241  // Get pointsize and line width
242  int lineWidth = symbolProperty.firstChildElement( QStringLiteral( "outlinewidth" ) ).text().toInt();
243  int pointSize = symbolProperty.firstChildElement( QStringLiteral( "pointsize" ) ).text().toInt();
244  // Just a precaution, checking for 0
245  if ( pointSize != 0 )
246  {
247  // int r = (s-2*lw)/2-1 --> 2r = (s-2*lw)-2 --> 2r+2 = s-2*lw
248  // --> 2r+2+2*lw = s
249  // where '2r' is the old size.
250  pointSize = pointSize + 2 + 2 * lineWidth;
251  QgsDebugMsg( QString( "Setting point size to %1" ).arg( pointSize ) );
252  QDomElement newPointSizeProperty = mDom.createElement( QStringLiteral( "pointsize" ) );
253  QDomText newPointSizeTxt = mDom.createTextNode( QString::number( pointSize ) );
254  newPointSizeProperty.appendChild( newPointSizeTxt );
255  symbolProperty.replaceChild( newPointSizeProperty, pointSymbol );
256  }
257  }
258  }
259 
260  }
261  return;
262 
263 }
264 
265 void QgsProjectFileTransform::transform0100to0110()
266 {
267  if ( ! mDom.isNull() )
268  {
269  //Change 'outlinewidth' in QgsSymbol
270  QPrinter myPrinter( QPrinter::ScreenResolution );
271  int screenDpi = myPrinter.resolution();
272  double widthScaleFactor = 25.4 / screenDpi;
273 
274  QDomNodeList outlineWidthList = mDom.elementsByTagName( QStringLiteral( "outlinewidth" ) );
275  for ( int i = 0; i < outlineWidthList.size(); ++i )
276  {
277  //calculate new width
278  QDomElement currentOutlineElem = outlineWidthList.at( i ).toElement();
279  double outlineWidth = currentOutlineElem.text().toDouble();
280  outlineWidth *= widthScaleFactor;
281 
282  //replace old text node
283  QDomNode outlineTextNode = currentOutlineElem.firstChild();
284  QDomText newOutlineText = mDom.createTextNode( QString::number( outlineWidth ) );
285  currentOutlineElem.replaceChild( newOutlineText, outlineTextNode );
286 
287  }
288 
289  //Change 'pointsize' in QgsSymbol
290  QDomNodeList pointSizeList = mDom.elementsByTagName( QStringLiteral( "pointsize" ) );
291  for ( int i = 0; i < pointSizeList.size(); ++i )
292  {
293  //calculate new size
294  QDomElement currentPointSizeElem = pointSizeList.at( i ).toElement();
295  double pointSize = currentPointSizeElem.text().toDouble();
296  pointSize *= widthScaleFactor;
297 
298  //replace old text node
299  QDomNode pointSizeTextNode = currentPointSizeElem.firstChild();
300  QDomText newPointSizeText = mDom.createTextNode( QString::number( static_cast< int >( pointSize ) ) );
301  currentPointSizeElem.replaceChild( newPointSizeText, pointSizeTextNode );
302  }
303  }
304 }
305 
306 void QgsProjectFileTransform::transform0110to1000()
307 {
308  if ( ! mDom.isNull() )
309  {
310  QDomNodeList layerList = mDom.elementsByTagName( QStringLiteral( "maplayer" ) );
311  for ( int i = 0; i < layerList.size(); ++i )
312  {
313  QDomElement layerElem = layerList.at( i ).toElement();
314  QString typeString = layerElem.attribute( QStringLiteral( "type" ) );
315  if ( typeString != QLatin1String( "vector" ) )
316  {
317  continue;
318  }
319 
320  //datasource
321  QDomNode dataSourceNode = layerElem.namedItem( QStringLiteral( "datasource" ) );
322  if ( dataSourceNode.isNull() )
323  {
324  return;
325  }
326  QString dataSource = dataSourceNode.toElement().text();
327 
328  //provider key
329  QDomNode providerNode = layerElem.namedItem( QStringLiteral( "provider" ) );
330  if ( providerNode.isNull() )
331  {
332  return;
333  }
334  QString providerKey = providerNode.toElement().text();
335 
336  //create the layer to get the provider for int->fieldName conversion
337  QgsVectorLayer* theLayer = new QgsVectorLayer( dataSource, QLatin1String( "" ), providerKey, false );
338  if ( !theLayer->isValid() )
339  {
340  delete theLayer;
341  return;
342  }
343 
344  QgsVectorDataProvider* theProvider = theLayer->dataProvider();
345  if ( !theProvider )
346  {
347  return;
348  }
349  QgsFields theFields = theProvider->fields();
350 
351  //read classificationfield
352  QDomNodeList classificationFieldList = layerElem.elementsByTagName( QStringLiteral( "classificationfield" ) );
353  for ( int j = 0; j < classificationFieldList.size(); ++j )
354  {
355  QDomElement classificationFieldElem = classificationFieldList.at( j ).toElement();
356  int fieldNumber = classificationFieldElem.text().toInt();
357  if ( fieldNumber >= 0 && fieldNumber < theFields.count() )
358  {
359  QDomText fieldName = mDom.createTextNode( theFields.at( fieldNumber ).name() );
360  QDomNode nameNode = classificationFieldElem.firstChild();
361  classificationFieldElem.replaceChild( fieldName, nameNode );
362  }
363  }
364 
365  }
366  }
367 }
368 
369 void QgsProjectFileTransform::transform1100to1200()
370 {
371  QgsDebugMsg( "Entering..." );
372  if ( mDom.isNull() )
373  return;
374 
375  QDomNode qgis = mDom.firstChildElement( QStringLiteral( "qgis" ) );
376  if ( qgis.isNull() )
377  return;
378 
379  QDomElement properties = qgis.firstChildElement( QStringLiteral( "properties" ) );
380  if ( properties.isNull() )
381  return;
382 
383  QDomElement digitizing = properties.firstChildElement( QStringLiteral( "Digitizing" ) );
384  if ( digitizing.isNull() )
385  return;
386 
387  QDomElement tolList = digitizing.firstChildElement( QStringLiteral( "LayerSnappingToleranceList" ) );
388  if ( tolList.isNull() )
389  return;
390 
391  QDomElement tolUnitList = digitizing.firstChildElement( QStringLiteral( "LayerSnappingToleranceUnitList" ) );
392  if ( !tolUnitList.isNull() )
393  return;
394 
395  QStringList units;
396  for ( int i = 0; i < tolList.childNodes().count(); i++ )
397  units << QStringLiteral( "0" );
398 
399  QgsPropertyValue value( units );
400  value.writeXml( QStringLiteral( "LayerSnappingToleranceUnitList" ), digitizing, mDom );
401 }
402 
403 void QgsProjectFileTransform::transform1400to1500()
404 {
405  //Adapt the XML description of the composer legend model to version 1.5
406  if ( mDom.isNull() )
407  {
408  return;
409  }
410  //Add layer id to <VectorClassificationItem>
411  QDomNodeList layerItemList = mDom.elementsByTagName( QStringLiteral( "LayerItem" ) );
412  QDomElement currentLayerItemElem;
413  QString currentLayerId;
414 
415  for ( int i = 0; i < layerItemList.size(); ++i )
416  {
417  currentLayerItemElem = layerItemList.at( i ).toElement();
418  if ( currentLayerItemElem.isNull() )
419  {
420  continue;
421  }
422  currentLayerId = currentLayerItemElem.attribute( QStringLiteral( "layerId" ) );
423 
424  QDomNodeList vectorClassificationList = currentLayerItemElem.elementsByTagName( QStringLiteral( "VectorClassificationItem" ) );
425  QDomElement currentClassificationElem;
426  for ( int j = 0; j < vectorClassificationList.size(); ++j )
427  {
428  currentClassificationElem = vectorClassificationList.at( j ).toElement();
429  if ( !currentClassificationElem.isNull() )
430  {
431  currentClassificationElem.setAttribute( QStringLiteral( "layerId" ), currentLayerId );
432  }
433  }
434 
435  //replace the text items with VectorClassification or RasterClassification items
436  QDomNodeList textItemList = currentLayerItemElem.elementsByTagName( QStringLiteral( "TextItem" ) );
437  QDomElement currentTextItem;
438 
439  for ( int j = 0; j < textItemList.size(); ++j )
440  {
441  currentTextItem = textItemList.at( j ).toElement();
442  if ( currentTextItem.isNull() )
443  {
444  continue;
445  }
446 
447  QDomElement classificationElement;
448  if ( !vectorClassificationList.isEmpty() ) //we guess it is a vector layer
449  {
450  classificationElement = mDom.createElement( QStringLiteral( "VectorClassificationItem" ) );
451  }
452  else
453  {
454  classificationElement = mDom.createElement( QStringLiteral( "RasterClassificationItem" ) );
455  }
456 
457  classificationElement.setAttribute( QStringLiteral( "layerId" ), currentLayerId );
458  classificationElement.setAttribute( QStringLiteral( "text" ), currentTextItem.attribute( QStringLiteral( "text" ) ) );
459  currentLayerItemElem.replaceChild( classificationElement, currentTextItem );
460  }
461  }
462 }
463 
464 void QgsProjectFileTransform::transform1800to1900()
465 {
466  if ( mDom.isNull() )
467  {
468  return;
469  }
470 
471  QDomNodeList layerItemList = mDom.elementsByTagName( QStringLiteral( "rasterproperties" ) );
472  for ( int i = 0; i < layerItemList.size(); ++i )
473  {
474  QDomElement rasterPropertiesElem = layerItemList.at( i ).toElement();
475  QDomNode layerNode = rasterPropertiesElem.parentNode();
476  QDomElement dataSourceElem = layerNode.firstChildElement( QStringLiteral( "datasource" ) );
477  QDomElement layerNameElem = layerNode.firstChildElement( QStringLiteral( "layername" ) );
478  QgsRasterLayer rasterLayer;
479  // TODO: We have to use more data from project file to read the layer it correctly,
480  // OTOH, we should not read it until it was converted
481  rasterLayer.readLayerXml( layerNode.toElement() );
482  convertRasterProperties( mDom, layerNode, rasterPropertiesElem, &rasterLayer );
483  }
484 
485  //composer: replace mGridAnnotationPosition with mLeftGridAnnotationPosition & co.
486  // and mGridAnnotationDirection with mLeftGridAnnotationDirection & co.
487  QDomNodeList composerMapList = mDom.elementsByTagName( QStringLiteral( "ComposerMap" ) );
488  for ( int i = 0; i < composerMapList.size(); ++i )
489  {
490  QDomNodeList gridList = composerMapList.at( i ).toElement().elementsByTagName( QStringLiteral( "Grid" ) );
491  for ( int j = 0; j < gridList.size(); ++j )
492  {
493  QDomNodeList annotationList = gridList.at( j ).toElement().elementsByTagName( QStringLiteral( "Annotation" ) );
494  for ( int k = 0; k < annotationList.size(); ++k )
495  {
496  QDomElement annotationElem = annotationList.at( k ).toElement();
497 
498  //position
499  if ( annotationElem.hasAttribute( QStringLiteral( "position" ) ) )
500  {
501  int pos = annotationElem.attribute( QStringLiteral( "position" ) ).toInt();
502  annotationElem.setAttribute( QStringLiteral( "leftPosition" ), pos );
503  annotationElem.setAttribute( QStringLiteral( "rightPosition" ), pos );
504  annotationElem.setAttribute( QStringLiteral( "topPosition" ), pos );
505  annotationElem.setAttribute( QStringLiteral( "bottomPosition" ), pos );
506  annotationElem.removeAttribute( QStringLiteral( "position" ) );
507  }
508 
509  //direction
510  if ( annotationElem.hasAttribute( QStringLiteral( "direction" ) ) )
511  {
512  int dir = annotationElem.attribute( QStringLiteral( "direction" ) ).toInt();
513  if ( dir == 2 )
514  {
515  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), 0 );
516  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), 0 );
517  annotationElem.setAttribute( QStringLiteral( "topDirection" ), 1 );
518  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), 1 );
519  }
520  else if ( dir == 3 )
521  {
522  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), 1 );
523  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), 1 );
524  annotationElem.setAttribute( QStringLiteral( "topDirection" ), 0 );
525  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), 0 );
526  }
527  else
528  {
529  annotationElem.setAttribute( QStringLiteral( "leftDirection" ), dir );
530  annotationElem.setAttribute( QStringLiteral( "rightDirection" ), dir );
531  annotationElem.setAttribute( QStringLiteral( "topDirection" ), dir );
532  annotationElem.setAttribute( QStringLiteral( "bottomDirection" ), dir );
533  }
534  annotationElem.removeAttribute( QStringLiteral( "direction" ) );
535  }
536  }
537  }
538  }
539 
540  //Composer: move all items under Composition element
541  QDomNodeList composerList = mDom.elementsByTagName( QStringLiteral( "Composer" ) );
542  for ( int i = 0; i < composerList.size(); ++i )
543  {
544  QDomElement composerElem = composerList.at( i ).toElement();
545 
546  //find <QgsComposition element
547  QDomElement compositionElem = composerElem.firstChildElement( QStringLiteral( "Composition" ) );
548  if ( compositionElem.isNull() )
549  {
550  continue;
551  }
552 
553  QDomNodeList composerChildren = composerElem.childNodes();
554 
555  if ( composerChildren.size() < 1 )
556  {
557  continue;
558  }
559 
560  for ( int j = composerChildren.size() - 1; j >= 0; --j )
561  {
562  QDomElement childElem = composerChildren.at( j ).toElement();
563  if ( childElem.tagName() == QLatin1String( "Composition" ) )
564  {
565  continue;
566  }
567 
568  composerElem.removeChild( childElem );
569  compositionElem.appendChild( childElem );
570 
571  }
572  }
573 
574  // SimpleFill symbol layer v2: avoid double transparency
575  // replacing alpha value of symbol layer's color with 255 (the
576  // transparency value is already stored as symbol transparency).
577  QDomNodeList rendererList = mDom.elementsByTagName( QStringLiteral( "renderer-v2" ) );
578  for ( int i = 0; i < rendererList.size(); ++i )
579  {
580  QDomNodeList layerList = rendererList.at( i ).toElement().elementsByTagName( QStringLiteral( "layer" ) );
581  for ( int j = 0; j < layerList.size(); ++j )
582  {
583  QDomElement layerElem = layerList.at( j ).toElement();
584  if ( layerElem.attribute( QStringLiteral( "class" ) ) == QLatin1String( "SimpleFill" ) )
585  {
586  QDomNodeList propList = layerElem.elementsByTagName( QStringLiteral( "prop" ) );
587  for ( int k = 0; k < propList.size(); ++k )
588  {
589  QDomElement propElem = propList.at( k ).toElement();
590  if ( propElem.attribute( QStringLiteral( "k" ) ) == QLatin1String( "color" ) || propElem.attribute( QStringLiteral( "k" ) ) == QLatin1String( "color_border" ) )
591  {
592  propElem.setAttribute( QStringLiteral( "v" ), propElem.attribute( QStringLiteral( "v" ) ).section( ',', 0, 2 ) + ",255" );
593  }
594  }
595  }
596  }
597  }
598 
599  QgsDebugMsg( mDom.toString() );
600 }
601 
602 void QgsProjectFileTransform::transform2200to2300()
603 {
604  //composer: set placement for all picture items to middle, to mimic <=2.2 behaviour
605  QDomNodeList composerPictureList = mDom.elementsByTagName( QStringLiteral( "ComposerPicture" ) );
606  for ( int i = 0; i < composerPictureList.size(); ++i )
607  {
608  QDomElement picture = composerPictureList.at( i ).toElement();
609  picture.setAttribute( QStringLiteral( "anchorPoint" ), QString::number( 4 ) );
610  }
611 }
612 
613 void QgsProjectFileTransform::convertRasterProperties( QDomDocument& doc, QDomNode& parentNode,
614  QDomElement& rasterPropertiesElem, QgsRasterLayer* rlayer )
615 {
616  //no data
617  //TODO: We would need to set no data on all bands, but we don't know number of bands here
618  QDomNode noDataNode = rasterPropertiesElem.namedItem( QStringLiteral( "mNoDataValue" ) );
619  QDomElement noDataElement = noDataNode.toElement();
620  if ( !noDataElement.text().isEmpty() )
621  {
622  QgsDebugMsg( "mNoDataValue = " + noDataElement.text() );
623  QDomElement noDataElem = doc.createElement( QStringLiteral( "noData" ) );
624 
625  QDomElement noDataRangeList = doc.createElement( QStringLiteral( "noDataRangeList" ) );
626  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), 1 );
627 
628  QDomElement noDataRange = doc.createElement( QStringLiteral( "noDataRange" ) );
629  noDataRange.setAttribute( QStringLiteral( "min" ), noDataElement.text() );
630  noDataRange.setAttribute( QStringLiteral( "max" ), noDataElement.text() );
631  noDataRangeList.appendChild( noDataRange );
632 
633  noDataElem.appendChild( noDataRangeList );
634 
635  parentNode.appendChild( noDataElem );
636  }
637 
638  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
639  //convert general properties
640 
641  //invert color
642  rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "0" ) );
643  QDomElement invertColorElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "mInvertColor" ) );
644  if ( !invertColorElem.isNull() )
645  {
646  if ( invertColorElem.text() == QLatin1String( "true" ) )
647  {
648  rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "1" ) );
649  }
650  }
651 
652  //opacity
653  rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) );
654  QDomElement transparencyElem = parentNode.firstChildElement( QStringLiteral( "transparencyLevelInt" ) );
655  if ( !transparencyElem.isNull() )
656  {
657  double transparency = transparencyElem.text().toInt();
658  rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QString::number( transparency / 255.0 ) );
659  }
660 
661  //alphaBand was not saved until now (bug)
662  rasterRendererElem.setAttribute( QStringLiteral( "alphaBand" ), -1 );
663 
664  //gray band is used for several renderers
665  int grayBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGrayBandName" ), rlayer );
666 
667  //convert renderer specific properties
668  QString drawingStyle = rasterPropertiesElem.firstChildElement( QStringLiteral( "mDrawingStyle" ) ).text();
669 
670  // While PalettedColor should normaly contain only integer values, usually
671  // color palette 0-255, it may happen (Tim, issue #7023) that it contains
672  // colormap classification with double values and text labels
673  // (which should normaly only appear in SingleBandPseudoColor drawingStyle)
674  // => we have to check first the values and change drawingStyle if necessary
675  if ( drawingStyle == QLatin1String( "PalettedColor" ) )
676  {
677  QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
678  QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
679 
680  for ( int i = 0; i < colorRampEntryList.size(); ++i )
681  {
682  QDomElement colorRampEntryElem = colorRampEntryList.at( i ).toElement();
683  QString strValue = colorRampEntryElem.attribute( QStringLiteral( "value" ) );
684  double value = strValue.toDouble();
685  if ( value < 0 || value > 10000 || !qgsDoubleNear( value, static_cast< int >( value ) ) )
686  {
687  QgsDebugMsg( QString( "forcing SingleBandPseudoColor value = %1" ).arg( value ) );
688  drawingStyle = QStringLiteral( "SingleBandPseudoColor" );
689  break;
690  }
691  }
692  }
693 
694  if ( drawingStyle == QLatin1String( "SingleBandGray" ) )
695  {
696  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandgray" ) );
697  rasterRendererElem.setAttribute( QStringLiteral( "grayBand" ), grayBand );
698  transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
699  }
700  else if ( drawingStyle == QLatin1String( "SingleBandPseudoColor" ) )
701  {
702  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandpseudocolor" ) );
703  rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
704  QDomElement newRasterShaderElem = doc.createElement( QStringLiteral( "rastershader" ) );
705  QDomElement newColorRampShaderElem = doc.createElement( QStringLiteral( "colorrampshader" ) );
706  newRasterShaderElem.appendChild( newColorRampShaderElem );
707  rasterRendererElem.appendChild( newRasterShaderElem );
708 
709  //switch depending on mColorShadingAlgorithm
710  QString colorShadingAlgorithm = rasterPropertiesElem.firstChildElement( QStringLiteral( "mColorShadingAlgorithm" ) ).text();
711  if ( colorShadingAlgorithm == QLatin1String( "PseudoColorShader" ) || colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
712  {
713  newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), QStringLiteral( "INTERPOLATED" ) );
714 
715  //get minmax from rasterlayer
716  QgsRasterBandStats rasterBandStats = rlayer->dataProvider()->bandStatistics( grayBand );
717  double minValue = rasterBandStats.minimumValue;
718  double maxValue = rasterBandStats.maximumValue;
719  double breakSize = ( maxValue - minValue ) / 3;
720 
721  QStringList colorList;
722  if ( colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
723  {
724  colorList << QStringLiteral( "#ff00ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ff0000" ) << QStringLiteral( "#00ff00" );
725  }
726  else //pseudocolor
727  {
728  colorList << QStringLiteral( "#0000ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ffff00" ) << QStringLiteral( "#ff0000" );
729  }
730  QStringList::const_iterator colorIt = colorList.constBegin();
731  double boundValue = minValue;
732  for ( ; colorIt != colorList.constEnd(); ++colorIt )
733  {
734  QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
735  newItemElem.setAttribute( QStringLiteral( "value" ), QString::number( boundValue ) );
736  newItemElem.setAttribute( QStringLiteral( "label" ), QString::number( boundValue ) );
737  newItemElem.setAttribute( QStringLiteral( "color" ), *colorIt );
738  newColorRampShaderElem.appendChild( newItemElem );
739  boundValue += breakSize;
740  }
741  }
742  else if ( colorShadingAlgorithm == QLatin1String( "ColorRampShader" ) )
743  {
744  QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
745  QString type = customColorRampElem.firstChildElement( QStringLiteral( "colorRampType" ) ).text();
746  newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), type );
747  QDomNodeList colorNodeList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
748 
749  QString value, label;
750  QColor newColor;
751  int red, green, blue;
752  QDomElement currentItemElem;
753  for ( int i = 0; i < colorNodeList.size(); ++i )
754  {
755  currentItemElem = colorNodeList.at( i ).toElement();
756  value = currentItemElem.attribute( QStringLiteral( "value" ) );
757  label = currentItemElem.attribute( QStringLiteral( "label" ) );
758  red = currentItemElem.attribute( QStringLiteral( "red" ) ).toInt();
759  green = currentItemElem.attribute( QStringLiteral( "green" ) ).toInt();
760  blue = currentItemElem.attribute( QStringLiteral( "blue" ) ).toInt();
761  newColor = QColor( red, green, blue );
762  QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
763  newItemElem.setAttribute( QStringLiteral( "value" ), value );
764  newItemElem.setAttribute( QStringLiteral( "label" ), label );
765  newItemElem.setAttribute( QStringLiteral( "color" ), newColor.name() );
766  newColorRampShaderElem.appendChild( newItemElem );
767  }
768  }
769  }
770  else if ( drawingStyle == QLatin1String( "PalettedColor" ) )
771  {
772  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "paletted" ) );
773  rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
774  QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
775  QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
776  QDomElement newColorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
777 
778  int red = 0;
779  int green = 0;
780  int blue = 0;
781  int value = 0;
782  QDomElement colorRampEntryElem;
783  for ( int i = 0; i < colorRampEntryList.size(); ++i )
784  {
785  colorRampEntryElem = colorRampEntryList.at( i ).toElement();
786  QDomElement newPaletteElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
787  value = static_cast< int >( colorRampEntryElem.attribute( QStringLiteral( "value" ) ).toDouble() );
788  newPaletteElem.setAttribute( QStringLiteral( "value" ), value );
789  red = colorRampEntryElem.attribute( QStringLiteral( "red" ) ).toInt();
790  green = colorRampEntryElem.attribute( QStringLiteral( "green" ) ).toInt();
791  blue = colorRampEntryElem.attribute( QStringLiteral( "blue" ) ).toInt();
792  newPaletteElem.setAttribute( QStringLiteral( "color" ), QColor( red, green, blue ).name() );
793  QString label = colorRampEntryElem.attribute( QStringLiteral( "label" ) );
794  if ( !label.isEmpty() )
795  {
796  newPaletteElem.setAttribute( QStringLiteral( "label" ), label );
797  }
798  newColorPaletteElem.appendChild( newPaletteElem );
799  }
800  rasterRendererElem.appendChild( newColorPaletteElem );
801  }
802  else if ( drawingStyle == QLatin1String( "MultiBandColor" ) )
803  {
804  rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "multibandcolor" ) );
805 
806  //red band, green band, blue band
807  int redBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mRedBandName" ), rlayer );
808  int greenBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGreenBandName" ), rlayer );
809  int blueBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mBlueBandName" ), rlayer );
810  rasterRendererElem.setAttribute( QStringLiteral( "redBand" ), redBand );
811  rasterRendererElem.setAttribute( QStringLiteral( "greenBand" ), greenBand );
812  rasterRendererElem.setAttribute( QStringLiteral( "blueBand" ), blueBand );
813 
814  transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
815  }
816  else
817  {
818  return;
819  }
820 
821  //replace rasterproperties element with rasterrenderer element
822  if ( !parentNode.isNull() )
823  {
824  parentNode.replaceChild( rasterRendererElem, rasterPropertiesElem );
825  }
826 }
827 
828 int QgsProjectFileTransform::rasterBandNumber( const QDomElement &rasterPropertiesElem, const QString &bandName,
829  QgsRasterLayer *rlayer )
830 {
831  if ( !rlayer )
832  {
833  return -1;
834  }
835 
836  int band = -1;
837  QDomElement rasterBandElem = rasterPropertiesElem.firstChildElement( bandName );
838  if ( !rasterBandElem.isNull() )
839  {
840  QRegExp re( "(\\d+)" );
841 
842  if ( re.indexIn( rasterBandElem.text() ) >= 0 )
843  {
844  return re.cap( 1 ).toInt();
845  }
846  }
847  return band;
848 }
849 
850 void QgsProjectFileTransform::transformContrastEnhancement( QDomDocument& doc, const QDomElement& rasterproperties, QDomElement& rendererElem )
851 {
852  if ( rasterproperties.isNull() || rendererElem.isNull() )
853  {
854  return;
855  }
856 
857  double minimumValue = 0;
858  double maximumValue = 0;
859  QDomElement contrastMinMaxElem = rasterproperties.firstChildElement( QStringLiteral( "contrastEnhancementMinMaxValues" ) );
860  if ( contrastMinMaxElem.isNull() )
861  {
862  return;
863  }
864 
865  QDomElement contrastEnhancementAlgorithmElem = rasterproperties.firstChildElement( QStringLiteral( "mContrastEnhancementAlgorithm" ) );
866  if ( contrastEnhancementAlgorithmElem.isNull() )
867  {
868  return;
869  }
870 
871  //convert enhancement name to enumeration
872  int algorithmEnum = 0;
873  QString algorithmString = contrastEnhancementAlgorithmElem.text();
874  if ( algorithmString == QLatin1String( "StretchToMinimumMaximum" ) )
875  {
876  algorithmEnum = 1;
877  }
878  else if ( algorithmString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
879  {
880  algorithmEnum = 2;
881  }
882  else if ( algorithmString == QLatin1String( "ClipToMinimumMaximum" ) )
883  {
884  algorithmEnum = 3;
885  }
886  else if ( algorithmString == QLatin1String( "UserDefinedEnhancement" ) )
887  {
888  algorithmEnum = 4;
889  }
890 
891  QDomNodeList minMaxEntryList = contrastMinMaxElem.elementsByTagName( QStringLiteral( "minMaxEntry" ) );
892  QStringList enhancementNameList;
893  if ( minMaxEntryList.size() == 1 )
894  {
895  enhancementNameList << QStringLiteral( "contrastEnhancement" );
896  }
897  if ( minMaxEntryList.size() == 3 )
898  {
899  enhancementNameList << QStringLiteral( "redContrastEnhancement" ) << QStringLiteral( "greenContrastEnhancement" ) << QStringLiteral( "blueContrastEnhancement" );
900  }
901  if ( minMaxEntryList.size() > enhancementNameList.size() )
902  {
903  return;
904  }
905 
906  QDomElement minMaxEntryElem;
907  for ( int i = 0; i < minMaxEntryList.size(); ++i )
908  {
909  minMaxEntryElem = minMaxEntryList.at( i ).toElement();
910  QDomElement minElem = minMaxEntryElem.firstChildElement( QStringLiteral( "min" ) );
911  if ( minElem.isNull() )
912  {
913  return;
914  }
915  minimumValue = minElem.text().toDouble();
916 
917  QDomElement maxElem = minMaxEntryElem.firstChildElement( QStringLiteral( "max" ) );
918  if ( maxElem.isNull() )
919  {
920  return;
921  }
922  maximumValue = maxElem.text().toDouble();
923 
924  QDomElement newContrastEnhancementElem = doc.createElement( enhancementNameList.at( i ) );
925  QDomElement newMinValElem = doc.createElement( QStringLiteral( "minValue" ) );
926  QDomText minText = doc.createTextNode( QString::number( minimumValue ) );
927  newMinValElem.appendChild( minText );
928  newContrastEnhancementElem.appendChild( newMinValElem );
929  QDomElement newMaxValElem = doc.createElement( QStringLiteral( "maxValue" ) );
930  QDomText maxText = doc.createTextNode( QString::number( maximumValue ) );
931  newMaxValElem.appendChild( maxText );
932  newContrastEnhancementElem.appendChild( newMaxValElem );
933 
934  QDomElement newAlgorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
935  QDomText newAlgorithmText = doc.createTextNode( QString::number( algorithmEnum ) );
936  newAlgorithmElem.appendChild( newAlgorithmText );
937  newContrastEnhancementElem.appendChild( newAlgorithmElem );
938 
939  rendererElem.appendChild( newContrastEnhancementElem );
940  }
941 }
942 
943 void QgsProjectFileTransform::transformRasterTransparency( QDomDocument& doc, const QDomElement& orig, QDomElement& rendererElem )
944 {
945  //soon...
946  Q_UNUSED( doc );
947  Q_UNUSED( orig );
948  Q_UNUSED( rendererElem );
949 }
950 
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
QgsProjectVersion PFV
void dump()
Prints the contents via QgsDebugMsg()
QString name
Definition: qgsfield.h:55
bool readLayerXml(const QDomElement &layerElement)
Sets state from Dom document.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
bool updateRevision(const QgsProjectVersion &version)
QgsPropertyValue node.
double maximumValue
The maximum cell value in the raster band.
Container of fields for a vector layer.
Definition: qgsfields.h:36
bool isValid() const
Return the status of the layer.
int count() const
Return number of items.
Definition: qgsfields.cpp:117
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:196
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:137
virtual QgsRasterBandStats bandStatistics(int theBandNo, int theStats=QgsRasterBandStats::All, const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0)
Get band statistics.
The RasterBandStats struct is a container for statistics about a single raster band.
A class to describe the version of a project.
virtual QgsFields fields() const =0
Returns the fields associated with this data provider.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
keyElement created by parent QgsPropertyKey
double minimumValue
The minimum cell value in the raster band.
QgsRasterDataProvider * dataProvider()
Returns the data provider.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
This is the base class for vector data providers.
Represents a vector layer which manages a vector based data sets.