QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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#include <cstdlib>
41
43
44// Transformer functions below. Declare functions here,
45// define them in qgsprojectfiletransform.cpp and add them
46// to the transformArray with proper version number
47void transformNull( QgsProjectFileTransform *pft ) { Q_UNUSED( pft ) } // Do absolutely nothing
50
51//helper functions
52int rasterBandNumber( const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer );
53void transformContrastEnhancement( QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem );
54void transformRasterTransparency( QDomDocument &doc, const QDomElement &orig, QDomElement &rendererElem );
55
56typedef struct
57{
60 void ( * transformFunc )( QgsProjectFileTransform * );
62
63typedef std::vector<TransformItem> Transformers;
64
66{
67 Q_UNUSED( newVersion )
68 bool returnValue = false;
69
70 static const Transformers transformers(
71 {
72 {PFV( 0, 8, 0 ), PFV( 0, 8, 1 ), &transformNull},
73 {PFV( 0, 8, 1 ), PFV( 0, 9, 0 ), &transformNull},
74 {PFV( 0, 9, 0 ), PFV( 0, 9, 1 ), &transformNull},
75 {PFV( 0, 9, 1 ), PFV( 0, 10, 0 ), &transformNull},
76 // Following line is a hack that takes us straight from 0.9.2 to 0.11.0
77 // due to an unknown bug in migrating 0.9.2 files which we didn't pursue (TS & GS)
78 {PFV( 0, 9, 2 ), PFV( 0, 11, 0 ), &transformNull},
79 {PFV( 0, 10, 0 ), PFV( 0, 11, 0 ), &transformNull},
80 {PFV( 0, 11, 0 ), PFV( 1, 0, 0 ), &transformNull},
81 {PFV( 1, 0, 0 ), PFV( 1, 1, 0 ), &transformNull},
82 {PFV( 1, 0, 2 ), PFV( 1, 1, 0 ), &transformNull},
83 {PFV( 1, 1, 0 ), PFV( 1, 2, 0 ), &transformNull},
84 {PFV( 1, 2, 0 ), PFV( 1, 3, 0 ), &transformNull},
85 {PFV( 1, 3, 0 ), PFV( 1, 4, 0 ), &transformNull},
86 {PFV( 1, 4, 0 ), PFV( 1, 5, 0 ), &transformNull},
87 {PFV( 1, 5, 0 ), PFV( 1, 6, 0 ), &transformNull},
88 {PFV( 1, 6, 0 ), PFV( 1, 7, 0 ), &transformNull},
89 {PFV( 1, 7, 0 ), PFV( 1, 8, 0 ), &transformNull},
90 {PFV( 1, 8, 0 ), PFV( 1, 9, 0 ), &transformNull},
91 {PFV( 1, 9, 0 ), PFV( 2, 0, 0 ), &transformNull},
92 {PFV( 2, 0, 0 ), PFV( 2, 1, 0 ), &transformNull},
93 {PFV( 2, 1, 0 ), PFV( 2, 2, 0 ), &transformNull},
94 {PFV( 2, 2, 0 ), PFV( 2, 3, 0 ), &transform2200to2300},
95 // A transformer with a NULL from version means that it should be run when upgrading
96 // from any version and will take care that it's not going to cause trouble if it's
97 // run several times on the same file.
98 {PFV(), PFV( 3, 0, 0 ), &transform3000},
99 } );
100
101 if ( !mDom.isNull() )
102 {
103 for ( const TransformItem &transformer : transformers )
104 {
105 if ( transformer.to >= mCurrentVersion && ( transformer.from == mCurrentVersion || transformer.from.isNull() ) )
106 {
107 // Run the transformer, and update the revision in every case
108 ( *( transformer.transformFunc ) )( this );
109 mCurrentVersion = transformer.to;
110 returnValue = true;
111 }
112 }
113 }
114 return returnValue;
115}
116
118{
119 QgsDebugMsgLevel( QStringLiteral( "Current project file version is %1.%2.%3" )
120 .arg( mCurrentVersion.majorVersion() )
121 .arg( mCurrentVersion.minorVersion() )
122 .arg( mCurrentVersion.subVersion() ), 1 );
123#ifdef QGISDEBUG
124 // Using QgsDebugMsgLevel() didn't print the entire pft->dom()...
125 std::cout << mDom.toString( 2 ).toLatin1().constData(); // OK
126#endif
127}
128
129/*
130 * Transformers below!
131 */
132
134{
135 //composer: set placement for all picture items to middle, to mimic <=2.2 behavior
136 const QDomNodeList composerPictureList = pft->dom().elementsByTagName( QStringLiteral( "ComposerPicture" ) );
137 for ( int i = 0; i < composerPictureList.size(); ++i )
138 {
139 QDomElement picture = composerPictureList.at( i ).toElement();
140 picture.setAttribute( QStringLiteral( "anchorPoint" ), QString::number( 4 ) );
141 }
142}
143
145{
146 // transform OTF off to "no projection" for project
147 QDomElement propsElem = pft->dom().firstChildElement( QStringLiteral( "qgis" ) ).toElement().firstChildElement( QStringLiteral( "properties" ) );
148 if ( !propsElem.isNull() )
149 {
150 const QDomNodeList srsNodes = propsElem.elementsByTagName( QStringLiteral( "SpatialRefSys" ) );
151 QDomElement srsElem;
152 QDomElement projElem;
153 if ( srsNodes.count() > 0 )
154 {
155 srsElem = srsNodes.at( 0 ).toElement();
156 const QDomNodeList projNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectionsEnabled" ) );
157 if ( projNodes.count() == 0 )
158 {
159 projElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
160 projElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
161 const QDomText projText = pft->dom().createTextNode( QStringLiteral( "0" ) );
162 projElem.appendChild( projText );
163 srsElem.appendChild( projElem );
164 }
165 }
166 else
167 {
168 srsElem = pft->dom().createElement( QStringLiteral( "SpatialRefSys" ) );
169 projElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
170 projElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
171 const QDomText projText = pft->dom().createTextNode( QStringLiteral( "0" ) );
172 projElem.appendChild( projText );
173 srsElem.appendChild( projElem );
174 propsElem.appendChild( srsElem );
175 }
176
177 // transform map canvas CRS to project CRS - this is because project CRS was inconsistently used
178 // prior to 3.0. In >= 3.0 main canvas CRS is forced to match project CRS, so we need to make
179 // sure we can read the project CRS correctly
180 const QDomNodeList canvasNodes = pft->dom().elementsByTagName( QStringLiteral( "mapcanvas" ) );
181 if ( canvasNodes.count() > 0 )
182 {
183 const QDomElement canvasElem = canvasNodes.at( 0 ).toElement();
184 const QDomNodeList canvasSrsNodes = canvasElem.elementsByTagName( QStringLiteral( "spatialrefsys" ) );
185 if ( canvasSrsNodes.count() > 0 )
186 {
187 const QDomElement canvasSrsElem = canvasSrsNodes.at( 0 ).toElement();
188 QString proj;
189 QString authid;
190 QString srsid;
191
192 const QDomNodeList proj4Nodes = canvasSrsElem.elementsByTagName( QStringLiteral( "proj4" ) );
193 if ( proj4Nodes.count() > 0 )
194 {
195 const QDomElement proj4Node = proj4Nodes.at( 0 ).toElement();
196 proj = proj4Node.text();
197 }
198 const QDomNodeList authidNodes = canvasSrsElem.elementsByTagName( QStringLiteral( "authid" ) );
199 if ( authidNodes.count() > 0 )
200 {
201 const QDomElement authidNode = authidNodes.at( 0 ).toElement();
202 authid = authidNode.text();
203 }
204 const QDomNodeList srsidNodes = canvasSrsElem.elementsByTagName( QStringLiteral( "srsid" ) );
205 if ( srsidNodes.count() > 0 )
206 {
207 const QDomElement srsidNode = srsidNodes.at( 0 ).toElement();
208 srsid = srsidNode.text();
209 }
210
211 // clear existing project CRS nodes
212 const QDomNodeList oldProjectProj4Nodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCRSProj4String" ) );
213 for ( int i = oldProjectProj4Nodes.count(); i >= 0; --i )
214 {
215 srsElem.removeChild( oldProjectProj4Nodes.at( i ) );
216 }
217 const QDomNodeList oldProjectCrsNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCrs" ) );
218 for ( int i = oldProjectCrsNodes.count(); i >= 0; --i )
219 {
220 srsElem.removeChild( oldProjectCrsNodes.at( i ) );
221 }
222 const QDomNodeList oldProjectCrsIdNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectCRSID" ) );
223 for ( int i = oldProjectCrsIdNodes.count(); i >= 0; --i )
224 {
225 srsElem.removeChild( oldProjectCrsIdNodes.at( i ) );
226 }
227 const QDomNodeList projectionsEnabledNodes = srsElem.elementsByTagName( QStringLiteral( "ProjectionsEnabled" ) );
228 for ( int i = projectionsEnabledNodes.count(); i >= 0; --i )
229 {
230 srsElem.removeChild( projectionsEnabledNodes.at( i ) );
231 }
232
233 QDomElement proj4Elem = pft->dom().createElement( QStringLiteral( "ProjectCRSProj4String" ) );
234 proj4Elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QString" ) );
235 const QDomText proj4Text = pft->dom().createTextNode( proj );
236 proj4Elem.appendChild( proj4Text );
237 QDomElement projectCrsElem = pft->dom().createElement( QStringLiteral( "ProjectCrs" ) );
238 projectCrsElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QString" ) );
239 const QDomText projectCrsText = pft->dom().createTextNode( authid );
240 projectCrsElem.appendChild( projectCrsText );
241 QDomElement projectCrsIdElem = pft->dom().createElement( QStringLiteral( "ProjectCRSID" ) );
242 projectCrsIdElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
243 const QDomText srsidText = pft->dom().createTextNode( srsid );
244 projectCrsIdElem.appendChild( srsidText );
245 QDomElement projectionsEnabledElem = pft->dom().createElement( QStringLiteral( "ProjectionsEnabled" ) );
246 projectionsEnabledElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "int" ) );
247 const QDomText projectionsEnabledText = pft->dom().createTextNode( QStringLiteral( "1" ) );
248 projectionsEnabledElem.appendChild( projectionsEnabledText );
249 srsElem.appendChild( proj4Elem );
250 srsElem.appendChild( projectCrsElem );
251 srsElem.appendChild( projectCrsIdElem );
252 srsElem.appendChild( projectionsEnabledElem );
253
254 const QDomNodeList srsNodes = propsElem.elementsByTagName( QStringLiteral( "SpatialRefSys" ) );
255 for ( int i = srsNodes.count(); i >= 0; --i )
256 {
257 propsElem.removeChild( srsNodes.at( i ) );
258 }
259 propsElem.appendChild( srsElem );
260 }
261 }
262 }
263
264
265 const QDomNodeList mapLayers = pft->dom().elementsByTagName( QStringLiteral( "maplayer" ) );
266
267 for ( int mapLayerIndex = 0; mapLayerIndex < mapLayers.count(); ++mapLayerIndex )
268 {
269 QDomElement layerElem = mapLayers.at( mapLayerIndex ).toElement();
270
271 // The newly added fieldConfiguration element
272 QDomElement fieldConfigurationElement = pft->dom().createElement( QStringLiteral( "fieldConfiguration" ) );
273 layerElem.appendChild( fieldConfigurationElement );
274
275 const QDomNodeList editTypeNodes = layerElem.namedItem( QStringLiteral( "edittypes" ) ).childNodes();
276 QDomElement constraintExpressionsElem = pft->dom().createElement( QStringLiteral( "constraintExpressions" ) );
277 layerElem.appendChild( constraintExpressionsElem );
278
279 for ( int i = 0; i < editTypeNodes.size(); ++i )
280 {
281 const QDomNode editTypeNode = editTypeNodes.at( i );
282 const QDomElement editTypeElement = editTypeNode.toElement();
283
284 QDomElement fieldElement = pft->dom().createElement( QStringLiteral( "field" ) );
285 fieldConfigurationElement.appendChild( fieldElement );
286
287 const QString name = editTypeElement.attribute( QStringLiteral( "name" ) );
288 fieldElement.setAttribute( QStringLiteral( "name" ), name );
289 QDomElement constraintExpressionElem = pft->dom().createElement( QStringLiteral( "constraint" ) );
290 constraintExpressionElem.setAttribute( QStringLiteral( "field" ), name );
291 constraintExpressionsElem.appendChild( constraintExpressionElem );
292
293 QDomElement editWidgetElement = pft->dom().createElement( QStringLiteral( "editWidget" ) );
294 fieldElement.appendChild( editWidgetElement );
295
296 const QString ewv2Type = editTypeElement.attribute( QStringLiteral( "widgetv2type" ) );
297 editWidgetElement.setAttribute( QStringLiteral( "type" ), ewv2Type );
298
299 const QDomElement ewv2CfgElem = editTypeElement.namedItem( QStringLiteral( "widgetv2config" ) ).toElement();
300
301 if ( !ewv2CfgElem.isNull() )
302 {
303 QDomElement editWidgetConfigElement = pft->dom().createElement( QStringLiteral( "config" ) );
304 editWidgetElement.appendChild( editWidgetConfigElement );
305
306 QVariantMap editWidgetConfiguration;
307
308 const QDomNamedNodeMap configAttrs = ewv2CfgElem.attributes();
309 for ( int configIndex = 0; configIndex < configAttrs.count(); ++configIndex )
310 {
311 const QDomAttr configAttr = configAttrs.item( configIndex ).toAttr();
312 if ( configAttr.name() == QLatin1String( "fieldEditable" ) )
313 {
314 editWidgetConfigElement.setAttribute( QStringLiteral( "fieldEditable" ), configAttr.value() );
315 }
316 else if ( configAttr.name() == QLatin1String( "labelOnTop" ) )
317 {
318 editWidgetConfigElement.setAttribute( QStringLiteral( "labelOnTop" ), configAttr.value() );
319 }
320 else if ( configAttr.name() == QLatin1String( "notNull" ) )
321 {
322 editWidgetConfigElement.setAttribute( QStringLiteral( "notNull" ), configAttr.value() );
323 }
324 else if ( configAttr.name() == QLatin1String( "constraint" ) )
325 {
326 constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), configAttr.value() );
327 }
328 else if ( configAttr.name() == QLatin1String( "constraintDescription" ) )
329 {
330 constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), configAttr.value() );
331 }
332 else
333 {
334 editWidgetConfiguration.insert( configAttr.name(), configAttr.value() );
335 }
336 }
337
338 if ( ewv2Type == QLatin1String( "ValueMap" ) )
339 {
340 const QDomNodeList configElements = ewv2CfgElem.childNodes();
341 QVariantMap map;
342 for ( int configIndex = 0; configIndex < configElements.count(); ++configIndex )
343 {
344 const QDomElement configElem = configElements.at( configIndex ).toElement();
345 map.insert( configElem.attribute( QStringLiteral( "key" ) ), configElem.attribute( QStringLiteral( "value" ) ) );
346 }
347 editWidgetConfiguration.insert( QStringLiteral( "map" ), map );
348 }
349 else if ( ewv2Type == QLatin1String( "Photo" ) )
350 {
351 editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
352
353 editWidgetConfiguration.insert( QStringLiteral( "DocumentViewer" ), 1 );
354 editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerHeight" ), editWidgetConfiguration.value( QStringLiteral( "Height" ) ) );
355 editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerWidth" ), editWidgetConfiguration.value( QStringLiteral( "Width" ) ) );
356 editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
357 }
358 else if ( ewv2Type == QLatin1String( "FileName" ) )
359 {
360 editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
361
362 editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
363 }
364 else if ( ewv2Type == QLatin1String( "WebView" ) )
365 {
366 editWidgetElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ExternalResource" ) );
367
368 editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerHeight" ), editWidgetConfiguration.value( QStringLiteral( "Height" ) ) );
369 editWidgetConfiguration.insert( QStringLiteral( "DocumentViewerWidth" ), editWidgetConfiguration.value( QStringLiteral( "Width" ) ) );
370 editWidgetConfiguration.insert( QStringLiteral( "RelativeStorage" ), 1 );
371 }
372
373 editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( editWidgetConfiguration, pft->dom() ) );
374 }
375 }
376 }
377}
378
379void QgsProjectFileTransform::convertRasterProperties( QDomDocument &doc, QDomNode &parentNode,
380 QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer )
381{
382 //no data
383 //TODO: We would need to set no data on all bands, but we don't know number of bands here
384 const QDomNode noDataNode = rasterPropertiesElem.namedItem( QStringLiteral( "mNoDataValue" ) );
385 const QDomElement noDataElement = noDataNode.toElement();
386 if ( !noDataElement.text().isEmpty() )
387 {
388 QgsDebugMsgLevel( "mNoDataValue = " + noDataElement.text(), 2 );
389 QDomElement noDataElem = doc.createElement( QStringLiteral( "noData" ) );
390
391 QDomElement noDataRangeList = doc.createElement( QStringLiteral( "noDataRangeList" ) );
392 noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), 1 );
393
394 QDomElement noDataRange = doc.createElement( QStringLiteral( "noDataRange" ) );
395 noDataRange.setAttribute( QStringLiteral( "min" ), noDataElement.text() );
396 noDataRange.setAttribute( QStringLiteral( "max" ), noDataElement.text() );
397 noDataRangeList.appendChild( noDataRange );
398
399 noDataElem.appendChild( noDataRangeList );
400
401 parentNode.appendChild( noDataElem );
402 }
403
404 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
405 //convert general properties
406
407 //invert color
408 rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "0" ) );
409 const QDomElement invertColorElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "mInvertColor" ) );
410 if ( !invertColorElem.isNull() )
411 {
412 if ( invertColorElem.text() == QLatin1String( "true" ) )
413 {
414 rasterRendererElem.setAttribute( QStringLiteral( "invertColor" ), QStringLiteral( "1" ) );
415 }
416 }
417
418 //opacity
419 rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QStringLiteral( "1" ) );
420 const QDomElement transparencyElem = parentNode.firstChildElement( QStringLiteral( "transparencyLevelInt" ) );
421 if ( !transparencyElem.isNull() )
422 {
423 const double transparency = transparencyElem.text().toInt();
424 rasterRendererElem.setAttribute( QStringLiteral( "opacity" ), QString::number( transparency / 255.0 ) );
425 }
426
427 //alphaBand was not saved until now (bug)
428 rasterRendererElem.setAttribute( QStringLiteral( "alphaBand" ), -1 );
429
430 //gray band is used for several renderers
431 const int grayBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGrayBandName" ), rlayer );
432
433 //convert renderer specific properties
434 QString drawingStyle = rasterPropertiesElem.firstChildElement( QStringLiteral( "mDrawingStyle" ) ).text();
435
436 // While PalettedColor should normally contain only integer values, usually
437 // color palette 0-255, it may happen (Tim, issue #7023) that it contains
438 // colormap classification with double values and text labels
439 // (which should normally only appear in SingleBandPseudoColor drawingStyle)
440 // => we have to check first the values and change drawingStyle if necessary
441 if ( drawingStyle == QLatin1String( "PalettedColor" ) )
442 {
443 const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
444 const QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
445
446 for ( int i = 0; i < colorRampEntryList.size(); ++i )
447 {
448 const QDomElement colorRampEntryElem = colorRampEntryList.at( i ).toElement();
449 const QString strValue = colorRampEntryElem.attribute( QStringLiteral( "value" ) );
450 const double value = strValue.toDouble();
451 if ( value < 0 || value > 10000 || !qgsDoubleNear( value, static_cast< int >( value ) ) )
452 {
453 QgsDebugMsgLevel( QStringLiteral( "forcing SingleBandPseudoColor value = %1" ).arg( value ), 2 );
454 drawingStyle = QStringLiteral( "SingleBandPseudoColor" );
455 break;
456 }
457 }
458 }
459
460 if ( drawingStyle == QLatin1String( "SingleBandGray" ) )
461 {
462 rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandgray" ) );
463 rasterRendererElem.setAttribute( QStringLiteral( "grayBand" ), grayBand );
464 transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
465 }
466 else if ( drawingStyle == QLatin1String( "SingleBandPseudoColor" ) )
467 {
468 rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singlebandpseudocolor" ) );
469 rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
470 QDomElement newRasterShaderElem = doc.createElement( QStringLiteral( "rastershader" ) );
471 QDomElement newColorRampShaderElem = doc.createElement( QStringLiteral( "colorrampshader" ) );
472 newRasterShaderElem.appendChild( newColorRampShaderElem );
473 rasterRendererElem.appendChild( newRasterShaderElem );
474
475 //switch depending on mColorShadingAlgorithm
476 const QString colorShadingAlgorithm = rasterPropertiesElem.firstChildElement( QStringLiteral( "mColorShadingAlgorithm" ) ).text();
477 if ( colorShadingAlgorithm == QLatin1String( "PseudoColorShader" ) || colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
478 {
479 newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), QStringLiteral( "INTERPOLATED" ) );
480
481 //get minmax from rasterlayer
482 const QgsRasterBandStats rasterBandStats = rlayer->dataProvider()->bandStatistics( grayBand );
483 const double minValue = rasterBandStats.minimumValue;
484 const double maxValue = rasterBandStats.maximumValue;
485 const double breakSize = ( maxValue - minValue ) / 3;
486
487 QStringList colorList;
488 if ( colorShadingAlgorithm == QLatin1String( "FreakOutShader" ) )
489 {
490 colorList << QStringLiteral( "#ff00ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ff0000" ) << QStringLiteral( "#00ff00" );
491 }
492 else //pseudocolor
493 {
494 colorList << QStringLiteral( "#0000ff" ) << QStringLiteral( "#00ffff" ) << QStringLiteral( "#ffff00" ) << QStringLiteral( "#ff0000" );
495 }
496 QStringList::const_iterator colorIt = colorList.constBegin();
497 double boundValue = minValue;
498 for ( ; colorIt != colorList.constEnd(); ++colorIt )
499 {
500 QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
501 newItemElem.setAttribute( QStringLiteral( "value" ), QString::number( boundValue ) );
502 newItemElem.setAttribute( QStringLiteral( "label" ), QString::number( boundValue ) );
503 newItemElem.setAttribute( QStringLiteral( "color" ), *colorIt );
504 newColorRampShaderElem.appendChild( newItemElem );
505 boundValue += breakSize;
506 }
507 }
508 else if ( colorShadingAlgorithm == QLatin1String( "ColorRampShader" ) )
509 {
510 const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
511 const QString type = customColorRampElem.firstChildElement( QStringLiteral( "colorRampType" ) ).text();
512 newColorRampShaderElem.setAttribute( QStringLiteral( "colorRampType" ), type );
513 const QDomNodeList colorNodeList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
514
515 QString value, label;
516 QColor newColor;
517 int red, green, blue;
518 QDomElement currentItemElem;
519 for ( int i = 0; i < colorNodeList.size(); ++i )
520 {
521 currentItemElem = colorNodeList.at( i ).toElement();
522 value = currentItemElem.attribute( QStringLiteral( "value" ) );
523 label = currentItemElem.attribute( QStringLiteral( "label" ) );
524 red = currentItemElem.attribute( QStringLiteral( "red" ) ).toInt();
525 green = currentItemElem.attribute( QStringLiteral( "green" ) ).toInt();
526 blue = currentItemElem.attribute( QStringLiteral( "blue" ) ).toInt();
527 newColor = QColor( red, green, blue );
528 QDomElement newItemElem = doc.createElement( QStringLiteral( "item" ) );
529 newItemElem.setAttribute( QStringLiteral( "value" ), value );
530 newItemElem.setAttribute( QStringLiteral( "label" ), label );
531 newItemElem.setAttribute( QStringLiteral( "color" ), newColor.name() );
532 newColorRampShaderElem.appendChild( newItemElem );
533 }
534 }
535 }
536 else if ( drawingStyle == QLatin1String( "PalettedColor" ) )
537 {
538 rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "paletted" ) );
539 rasterRendererElem.setAttribute( QStringLiteral( "band" ), grayBand );
540 const QDomElement customColorRampElem = rasterPropertiesElem.firstChildElement( QStringLiteral( "customColorRamp" ) );
541 const QDomNodeList colorRampEntryList = customColorRampElem.elementsByTagName( QStringLiteral( "colorRampEntry" ) );
542 QDomElement newColorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
543
544 int red = 0;
545 int green = 0;
546 int blue = 0;
547 int value = 0;
548 QDomElement colorRampEntryElem;
549 for ( int i = 0; i < colorRampEntryList.size(); ++i )
550 {
551 colorRampEntryElem = colorRampEntryList.at( i ).toElement();
552 QDomElement newPaletteElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
553 value = static_cast< int >( colorRampEntryElem.attribute( QStringLiteral( "value" ) ).toDouble() );
554 newPaletteElem.setAttribute( QStringLiteral( "value" ), value );
555 red = colorRampEntryElem.attribute( QStringLiteral( "red" ) ).toInt();
556 green = colorRampEntryElem.attribute( QStringLiteral( "green" ) ).toInt();
557 blue = colorRampEntryElem.attribute( QStringLiteral( "blue" ) ).toInt();
558 newPaletteElem.setAttribute( QStringLiteral( "color" ), QColor( red, green, blue ).name() );
559 const QString label = colorRampEntryElem.attribute( QStringLiteral( "label" ) );
560 if ( !label.isEmpty() )
561 {
562 newPaletteElem.setAttribute( QStringLiteral( "label" ), label );
563 }
564 newColorPaletteElem.appendChild( newPaletteElem );
565 }
566 rasterRendererElem.appendChild( newColorPaletteElem );
567 }
568 else if ( drawingStyle == QLatin1String( "MultiBandColor" ) )
569 {
570 rasterRendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "multibandcolor" ) );
571
572 //red band, green band, blue band
573 const int redBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mRedBandName" ), rlayer );
574 const int greenBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mGreenBandName" ), rlayer );
575 const int blueBand = rasterBandNumber( rasterPropertiesElem, QStringLiteral( "mBlueBandName" ), rlayer );
576 rasterRendererElem.setAttribute( QStringLiteral( "redBand" ), redBand );
577 rasterRendererElem.setAttribute( QStringLiteral( "greenBand" ), greenBand );
578 rasterRendererElem.setAttribute( QStringLiteral( "blueBand" ), blueBand );
579
580 transformContrastEnhancement( doc, rasterPropertiesElem, rasterRendererElem );
581 }
582 else
583 {
584 return;
585 }
586
587 //replace rasterproperties element with rasterrenderer element
588 if ( !parentNode.isNull() )
589 {
590 parentNode.replaceChild( rasterRendererElem, rasterPropertiesElem );
591 }
592}
593
595{
596 return mDom;
597}
598
600{
601 return mCurrentVersion;
602}
603
604int rasterBandNumber( const QDomElement &rasterPropertiesElem, const QString &bandName, QgsRasterLayer *rlayer )
605{
606 if ( !rlayer )
607 {
608 return -1;
609 }
610
611 const int band = -1;
612 const QDomElement rasterBandElem = rasterPropertiesElem.firstChildElement( bandName );
613 if ( !rasterBandElem.isNull() )
614 {
615 const thread_local QRegularExpression re( "(\\d+)" );
616 const QRegularExpressionMatch match = re.match( rasterBandElem.text() );
617 if ( match.hasMatch() )
618 {
619 return match.captured( 1 ).toInt();
620 }
621 }
622 return band;
623}
624
625void transformContrastEnhancement( QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem )
626{
627 if ( rasterproperties.isNull() || rendererElem.isNull() )
628 {
629 return;
630 }
631
632 double minimumValue = 0;
633 double maximumValue = 0;
634 const QDomElement contrastMinMaxElem = rasterproperties.firstChildElement( QStringLiteral( "contrastEnhancementMinMaxValues" ) );
635 if ( contrastMinMaxElem.isNull() )
636 {
637 return;
638 }
639
640 const QDomElement contrastEnhancementAlgorithmElem = rasterproperties.firstChildElement( QStringLiteral( "mContrastEnhancementAlgorithm" ) );
641 if ( contrastEnhancementAlgorithmElem.isNull() )
642 {
643 return;
644 }
645
646 //convert enhancement name to enumeration
647 int algorithmEnum = 0;
648 const QString algorithmString = contrastEnhancementAlgorithmElem.text();
649 if ( algorithmString == QLatin1String( "StretchToMinimumMaximum" ) )
650 {
651 algorithmEnum = 1;
652 }
653 else if ( algorithmString == QLatin1String( "StretchAndClipToMinimumMaximum" ) )
654 {
655 algorithmEnum = 2;
656 }
657 else if ( algorithmString == QLatin1String( "ClipToMinimumMaximum" ) )
658 {
659 algorithmEnum = 3;
660 }
661 else if ( algorithmString == QLatin1String( "UserDefinedEnhancement" ) )
662 {
663 algorithmEnum = 4;
664 }
665
666 const QDomNodeList minMaxEntryList = contrastMinMaxElem.elementsByTagName( QStringLiteral( "minMaxEntry" ) );
667 QStringList enhancementNameList;
668 if ( minMaxEntryList.size() == 1 )
669 {
670 enhancementNameList << QStringLiteral( "contrastEnhancement" );
671 }
672 if ( minMaxEntryList.size() == 3 )
673 {
674 enhancementNameList << QStringLiteral( "redContrastEnhancement" ) << QStringLiteral( "greenContrastEnhancement" ) << QStringLiteral( "blueContrastEnhancement" );
675 }
676 if ( minMaxEntryList.size() > enhancementNameList.size() )
677 {
678 return;
679 }
680
681 QDomElement minMaxEntryElem;
682 for ( int i = 0; i < minMaxEntryList.size(); ++i )
683 {
684 minMaxEntryElem = minMaxEntryList.at( i ).toElement();
685 const QDomElement minElem = minMaxEntryElem.firstChildElement( QStringLiteral( "min" ) );
686 if ( minElem.isNull() )
687 {
688 return;
689 }
690 minimumValue = minElem.text().toDouble();
691
692 const QDomElement maxElem = minMaxEntryElem.firstChildElement( QStringLiteral( "max" ) );
693 if ( maxElem.isNull() )
694 {
695 return;
696 }
697 maximumValue = maxElem.text().toDouble();
698
699 QDomElement newContrastEnhancementElem = doc.createElement( enhancementNameList.at( i ) );
700 QDomElement newMinValElem = doc.createElement( QStringLiteral( "minValue" ) );
701 const QDomText minText = doc.createTextNode( QString::number( minimumValue ) );
702 newMinValElem.appendChild( minText );
703 newContrastEnhancementElem.appendChild( newMinValElem );
704 QDomElement newMaxValElem = doc.createElement( QStringLiteral( "maxValue" ) );
705 const QDomText maxText = doc.createTextNode( QString::number( maximumValue ) );
706 newMaxValElem.appendChild( maxText );
707 newContrastEnhancementElem.appendChild( newMaxValElem );
708
709 QDomElement newAlgorithmElem = doc.createElement( QStringLiteral( "algorithm" ) );
710 const QDomText newAlgorithmText = doc.createTextNode( QString::number( algorithmEnum ) );
711 newAlgorithmElem.appendChild( newAlgorithmText );
712 newContrastEnhancementElem.appendChild( newAlgorithmElem );
713
714 rendererElem.appendChild( newContrastEnhancementElem );
715 }
716}
717
718void QgsProjectFileTransform::fixOldSymbolLayerReferences( const QMap<QString, QgsMapLayer *> &mapLayers )
719{
720 for ( QgsMapLayer *ml : mapLayers )
721 {
722 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( ml );
723 if ( !vl )
724 continue;
725
726 auto migrateOldReferences = [&mapLayers]( const QList<QgsSymbolLayerReference> &slRefs )
727 {
728 QList<QgsSymbolLayerReference> newRefs;
729 for ( const QgsSymbolLayerReference &slRef : slRefs )
730 {
731 const QgsVectorLayer *vlRef = qobject_cast<QgsVectorLayer *>( mapLayers[ slRef.layerId() ] );
732 const QgsFeatureRenderer *renderer = vlRef ? vlRef->renderer() : nullptr;
734 QSet<const QgsSymbolLayer *> symbolLayers = renderer ? QgsSymbolLayerUtils::toSymbolLayerPointers(
735 renderer, QSet<QgsSymbolLayerId>() << slRef.symbolLayerId() ) : QSet<const QgsSymbolLayer *>();
737 const QString slId = symbolLayers.isEmpty() ? QString() : ( *symbolLayers.constBegin() )->id();
738 newRefs << QgsSymbolLayerReference( slRef.layerId(), slId );
739 }
740
741 return newRefs;
742 };
743
744 if ( QgsAbstractVectorLayerLabeling *labeling = vl->labeling() )
745 {
746 const QStringList subProviders = labeling->subProviders();
747 for ( const QString &provider : subProviders )
748 {
749 QgsPalLayerSettings settings = labeling->settings( provider );
750 QgsTextFormat format = settings.format();
751 QList<QgsSymbolLayerReference> newMaskedSymbolLayers = migrateOldReferences( format.mask().maskedSymbolLayers() );
752 format.mask().setMaskedSymbolLayers( newMaskedSymbolLayers );
753 settings.setFormat( format );
754 labeling->setSettings( new QgsPalLayerSettings( settings ), provider );
755 }
756 }
757
758 if ( QgsFeatureRenderer *renderer = vl->renderer() )
759 {
760
761 class SymbolLayerVisitor : public QgsStyleEntityVisitorInterface
762 {
763 public:
764 bool visitEnter( const QgsStyleEntityVisitorInterface::Node &node ) override
765 {
767 }
768
769 void visitSymbol( const QgsSymbol *symbol )
770 {
771 for ( int idx = 0; idx < symbol->symbolLayerCount(); idx++ )
772 {
773 const QgsSymbolLayer *sl = symbol->symbolLayer( idx );
774
775 // recurse over sub symbols
776 const QgsSymbol *subSymbol = const_cast<QgsSymbolLayer *>( sl )->subSymbol();
777 if ( subSymbol )
778 visitSymbol( subSymbol );
779
780 if ( const QgsMaskMarkerSymbolLayer *maskLayer = dynamic_cast<const QgsMaskMarkerSymbolLayer *>( sl ) )
781 maskSymbolLayers << maskLayer;
782 }
783 }
784
785 bool visit( const QgsStyleEntityVisitorInterface::StyleLeaf &leaf ) override
786 {
787 if ( leaf.entity && leaf.entity->type() == QgsStyle::SymbolEntity )
788 {
789 auto symbolEntity = static_cast<const QgsStyleSymbolEntity *>( leaf.entity );
790 if ( symbolEntity->symbol() )
791 visitSymbol( symbolEntity->symbol() );
792 }
793 return true;
794 }
795
796 QList<const QgsMaskMarkerSymbolLayer *> maskSymbolLayers;
797 };
798
799 SymbolLayerVisitor visitor;
800 renderer->accept( &visitor );
801
802 for ( const QgsMaskMarkerSymbolLayer *maskSymbolLayer : std::as_const( visitor.maskSymbolLayers ) )
803 // Ugly but there is no other proper way to get those layer in order to modify them
804 const_cast<QgsMaskMarkerSymbolLayer *>( maskSymbolLayer )->setMasks( migrateOldReferences( maskSymbolLayer->masks() ) );
805 }
806 }
807}
Abstract base class - its implementations define different approaches to the labeling of a vector lay...
Base class for all map layer types.
Definition: qgsmaplayer.h:75
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 QgsDebugMsgLevel()
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)
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.
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.
Q_DECL_DEPRECATED QgsRasterBandStats bandStatistics(int bandNo, int stats, 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.
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:1372
@ 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:94
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:760
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.
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.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:5776
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5775
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void transformContrastEnhancement(QDomDocument &doc, const QDomElement &rasterproperties, QDomElement &rendererElem)
void transform3000(QgsProjectFileTransform *pft)
void transform2200to2300(QgsProjectFileTransform *pft)
void transformNull(QgsProjectFileTransform *pft)
QgsProjectVersion PFV
std::vector< TransformItem > Transformers
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.
QgsProjectVersion from
QgsProjectVersion to