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