QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsblanksegmentutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 blanksegmentutils.cpp
3 ---------------------
4 begin : 2025/11/05
5 copyright : (C) 2025 by Julien Cabieces
6 email : julien dot cabieces at oslandia dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsrendercontext.h"
19
20QList<QList<QgsBlankSegmentUtils::BlankSegments>> QgsBlankSegmentUtils::parseBlankSegments( const QString &strBlankSegments, const QgsRenderContext &renderContext, Qgis::RenderUnit unit, QString &error )
21{
22 QString currentNumber;
23 QList<QList<BlankSegments>> blankSegments;
24 constexpr char internalError[] = "Internal error while processing blank segments";
25
26 auto appendLevel = [&blankSegments, &internalError]( int level ) -> void
27 {
28 if ( level == 0 )
29 {
30 blankSegments.append( QList<BlankSegments>() );
31 }
32 else if ( level == 1 )
33 {
34 if ( blankSegments.isEmpty() )
35 throw std::runtime_error( internalError ); // should not happen
36 blankSegments.back().append( BlankSegments() );
37 }
38 else if ( level == 2 )
39 {
40 if ( blankSegments.isEmpty() || blankSegments.back().isEmpty() )
41 throw std::runtime_error( internalError ); // should not happen
42 blankSegments.back().back().append( QPair<double, double>( -1, -1 ) );
43 }
44 else
45 throw std::runtime_error( internalError ); // should not happen
46 };
47
48 auto addNumber = [&blankSegments, &internalError, &currentNumber]( const QChar & c ) -> void
49 {
50 if ( blankSegments.isEmpty() || blankSegments.back().isEmpty() || blankSegments.back().back().isEmpty() )
51 {
52 throw std::runtime_error( internalError ); // should not happen
53 }
54
55 if ( ( c == ')' || c == ',' ) && blankSegments.back().back().back().first == -1 )
56 {
57 throw std::runtime_error( "Missing number" );
58 }
59
60 if ( blankSegments.back().back().back().second != -1 )
61 {
62 throw std::runtime_error( "Too many number" );
63 }
64
65 bool ok;
66 const double number = currentNumber.toDouble( &ok );
67 if ( !ok )
68 {
69 throw std::runtime_error( QStringLiteral( "bad formatted number '%1'" ).arg( currentNumber ).toStdString() );
70 }
71
72 BlankSegments &segments = blankSegments.back().back();
73 if ( segments.back().first == -1 )
74 {
75 if ( segments.count() > 1 && segments.at( segments.count() - 2 ).second > number )
76 throw std::runtime_error( QStringLiteral( "Wrong blank segments distances, start (%1) < previous end (%2)" ).arg( number ).arg( segments.at( segments.count() - 2 ).second ).toStdString() );
77
78 blankSegments.back().back().back().first = number;
79 }
80 else if ( blankSegments.back().back().back().first > number )
81 {
82 throw std::runtime_error( QStringLiteral( "Wrong blank segments distances, start (%1) > end (%2)" ).arg( blankSegments.back().back().back().first ).arg( number ).toStdString() );
83 }
84 else
85 {
86 blankSegments.back().back().back().second = number;
87 }
88 currentNumber.clear();
89 };
90
91 int level = -1;
92 int iChar = 0;
93 try
94 {
95 for ( const QChar &c : strBlankSegments )
96 {
97 if ( !currentNumber.isEmpty() && ( c.isSpace() || c == ')' || c == ',' ) )
98 {
99 if ( level < 2 )
100 throw std::runtime_error( "Missing '('" );
101 addNumber( c );
102 }
103
104 if ( c == '(' )
105 {
106 if ( level >= 2 )
107 throw std::runtime_error( "Extraneous '('" );
108 appendLevel( ++level );
109 }
110 else if ( c == ')' )
111 {
112 if ( level < 0 )
113 throw std::runtime_error( "Extraneous ')'" );
114 if ( level == 2 && !blankSegments.isEmpty() && !blankSegments.back().isEmpty() && blankSegments.back().back().count() == 1
115 && blankSegments.back().back().back() == QPair<double, double>( -1, -1 ) )
116 {
117 blankSegments.back().back().pop_back();
118 }
119 level--;
120
121 }
122 else if ( c == ',' )
123 {
124 if ( ( level == 0 && blankSegments.count() == 0 )
125 || ( level == 1 && !blankSegments.isEmpty() && blankSegments.back().count() == 0 )
126 || ( level == 2 && !blankSegments.isEmpty() && !blankSegments.back().isEmpty() && blankSegments.back().back().count() == 0 ) )
127 throw std::runtime_error( "No elements, Not expecting ','" );
128
129 appendLevel( level );
130 }
131 else if ( c.isNumber() || c == '.' )
132 {
133 currentNumber.append( c );
134 }
135 else if ( !c.isSpace() )
136 {
137 throw std::runtime_error( QStringLiteral( "Invalid character '%1'" ).arg( c ).toStdString() );
138 }
139 iChar++;
140 }
141 if ( level != -1 )
142 {
143 throw std::runtime_error( "Missing ')'" );
144 }
145 }
146 catch ( const std::exception &e )
147 {
148 blankSegments.clear();
149 error = QStringLiteral( "%1 (column: %2)" ).arg( e.what() ).arg( iChar );
150 }
151
152 // convert in pixels
153 std::for_each( blankSegments.begin(), blankSegments.end(), [&renderContext, &unit]( QList<BlankSegments> &rings )
154 {
155 std::for_each( rings.begin(), rings.end(), [&renderContext, &unit]( BlankSegments & blankSegments )
156 {
157 std::for_each( blankSegments.begin(), blankSegments.end(), [&renderContext, &unit]( QPair<double, double> &blankSegment )
158 {
159 blankSegment.first = renderContext.convertToPainterUnits( blankSegment.first, unit );
160 blankSegment.second = renderContext.convertToPainterUnits( blankSegment.second, unit );
161 } );
162 } );
163 } );
164
165 return blankSegments;
166}
RenderUnit
Rendering size units.
Definition qgis.h:5183
QList< QPair< double, double > > BlankSegments
static QList< QList< BlankSegments > > parseBlankSegments(const QString &strBlankSegments, const QgsRenderContext &renderContext, Qgis::RenderUnit unit, QString &error)
Parse blank segments string representation strBlankSegments The blank segments are expected to be exp...
Contains information about the context of a rendering operation.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c