Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmapboxglstyleconverter.h
3 : : --------------------------------------
4 : : Date : September 2020
5 : : Copyright : (C) 2020 by Nyall Dawson
6 : : Email : nyall dot dawson at gmail 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 : :
16 : : #ifndef QGSMAPBOXGLSTYLECONVERTER_H
17 : : #define QGSMAPBOXGLSTYLECONVERTER_H
18 : :
19 : : #include "qgis_core.h"
20 : : #include "qgis_sip.h"
21 : : #include "qgsproperty.h"
22 : : #include <QVariantMap>
23 : : #include <memory>
24 : :
25 : : class QgsVectorTileRenderer;
26 : : class QgsVectorTileLabeling;
27 : : class QgsVectorTileBasicRendererStyle;
28 : : class QgsVectorTileBasicLabelingStyle;
29 : :
30 : : /**
31 : : * Context for a MapBox GL style conversion operation.
32 : : * \warning This is private API only, and may change in future QGIS versions
33 : : * \ingroup core
34 : : * \since QGIS 3.16
35 : : */
36 : 0 : class CORE_EXPORT QgsMapBoxGlStyleConversionContext
37 : : {
38 : : public:
39 : :
40 : : /**
41 : : * Pushes a \a warning message generated during the conversion.
42 : : */
43 : : void pushWarning( const QString &warning );
44 : :
45 : : /**
46 : : * Returns a list of warning messages generated during the conversion.
47 : : */
48 : 0 : QStringList warnings() const { return mWarnings; }
49 : :
50 : : /**
51 : : * Clears the list of warning messages.
52 : : */
53 : 0 : void clearWarnings() { mWarnings.clear(); }
54 : :
55 : : /**
56 : : * Returns the target unit type.
57 : : *
58 : : * By default this is QgsUnitTypes::RenderPixels in order to exactly match the original
59 : : * style rendering. But rendering in pixels can cause issues on hidpi displays or with print
60 : : * layouts, so setting a target unit of QgsUnitTypes::Millimeters or another real-world unit
61 : : * type is often more appropriate.
62 : : *
63 : : * \see setTargetUnit()
64 : : */
65 : : QgsUnitTypes::RenderUnit targetUnit() const;
66 : :
67 : : /**
68 : : * Sets the target unit type.
69 : : *
70 : : * By default this is QgsUnitTypes::RenderPixels in order to exactly match the original
71 : : * style rendering. But rendering in pixels can cause issues on hidpi displays or with print
72 : : * layouts, so setting a target unit of QgsUnitTypes::Millimeters or another real-world unit
73 : : * type is often more appropriate.
74 : : *
75 : : * If setting to a non-pixel unit, be sure to call setPixelSizeConversionFactor() in order
76 : : * to setup an appropriate pixel-to-unit conversion factor to scale converted sizes
77 : : * using. E.g. if the target unit is millimeters, the size conversion factor should be
78 : : * set to a pixel-to-millimeter value.
79 : : *
80 : : * \see targetUnit()
81 : : */
82 : : void setTargetUnit( QgsUnitTypes::RenderUnit targetUnit );
83 : :
84 : : /**
85 : : * Returns the pixel size conversion factor, used to scale the original pixel sizes
86 : : * when converting styles.
87 : : *
88 : : * \see setPixelSizeConversionFactor()
89 : : */
90 : : double pixelSizeConversionFactor() const;
91 : :
92 : : /**
93 : : * Sets the pixel size conversion factor, used to scale the original pixel sizes
94 : : * when converting styles.
95 : : *
96 : : * \see pixelSizeConversionFactor()
97 : : */
98 : : void setPixelSizeConversionFactor( double sizeConversionFactor );
99 : :
100 : : /**
101 : : * Returns the sprite image to use during conversion, or an invalid image if this is not set.
102 : : *
103 : : * \see spriteDefinitions()
104 : : * \see setSprites()
105 : : */
106 : : QImage spriteImage() const;
107 : :
108 : : /**
109 : : * Returns the sprite definitions to use during conversion.
110 : : *
111 : : * \see spriteImage()
112 : : * \see setSprites()
113 : : */
114 : : QVariantMap spriteDefinitions() const;
115 : :
116 : : /**
117 : : * Sets the sprite \a image and \a definitions JSON to use during conversion.
118 : : *
119 : : * \see spriteImage()
120 : : * \see spriteDefinitions()
121 : : */
122 : : void setSprites( const QImage &image, const QVariantMap &definitions );
123 : :
124 : : /**
125 : : * Sets the sprite \a image and \a definitions JSON string to use during conversion.
126 : : *
127 : : * \see spriteImage()
128 : : * \see spriteDefinitions()
129 : : */
130 : : void setSprites( const QImage &image, const QString &definitions );
131 : :
132 : : /**
133 : : * Returns the layer ID of the layer currently being converted.
134 : : *
135 : : * \see setLayerId()
136 : : */
137 : : QString layerId() const;
138 : :
139 : : /**
140 : : * Sets the layer ID of the layer currently being converted.
141 : : *
142 : : * \see layerId()
143 : : */
144 : : void setLayerId( const QString &value );
145 : :
146 : : private:
147 : :
148 : : QStringList mWarnings;
149 : :
150 : : QString mLayerId;
151 : :
152 : 0 : QgsUnitTypes::RenderUnit mTargetUnit = QgsUnitTypes::RenderPixels;
153 : :
154 : 0 : double mSizeConversionFactor = 1.0;
155 : :
156 : : QImage mSpriteImage;
157 : : QVariantMap mSpriteDefinitions;
158 : : };
159 : :
160 : : /**
161 : : * \ingroup core
162 : : * \brief Handles conversion of MapBox GL styles to QGIS vector tile renderers and labeling
163 : : * settings.
164 : : *
165 : : * Conversions are performed by calling convert() with either a JSON map or JSON
166 : : * string value, and then retrieving the results by calling renderer() or labeling()
167 : : * respectively.
168 : : *
169 : : * \since QGIS 3.16
170 : : */
171 : : class CORE_EXPORT QgsMapBoxGlStyleConverter
172 : : {
173 : : public:
174 : :
175 : : /**
176 : : * Constructor for QgsMapBoxGlStyleConverter.
177 : : */
178 : : QgsMapBoxGlStyleConverter();
179 : :
180 : : //! QgsMapBoxGlStyleConverter cannot be copied
181 : : QgsMapBoxGlStyleConverter( const QgsMapBoxGlStyleConverter &other ) = delete;
182 : : //! QgsMapBoxGlStyleConverter cannot be copied
183 : : QgsMapBoxGlStyleConverter &operator=( const QgsMapBoxGlStyleConverter &other ) = delete;
184 : :
185 : : ~QgsMapBoxGlStyleConverter();
186 : :
187 : : //! Result of conversion
188 : : enum Result
189 : : {
190 : : Success = 0, //!< Conversion was successful
191 : : NoLayerList = 1, //!< No layer list was found in JSON input
192 : : };
193 : :
194 : : /**
195 : : * Converts a JSON \a style map, and returns the resultant status of the conversion.
196 : : *
197 : : * If an error occurs during conversion then a descriptive error message can be retrieved
198 : : * by calling errorMessage().
199 : : *
200 : : * After conversion, the resultant labeling and style rules can be retrieved by calling
201 : : * renderer() or labeling() respectively.
202 : : *
203 : : * The optional \a context argument can be set to use a specific context during the conversion.
204 : : */
205 : : Result convert( const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context = nullptr );
206 : :
207 : : /**
208 : : * Converts a JSON \a style string, and returns the resultant status of the conversion.
209 : : *
210 : : * If an error occurs during conversion then a descriptive error message can be retrieved
211 : : * by calling errorMessage().
212 : : *
213 : : * After conversion, the resultant labeling and style rules can be retrieved by calling
214 : : * renderer() or labeling() respectively.
215 : : *
216 : : * The optional \a context argument can be set to use a specific context during the conversion.
217 : : */
218 : : Result convert( const QString &style, QgsMapBoxGlStyleConversionContext *context = nullptr );
219 : :
220 : : /**
221 : : * Returns a descriptive error message if an error was encountered during the style conversion,
222 : : * or an empty string if no error was encountered.
223 : : *
224 : : * \see warnings()
225 : : */
226 : 0 : QString errorMessage() const { return mError; }
227 : :
228 : : /**
229 : : * Returns a list of user-friendly warnings generated during the conversion, e.g. as a result
230 : : * of MapBox GL style settings which cannot be translated to QGIS styles.
231 : : *
232 : : * \see errorMessage()
233 : : */
234 : 0 : QStringList warnings() const { return mWarnings; }
235 : :
236 : : /**
237 : : * Returns a new instance of a vector tile renderer representing the converted style,
238 : : * or NULLPTR if the style could not be converted successfully.
239 : : */
240 : : QgsVectorTileRenderer *renderer() const SIP_FACTORY;
241 : :
242 : : /**
243 : : * Returns a new instance of a vector tile labeling representing the converted style,
244 : : * or NULLPTR if the style could not be converted successfully.
245 : : */
246 : : QgsVectorTileLabeling *labeling() const SIP_FACTORY;
247 : :
248 : : protected:
249 : :
250 : : /**
251 : : * Property types, for interpolated value conversion
252 : : * \warning This is private API only, and may change in future QGIS versions
253 : : */
254 : : enum PropertyType
255 : : {
256 : : Color, //!< Color property
257 : : Numeric, //!< Numeric property (e.g. line width, text size)
258 : : Opacity, //!< Opacity property
259 : : Point, //!< Point/offset property
260 : : };
261 : :
262 : : /**
263 : : * Parse list of \a layers from JSON.
264 : : * \warning This is private API only, and may change in future QGIS versions
265 : : */
266 : : void parseLayers( const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context = nullptr );
267 : :
268 : : /**
269 : : * Parses a fill layer.
270 : : *
271 : : * \warning This is private API only, and may change in future QGIS versions
272 : : *
273 : : * \param jsonLayer fill layer to parse
274 : : * \param style generated QGIS vector tile style
275 : : * \param context conversion context
276 : : * \returns TRUE if the layer was successfully parsed.
277 : : */
278 : : static bool parseFillLayer( const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style SIP_OUT, QgsMapBoxGlStyleConversionContext &context );
279 : :
280 : : /**
281 : : * Parses a line layer.
282 : : *
283 : : * \warning This is private API only, and may change in future QGIS versions
284 : : *
285 : : * \param jsonLayer line layer to parse
286 : : * \param style generated QGIS vector tile style
287 : : * \param context conversion context
288 : : * \returns TRUE if the layer was successfully parsed.
289 : : */
290 : : static bool parseLineLayer( const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style SIP_OUT, QgsMapBoxGlStyleConversionContext &context );
291 : :
292 : : /**
293 : : * Parses a circle layer.
294 : : *
295 : : * \warning This is private API only, and may change in future QGIS versions
296 : : *
297 : : * \param jsonLayer circle layer to parse
298 : : * \param style generated QGIS vector tile style
299 : : * \param context conversion context
300 : : * \returns TRUE if the layer was successfully parsed.
301 : : */
302 : : static bool parseCircleLayer( const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style SIP_OUT, QgsMapBoxGlStyleConversionContext &context );
303 : :
304 : : /**
305 : : * Parses a symbol layer as renderer or labeling.
306 : : *
307 : : * \warning This is private API only, and may change in future QGIS versions
308 : : *
309 : : * \param jsonLayer symbol layer to parse
310 : : * \param rendererStyle generated QGIS vector tile style
311 : : * \param hasRenderer will be set to TRUE if symbol layer generated a renderer style
312 : : * \param labelingStyle generated QGIS vector tile labeling
313 : : * \param hasLabeling will be set to TRUE if symbol layer generated a labeling style
314 : : * \param context conversion context
315 : : */
316 : : static void parseSymbolLayer( const QVariantMap &jsonLayer,
317 : : QgsVectorTileBasicRendererStyle &rendererStyle SIP_OUT,
318 : : bool &hasRenderer SIP_OUT,
319 : : QgsVectorTileBasicLabelingStyle &labelingStyle SIP_OUT,
320 : : bool &hasLabeling SIP_OUT, QgsMapBoxGlStyleConversionContext &context );
321 : :
322 : : /**
323 : : * Parses a symbol layer as a renderer
324 : : *
325 : : * \warning This is private API only, and may change in future QGIS versions
326 : : *
327 : : * \param jsonLayer fill layer to parse
328 : : * \param rendererStyle generated QGIS vector tile style
329 : : * \param context conversion context
330 : : *
331 : : * \returns TRUE if symbol layer was converted to renderer
332 : : */
333 : : static bool parseSymbolLayerAsRenderer( const QVariantMap &jsonLayer,
334 : : QgsVectorTileBasicRendererStyle &rendererStyle SIP_OUT, QgsMapBoxGlStyleConversionContext &context );
335 : :
336 : : /**
337 : : * Parses a color value which is interpolated by zoom range.
338 : : *
339 : : * \param json definition of color interpolation
340 : : * \param context conversion context
341 : : * \param defaultColor optional storage for a reasonable "default" color representing the overall property.
342 : : *
343 : : * \returns QgsProperty representing interpolation settings
344 : : */
345 : : static QgsProperty parseInterpolateColorByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor SIP_OUT = nullptr );
346 : :
347 : : /**
348 : : * Parses a numeric value which is interpolated by zoom range.
349 : : *
350 : : * \param json definition of interpolation
351 : : * \param context conversion context
352 : : * \param multiplier optional multiplication factor
353 : : * \param defaultNumber optional storage for a reasonable "default" number representing the overall property.
354 : : *
355 : : * \returns QgsProperty representing interpolation settings
356 : : */
357 : : static QgsProperty parseInterpolateByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1, double *defaultNumber SIP_OUT = nullptr );
358 : :
359 : : /**
360 : : * Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
361 : : * For \a json with intermediate stops it uses parseOpacityStops() function.
362 : : * It uses QGIS set_color_part() function to set alpha component of color.
363 : : *
364 : : * \warning This is private API only, and may change in future QGIS versions
365 : : */
366 : : static QgsProperty parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity );
367 : :
368 : : /**
369 : : * Takes values from stops and uses either scale_linear() or scale_exp() functions
370 : : * to interpolate alpha component of color.
371 : : *
372 : : * \warning This is private API only, and may change in future QGIS versions
373 : : */
374 : : static QString parseOpacityStops( double base, const QVariantList &stops, int maxOpacity );
375 : :
376 : : /**
377 : : * Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
378 : : * For \a json with intermediate stops it uses parsePointStops() function.
379 : : *
380 : : * \warning This is private API only, and may change in future QGIS versions
381 : : */
382 : : static QgsProperty parseInterpolatePointByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1, QPointF *defaultPoint SIP_OUT = nullptr );
383 : :
384 : : /**
385 : : * Interpolates a string by zoom.
386 : : * For \a json with intermediate stops it uses parseStringStops() function.
387 : : *
388 : : * \warning This is private API only, and may change in future QGIS versions
389 : : */
390 : : static QgsProperty parseInterpolateStringByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context,
391 : : const QVariantMap &conversionMap,
392 : : QString *defaultString SIP_OUT = nullptr );
393 : :
394 : :
395 : : /**
396 : : * Takes values from stops and uses either scale_linear() or scale_exp() functions
397 : : * to interpolate point/offset values.
398 : : *
399 : : * \warning This is private API only, and may change in future QGIS versions
400 : : */
401 : : static QString parsePointStops( double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1 );
402 : :
403 : : /**
404 : : * Parses a list of interpolation stops
405 : : *
406 : : * \param base interpolation exponent base
407 : : * \param stops definition of interpolation stops
408 : : * \param multiplier optional multiplication factor
409 : : * \param context conversion context
410 : : */
411 : : static QString parseStops( double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context );
412 : :
413 : : /**
414 : : * Parses a list of interpolation stops containing string values.
415 : : *
416 : : * \param stops definition of interpolation stops
417 : : * \param context conversion context
418 : : * \param conversionMap map of input string to output expression value
419 : : * \param defaultString reasonable default value taken from stops
420 : : *
421 : : * \returns converted expression
422 : : */
423 : : static QString parseStringStops( const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context,
424 : : const QVariantMap &conversionMap,
425 : : QString *defaultString SIP_OUT = nullptr );
426 : :
427 : :
428 : : /**
429 : : * Parses and converts a value list (e.g. an interpolate list).
430 : : *
431 : : * \warning This is private API only, and may change in future QGIS versions
432 : : */
433 : : static QgsProperty parseValueList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
434 : : int maxOpacity = 255, QColor *defaultColor SIP_OUT = nullptr, double *defaultNumber SIP_OUT = nullptr );
435 : :
436 : :
437 : : /**
438 : : * Parses and converts a match function value list.
439 : : *
440 : : * \warning This is private API only, and may change in future QGIS versions
441 : : */
442 : : static QgsProperty parseMatchList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
443 : : int maxOpacity = 255, QColor *defaultColor SIP_OUT = nullptr, double *defaultNumber SIP_OUT = nullptr );
444 : :
445 : : /**
446 : : * Interpolates a list which starts with the interpolate function.
447 : : *
448 : : * \warning This is private API only, and may change in future QGIS versions
449 : : */
450 : : static QgsProperty parseInterpolateListByZoom( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
451 : : int maxOpacity = 255, QColor *defaultColor SIP_OUT = nullptr, double *defaultNumber SIP_OUT = nullptr );
452 : :
453 : : /**
454 : : * Parses a \a color in one of these supported formats:
455 : : *
456 : : * - \c \#fff or \c \#ffffff
457 : : * - ``hsl(30, 19%, 90%)`` or ``hsla(30, 19%, 90%, 0.4)``
458 : : * - ``rgb(10, 20, 30)`` or ``rgba(10, 20, 30, 0.5)``
459 : : *
460 : : * Returns an invalid color if the color could not be parsed.
461 : : *
462 : : * \warning This is private API only, and may change in future QGIS versions
463 : : */
464 : : static QColor parseColor( const QVariant &color, QgsMapBoxGlStyleConversionContext &context );
465 : :
466 : : /**
467 : : * Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression function.
468 : : * \param color input color
469 : : * \param hue an integer value from 0 to 360
470 : : * \param saturation an integer value from 0 to 100
471 : : * \param lightness an integer value from 0 to 100
472 : : * \param alpha an integer value from 0 (completely transparent) to 255 (opaque).
473 : : *
474 : : * \warning This is private API only, and may change in future QGIS versions
475 : : */
476 : : static void colorAsHslaComponents( const QColor &color, int &hue, int &saturation, int &lightness, int &alpha );
477 : :
478 : : /**
479 : : * Generates an interpolation for values between \a valueMin and \a valueMax, scaled between the
480 : : * ranges \a zoomMin to \a zoomMax.
481 : : *
482 : : * \warning This is private API only, and may change in future QGIS versions
483 : : */
484 : : static QString interpolateExpression( double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier = 1 );
485 : :
486 : : /**
487 : : * Converts a value to Qt::PenCapStyle enum from JSON value.
488 : : *
489 : : * \warning This is private API only, and may change in future QGIS versions
490 : : */
491 : : static Qt::PenCapStyle parseCapStyle( const QString &style );
492 : :
493 : : /**
494 : : * Converts a value to Qt::PenJoinStyle enum from JSON value.
495 : : *
496 : : * \warning This is private API only, and may change in future QGIS versions
497 : : */
498 : : static Qt::PenJoinStyle parseJoinStyle( const QString &style );
499 : :
500 : : /**
501 : : * Converts a MapBox GL expression to a QGIS expression.
502 : : *
503 : : * \warning This is private API only, and may change in future QGIS versions
504 : : */
505 : : static QString parseExpression( const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context );
506 : :
507 : : /**
508 : : * Retrieves the sprite image with the specified \a name, taken from the specified \a context.
509 : : *
510 : : * The \a context must have valid sprite definitions and images set via QgsMapBoxGlStyleConversionContext::setSprites()
511 : : * prior to conversion.
512 : : */
513 : : static QImage retrieveSprite( const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize );
514 : :
515 : : /**
516 : : * Retrieves the sprite image with the specified \a name, taken from the specified \a context as a base64 encoded value
517 : : *
518 : : * The \a context must have valid sprite definitions and images set via QgsMapBoxGlStyleConversionContext::setSprites()
519 : : * prior to conversion.
520 : : */
521 : : static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty );
522 : :
523 : : private:
524 : :
525 : : #ifdef SIP_RUN
526 : : QgsMapBoxGlStyleConverter( const QgsMapBoxGlStyleConverter &other );
527 : : #endif
528 : :
529 : : static QString parseValue( const QVariant &value, QgsMapBoxGlStyleConversionContext &context );
530 : : static QString parseKey( const QVariant &value );
531 : :
532 : : QString mError;
533 : : QStringList mWarnings;
534 : :
535 : : std::unique_ptr< QgsVectorTileRenderer > mRenderer;
536 : : std::unique_ptr< QgsVectorTileLabeling > mLabeling;
537 : :
538 : : };
539 : :
540 : : #endif // QGSMAPBOXGLSTYLECONVERTER_H
|