QGIS API Documentation 3.99.0-Master (357b655ed83)
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
20#include <QString>
21
22using namespace Qt::StringLiterals;
23
24QList<QList<QgsBlankSegmentUtils::BlankSegments>> QgsBlankSegmentUtils::parseBlankSegments( const QString &strBlankSegments, const QgsRenderContext &renderContext, Qgis::RenderUnit unit, QString &error )
25{
26 QString currentNumber;
27 QList<QList<BlankSegments>> blankSegments;
28 constexpr char internalError[] = "Internal error while processing blank segments";
29
30 auto appendLevel = [&blankSegments, &internalError]( int level ) -> void
31 {
32 if ( level == 0 )
33 {
34 blankSegments.append( QList<BlankSegments>() );
35 }
36 else if ( level == 1 )
37 {
38 if ( blankSegments.isEmpty() )
39 throw std::runtime_error( internalError ); // should not happen
40 blankSegments.back().append( BlankSegments() );
41 }
42 else if ( level == 2 )
43 {
44 if ( blankSegments.isEmpty() || blankSegments.back().isEmpty() )
45 throw std::runtime_error( internalError ); // should not happen
46 blankSegments.back().back().append( QPair<double, double>( -1, -1 ) );
47 }
48 else
49 throw std::runtime_error( internalError ); // should not happen
50 };
51
52 auto addNumber = [&blankSegments, &internalError, &currentNumber]( const QChar & c ) -> void
53 {
54 if ( blankSegments.isEmpty() || blankSegments.back().isEmpty() || blankSegments.back().back().isEmpty() )
55 {
56 throw std::runtime_error( internalError ); // should not happen
57 }
58
59 if ( ( c == ')' || c == ',' ) && blankSegments.back().back().back().first == -1 )
60 {
61 throw std::runtime_error( "Missing number" );
62 }
63
64 if ( blankSegments.back().back().back().second != -1 )
65 {
66 throw std::runtime_error( "Too many number" );
67 }
68
69 bool ok;
70 const double number = currentNumber.toDouble( &ok );
71 if ( !ok )
72 {
73 throw std::runtime_error( u"bad formatted number '%1'"_s.arg( currentNumber ).toStdString() );
74 }
75
76 BlankSegments &segments = blankSegments.back().back();
77 if ( segments.back().first == -1 )
78 {
79 if ( segments.count() > 1 && segments.at( segments.count() - 2 ).second > number )
80 throw std::runtime_error( u"Wrong blank segments distances, start (%1) < previous end (%2)"_s.arg( number ).arg( segments.at( segments.count() - 2 ).second ).toStdString() );
81
82 blankSegments.back().back().back().first = number;
83 }
84 else if ( blankSegments.back().back().back().first > number )
85 {
86 throw std::runtime_error( u"Wrong blank segments distances, start (%1) > end (%2)"_s.arg( blankSegments.back().back().back().first ).arg( number ).toStdString() );
87 }
88 else
89 {
90 blankSegments.back().back().back().second = number;
91 }
92 currentNumber.clear();
93 };
94
95 int level = -1;
96 int iChar = 0;
97 try
98 {
99 for ( const QChar &c : strBlankSegments )
100 {
101 if ( !currentNumber.isEmpty() && ( c.isSpace() || c == ')' || c == ',' ) )
102 {
103 if ( level < 2 )
104 throw std::runtime_error( "Missing '('" );
105 addNumber( c );
106 }
107
108 if ( c == '(' )
109 {
110 if ( level >= 2 )
111 throw std::runtime_error( "Extraneous '('" );
112 appendLevel( ++level );
113 }
114 else if ( c == ')' )
115 {
116 if ( level < 0 )
117 throw std::runtime_error( "Extraneous ')'" );
118 if ( level == 2 && !blankSegments.isEmpty() && !blankSegments.back().isEmpty() && blankSegments.back().back().count() == 1
119 && blankSegments.back().back().back() == QPair<double, double>( -1, -1 ) )
120 {
121 blankSegments.back().back().pop_back();
122 }
123 level--;
124
125 }
126 else if ( c == ',' )
127 {
128 if ( ( level == 0 && blankSegments.count() == 0 )
129 || ( level == 1 && !blankSegments.isEmpty() && blankSegments.back().count() == 0 )
130 || ( level == 2 && !blankSegments.isEmpty() && !blankSegments.back().isEmpty() && blankSegments.back().back().count() == 0 ) )
131 throw std::runtime_error( "No elements, Not expecting ','" );
132
133 appendLevel( level );
134 }
135 else if ( c.isNumber() || c == '.' )
136 {
137 currentNumber.append( c );
138 }
139 else if ( !c.isSpace() )
140 {
141 throw std::runtime_error( u"Invalid character '%1'"_s.arg( c ).toStdString() );
142 }
143 iChar++;
144 }
145 if ( level != -1 )
146 {
147 throw std::runtime_error( "Missing ')'" );
148 }
149 }
150 catch ( const std::exception &e )
151 {
152 blankSegments.clear();
153 error = u"%1 (column: %2)"_s.arg( e.what() ).arg( iChar );
154 }
155
156 // convert in pixels
157 std::for_each( blankSegments.begin(), blankSegments.end(), [&renderContext, &unit]( QList<BlankSegments> &rings )
158 {
159 std::for_each( rings.begin(), rings.end(), [&renderContext, &unit]( BlankSegments & blankSegments )
160 {
161 std::for_each( blankSegments.begin(), blankSegments.end(), [&renderContext, &unit]( QPair<double, double> &blankSegment )
162 {
163 blankSegment.first = renderContext.convertToPainterUnits( blankSegment.first, unit );
164 blankSegment.second = renderContext.convertToPainterUnits( blankSegment.second, unit );
165 } );
166 } );
167 } );
168
169 return blankSegments;
170}
RenderUnit
Rendering size units.
Definition qgis.h:5290
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.
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