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