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