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