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