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