QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
qgssfcgalengine.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssfcgalengine.cpp
3 ----------------
4 begin : May 2025
5 copyright : (C) 2025 by Oslandia
6 email : benoit dot de dot mezzo at oslandia dot com
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#ifdef WITH_SFCGAL
19#include "qgssfcgalengine.h"
20
21#include <SFCGAL/capi/sfcgal_c.h>
22#include <nlohmann/json.hpp>
23
24#include "qgsgeometry.h"
25#include "qgsgeometryfactory.h"
26#include "qgssfcgalgeometry.h"
27
28// ===================================
29// sfcgal namespace
30// ===================================
31
32thread_local sfcgal::ErrorHandler sSfcgalErrorHandler;
33
34sfcgal::ErrorHandler *sfcgal::errorHandler()
35{
36 return &sSfcgalErrorHandler;
37}
38
39void sfcgal::GeometryDeleter::operator()( sfcgal::geometry *geom ) const
40{
41 sfcgal_geometry_delete( geom );
42}
43
44sfcgal::shared_geom sfcgal::make_shared_geom( sfcgal::geometry *geom )
45{
46 return sfcgal::shared_geom( geom, sfcgal::GeometryDeleter() );
47}
48
49
50#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
51void sfcgal::PrimitiveDeleter::operator()( sfcgal::primitive *prim ) const
52{
53 sfcgal_primitive_delete( prim );
54}
55
56sfcgal::shared_prim sfcgal::make_shared_prim( sfcgal::primitive *prim )
57{
58 return sfcgal::shared_prim( prim, sfcgal::PrimitiveDeleter() );
59}
60#endif
61
62bool sfcgal::ErrorHandler::hasSucceedOrStack( QString *errorMsg, const char *fromFile, const char *fromFunc, int fromLine )
63{
64 bool succeed = isTextEmpty();
65 if ( !succeed )
66 {
67 addText( "relaying error from: ", fromFile, fromFunc, fromLine );
68 if ( errorMsg )
69 {
70 errorMsg->append( errorMessages.first() );
71 }
72 }
73 return succeed;
74}
75
76int sfcgal::errorCallback( const char *fmt, ... )
77{
78 va_list ap;
79 char buffer[1024];
80
81 va_start( ap, fmt );
82 vsnprintf( buffer, sizeof buffer, fmt, ap );
83 va_end( ap );
84
85 sfcgal::errorHandler()->addText( QStringLiteral( "SFCGAL error occurred: %1" ).arg( buffer ), __FILE__, __FUNCTION__, __LINE__ );
86
87 return static_cast<int>( strlen( buffer ) );
88}
89
90int sfcgal::warningCallback( const char *fmt, ... )
91{
92 va_list ap;
93 char buffer[1024];
94
95 va_start( ap, fmt );
96 vsnprintf( buffer, sizeof buffer, fmt, ap );
97 va_end( ap );
98
99 sfcgal::errorHandler()->addText( QStringLiteral( "SFCGAL warning occurred: %1" ).arg( buffer ), __FILE__, __FUNCTION__, __LINE__ );
100
101 return static_cast<int>( strlen( buffer ) );
102}
103
104
105sfcgal::ErrorHandler::ErrorHandler()
106{
107 sfcgal_init(); // empty but called
108 sfcgal_set_error_handlers( sfcgal::warningCallback, sfcgal::errorCallback );
109}
110
111void sfcgal::ErrorHandler::clearText( QString *errorMsg )
112{
113 errorMessages.clear();
114 if ( errorMsg )
115 {
116 errorMsg->clear();
117 }
118}
119
120QString sfcgal::ErrorHandler::getMainText() const
121{
122 return errorMessages.isEmpty() ? QString() : QString( "Error occurred: " ) + errorMessages.last();
123}
124
125QString sfcgal::ErrorHandler::getFullText() const
126{
127 return errorMessages.isEmpty() ? QString() : getMainText() + "\n\t\t" + errorMessages.join( "\n\t\t" );
128}
129
130bool sfcgal::ErrorHandler::isTextEmpty() const
131{
132 return errorMessages.isEmpty();
133}
134
135void sfcgal::ErrorHandler::addText( const QString &msg, const char *fromFile, const char *fromFunc, int fromLine )
136{
137 QString txt = QString( "%2 (%3:%4) %1" ).arg( msg ).arg( fromFunc ).arg( fromFile ).arg( fromLine );
138
139 errorMessages.push_front( txt );
140}
141
142// ===================================
143// QgsSfcgalEngine static functions
144// ===================================
145
154template<typename T>
155static T geom_to_primtype(
156 T( *func_2d )( const sfcgal_geometry_t * ),
157 T( *func_3d )( const sfcgal_geometry_t * ),
158 const sfcgal::geometry *geom,
159 QString *errorMsg
160)
161{
162 sfcgal::errorHandler()->clearText( errorMsg );
163 CHECK_NOT_NULL( geom, std::numeric_limits<T>::quiet_NaN() );
164
165 T result;
166 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
167 result = func_3d( geom );
168 else
169 result = func_2d( geom );
170
171 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
172
173 return result;
174};
175
185template<typename T>
186static T geomgeom_to_primtype(
187 T( *func_2d )( const sfcgal_geometry_t *, const sfcgal_geometry_t * ),
188 T( *func_3d )( const sfcgal_geometry_t *, const sfcgal_geometry_t * ),
189 const sfcgal::geometry *geomA,
190 const sfcgal::geometry *geomB,
191 QString *errorMsg
192)
193{
194 sfcgal::errorHandler()->clearText( errorMsg );
195 CHECK_NOT_NULL( geomA, false );
196 CHECK_NOT_NULL( geomB, false );
197
198 T result;
199 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
200 result = func_3d( geomA, geomB );
201 else
202 result = func_2d( geomA, geomB );
203
204 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
205
206 return result;
207};
208
217static sfcgal::shared_geom geom_to_geom(
218 sfcgal::func_geom_to_geom func_2d,
219 sfcgal::func_geom_to_geom func_3d,
220 const sfcgal::geometry *geom,
221 QString *errorMsg
222)
223{
224 sfcgal::errorHandler()->clearText( errorMsg );
225 CHECK_NOT_NULL( geom, nullptr );
226
227 sfcgal::geometry *result = nullptr;
228 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
229 result = func_3d( geom );
230 else
231 result = func_2d( geom );
232
233 CHECK_SUCCESS( errorMsg, nullptr );
234 CHECK_NOT_NULL( result, nullptr );
235
236 return sfcgal::make_shared_geom( result );
237};
238
239
249static sfcgal::shared_geom geomgeom_to_geom(
250 sfcgal::func_geomgeom_to_geom func_2d,
251 sfcgal::func_geomgeom_to_geom func_3d,
252 const sfcgal::geometry *geomA,
253 const sfcgal::geometry *geomB,
254 QString *errorMsg
255)
256{
257 sfcgal::errorHandler()->clearText( errorMsg );
258 CHECK_NOT_NULL( geomA, nullptr );
259 CHECK_NOT_NULL( geomB, nullptr );
260
261 sfcgal::geometry *result = nullptr;
262 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
263 result = func_3d( geomA, geomB );
264 else
265 result = func_2d( geomA, geomB );
266
267 CHECK_SUCCESS( errorMsg, nullptr );
268 CHECK_NOT_NULL( result, nullptr );
269
270 return sfcgal::make_shared_geom( result );
271};
272
273
274// ===================================
275// QgsSfcgalEngine class
276// ===================================
277
278
279std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_geom &geom, QString *errorMsg )
280{
281 sfcgal::errorHandler()->clearText( errorMsg );
282 CHECK_NOT_NULL( geom.get(), nullptr );
283
284 return std::make_unique<QgsSfcgalGeometry>( geom );
285}
286
287std::unique_ptr<QgsAbstractGeometry> QgsSfcgalEngine::toAbstractGeometry( const sfcgal::geometry *geom, QString *errorMsg )
288{
289 std::unique_ptr<QgsAbstractGeometry> out( nullptr );
290 sfcgal::errorHandler()->clearText( errorMsg );
291 CHECK_NOT_NULL( geom, out );
292
293 QByteArray wkbArray = QgsSfcgalEngine::toWkb( geom, errorMsg );
294 CHECK_SUCCESS( errorMsg, out );
295
296 QgsConstWkbPtr wkbPtr( wkbArray );
297 out = QgsGeometryFactory::geomFromWkb( wkbPtr );
298 if ( !out )
299 {
300 Qgis::WkbType sfcgalType = QgsSfcgalEngine::wkbType( geom );
301 sfcgal::errorHandler()->addText( QStringLiteral( "WKB contains unmanaged geometry type (WKB:%1 / SFCGAL:%2" ) //
302 .arg( static_cast<int>( wkbPtr.readHeader() ) ) //
303 .arg( static_cast<int>( sfcgalType ) ),
304 __FILE__, __FUNCTION__, __LINE__ );
305 }
306
307 return out;
308}
309
310sfcgal::shared_geom QgsSfcgalEngine::fromAbstractGeometry( const QgsAbstractGeometry *geom, QString *errorMsg )
311{
312 sfcgal::errorHandler()->clearText( errorMsg );
313 CHECK_NOT_NULL( geom, sfcgal::shared_geom( nullptr ) );
314
315 QByteArray wkbBytes = geom->asWkb();
316
317 sfcgal::geometry *out = sfcgal_io_read_wkb( wkbBytes.data(), wkbBytes.length() );
318 CHECK_SUCCESS( errorMsg, nullptr );
319
320 return sfcgal::make_shared_geom( out );
321}
322
323sfcgal::shared_geom QgsSfcgalEngine::cloneGeometry( const sfcgal::geometry *geom, QString *errorMsg )
324{
325 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_clone, nullptr, geom, errorMsg );
326 CHECK_SUCCESS( errorMsg, nullptr );
327 return out;
328}
329
330QString QgsSfcgalEngine::geometryType( const sfcgal::geometry *geom, QString *errorMsg )
331{
332#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
333 ( void )geom;
334 ( void )errorMsg;
335 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "geometryType" ) );
336#else
337 sfcgal::errorHandler()->clearText( errorMsg );
338
339 char *typeChar;
340 size_t typeLen;
341 sfcgal_geometry_type( geom, &typeChar, &typeLen );
342 std::string typeStr( typeChar, typeLen );
343 sfcgal_free_buffer( typeChar );
344
345 return QString::fromStdString( typeStr );
346#endif
347}
348
349sfcgal::shared_geom QgsSfcgalEngine::fromWkb( const QgsConstWkbPtr &wkbPtr, QString *errorMsg )
350{
351 sfcgal::errorHandler()->clearText( errorMsg );
352
353 const unsigned char *wkbUnsignedPtr = wkbPtr;
354 sfcgal::geometry *out = sfcgal_io_read_wkb( reinterpret_cast<const char *>( wkbUnsignedPtr ), wkbPtr.remaining() );
355 CHECK_SUCCESS( errorMsg, nullptr );
356
357 return sfcgal::make_shared_geom( out );
358}
359
360sfcgal::shared_geom QgsSfcgalEngine::fromWkt( const QString &wkt, QString *errorMsg )
361{
362 sfcgal::errorHandler()->clearText( errorMsg );
363
364 sfcgal::geometry *out = sfcgal_io_read_wkt( wkt.toStdString().c_str(), wkt.length() );
365 CHECK_SUCCESS( errorMsg, nullptr );
366
367 return sfcgal::unique_geom( out );
368}
369
370QByteArray QgsSfcgalEngine::toWkb( const sfcgal::geometry *geom, QString *errorMsg )
371{
372 sfcgal::errorHandler()->clearText( errorMsg );
373 CHECK_NOT_NULL( geom, QByteArray() );
374
375 char *wkbHex;
376 size_t len = 0;
377 sfcgal_geometry_as_wkb( geom, &wkbHex, &len );
378 QByteArray wkbArray( wkbHex, static_cast<int>( len ) );
379
380#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
381 sfcgal_free_buffer( wkbHex );
382#else
383 free( wkbHex );
384#endif
385
386 CHECK_SUCCESS( errorMsg, QByteArray() );
387 return wkbArray;
388}
389
390QString QgsSfcgalEngine::toWkt( const sfcgal::geometry *geom, int numDecimals, QString *errorMsg )
391{
392 sfcgal::errorHandler()->clearText( errorMsg );
393 CHECK_NOT_NULL( geom, QString() );
394
395 char *wkt;
396 size_t len = 0;
397 sfcgal_geometry_as_text_decim( geom, numDecimals, &wkt, &len );
398 CHECK_SUCCESS( errorMsg, QString() );
399
400 std::string wktString( wkt, len );
401#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
402 sfcgal_free_buffer( wkt );
403#else
404 free( wkt );
405#endif
406 return QString::fromStdString( wktString );
407}
408
409Qgis::WkbType QgsSfcgalEngine::wkbType( const sfcgal::geometry *geom, QString *errorMsg )
410{
411 sfcgal::errorHandler()->clearText( errorMsg );
412 CHECK_NOT_NULL( geom, Qgis::WkbType::Unknown );
413
414 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
415 CHECK_SUCCESS( errorMsg, Qgis::WkbType::Unknown );
416
417 int wkbType = type;
418 if ( sfcgal_geometry_is_3d( geom ) )
419 wkbType += 1000;
420
421 if ( sfcgal_geometry_is_measured( geom ) )
422 wkbType += 2000;
423
424 Qgis::WkbType qgisType = static_cast<Qgis::WkbType>( wkbType );
425 if ( qgisType >= Qgis::WkbType::Unknown && qgisType <= Qgis::WkbType::TriangleZM )
426 return qgisType;
427
428 sfcgal::errorHandler()->addText( QStringLiteral( "WKB type '%1' is not known from QGIS" ).arg( wkbType ), //
429 __FILE__, __FUNCTION__, __LINE__ );
431}
432
433int QgsSfcgalEngine::dimension( const sfcgal::geometry *geom, QString *errorMsg )
434{
435#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
436 ( void )geom;
437 ( void )errorMsg;
438 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "dimension" ) );
439#else
440 int out = geom_to_primtype<int>( sfcgal_geometry_dimension, nullptr, geom, errorMsg );
441 CHECK_SUCCESS( errorMsg, std::numeric_limits<int>::quiet_NaN() );
442 return out;
443#endif
444}
445
446int QgsSfcgalEngine::partCount( const sfcgal::geometry *geom, QString *errorMsg )
447{
448 size_t out;
449 sfcgal::errorHandler()->clearText( errorMsg );
450 CHECK_NOT_NULL( geom, -1 );
451
452 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
453 CHECK_SUCCESS( errorMsg, -1 );
454
455 switch ( type )
456 {
457 case SFCGAL_TYPE_MULTIPOINT:
458 case SFCGAL_TYPE_MULTILINESTRING:
459 case SFCGAL_TYPE_MULTIPOLYGON:
460 case SFCGAL_TYPE_MULTISOLID:
461 case SFCGAL_TYPE_GEOMETRYCOLLECTION:
462#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
463 out = sfcgal_geometry_num_geometries( geom );
464#else
465 out = sfcgal_geometry_collection_num_geometries( geom );
466#endif
467 break;
468 case SFCGAL_TYPE_POLYGON:
469 out = sfcgal_polygon_num_interior_rings( geom ) + 1;
470 break;
471 case SFCGAL_TYPE_SOLID:
472 out = sfcgal_solid_num_shells( geom );
473 break;
474 case SFCGAL_TYPE_POLYHEDRALSURFACE:
475#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
476 out = sfcgal_polyhedral_surface_num_patches( geom );
477#else
478 out = sfcgal_polyhedral_surface_num_polygons( geom );
479#endif
480 break;
481 case SFCGAL_TYPE_TRIANGULATEDSURFACE:
482#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
483 out = sfcgal_triangulated_surface_num_patches( geom );
484#else
485 out = sfcgal_triangulated_surface_num_triangles( geom );
486#endif
487 break;
488 case SFCGAL_TYPE_LINESTRING:
489 out = sfcgal_linestring_num_points( geom );
490 break;
491 case SFCGAL_TYPE_TRIANGLE:
492 out = 3;
493 break;
494 case SFCGAL_TYPE_POINT:
495 out = 1;
496 break;
497 default:
498 out = -1;
499 }
500
501 CHECK_SUCCESS( errorMsg, -1 );
502
503 return static_cast<int>( out );
504}
505
506bool QgsSfcgalEngine::addZValue( sfcgal::geometry *geom, double zValue, QString *errorMsg )
507{
508#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
509 ( void )geom;
510 ( void )zValue;
511 ( void )errorMsg;
512 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "addZValue" ) );
513#else
514 sfcgal::errorHandler()->clearText( errorMsg );
515 CHECK_NOT_NULL( geom, false );
516
517 return sfcgal_geometry_force_z( geom, zValue );
518#endif
519}
520
521bool QgsSfcgalEngine::addMValue( sfcgal::geometry *geom, double mValue, QString *errorMsg )
522{
523#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
524 ( void )geom;
525 ( void )mValue;
526 ( void )errorMsg;
527 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "addMValue" ) );
528#else
529 sfcgal::errorHandler()->clearText( errorMsg );
530 CHECK_NOT_NULL( geom, false );
531
532 return sfcgal_geometry_force_m( geom, mValue );
533#endif
534}
535
536bool QgsSfcgalEngine::dropZValue( sfcgal::geometry *geom, QString *errorMsg )
537{
538#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
539 ( void )geom;
540 ( void )errorMsg;
541 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "dropZValue" ) );
542#else
543 sfcgal::errorHandler()->clearText( errorMsg );
544 CHECK_NOT_NULL( geom, false );
545
546 return sfcgal_geometry_drop_z( geom );
547#endif
548}
549
550bool QgsSfcgalEngine::dropMValue( sfcgal::geometry *geom, QString *errorMsg )
551{
552#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
553 ( void )geom;
554 ( void )errorMsg;
555 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "dropMValue" ) );
556#else
557 sfcgal::errorHandler()->clearText( errorMsg );
558 CHECK_NOT_NULL( geom, false );
559
560 return sfcgal_geometry_drop_m( geom );
561#endif
562}
563
564void QgsSfcgalEngine::swapXy( sfcgal::geometry *geom, QString *errorMsg )
565{
566#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
567 ( void )geom;
568 ( void )errorMsg;
569 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "swapXy" ) );
570#else
571 sfcgal::errorHandler()->clearText( errorMsg );
572 CHECK_NOT_NULL( geom, void() );
573
574 sfcgal_geometry_swap_xy( geom );
575#endif
576}
577
578bool QgsSfcgalEngine::isEqual( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, double tolerance, QString *errorMsg )
579{
580#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
581 ( void )geomA;
582 ( void )geomB;
583 ( void )tolerance;
584 ( void )errorMsg;
585 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "isEqual" ) );
586#else
587 sfcgal::errorHandler()->clearText( errorMsg );
588 CHECK_NOT_NULL( geomA, false );
589 CHECK_NOT_NULL( geomB, false );
590
591 bool result = sfcgal_geometry_is_almost_equals( geomA, geomB, tolerance );
592 CHECK_SUCCESS( errorMsg, false );
593
594 return result;
595#endif
596}
597
598bool QgsSfcgalEngine::isEmpty( const sfcgal::geometry *geom, QString *errorMsg )
599{
600 int res = geom_to_primtype<int>( sfcgal_geometry_is_empty, nullptr, geom, errorMsg );
601 CHECK_SUCCESS( errorMsg, false );
602 return static_cast<bool>( res );
603}
604
605bool QgsSfcgalEngine::isValid( const sfcgal::geometry *geom, QString *errorMsg, QgsGeometry *errorLoc )
606{
607 sfcgal::errorHandler()->clearText( errorMsg );
608 CHECK_NOT_NULL( geom, false );
609
610 bool result = false;
611 char *reason;
612 sfcgal::geometry *location;
613 result = sfcgal_geometry_is_valid_detail( geom, &reason, &location );
614
615 CHECK_SUCCESS( errorMsg, false );
616
617 if ( reason && strlen( reason ) )
618 {
619 sfcgal::errorHandler()->addText( QString( reason ) );
620 free( reason );
621 }
622
623 if ( location && errorLoc )
624 {
625 std::unique_ptr<QgsAbstractGeometry> locationGeom = toAbstractGeometry( location, errorMsg );
626 CHECK_SUCCESS( errorMsg, false );
627 errorLoc->addPartV2( locationGeom.release() );
628 }
629
630 return result;
631}
632
633bool QgsSfcgalEngine::isSimple( const sfcgal::geometry *geom, QString *errorMsg )
634{
635#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
636 ( void )geom;
637 ( void )errorMsg;
638 throw QgsNotSupportedException( QObject::tr( "Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "isSimple" ) );
639#else
640 int res = geom_to_primtype<int>( sfcgal_geometry_is_simple, nullptr, geom, errorMsg );
641 CHECK_SUCCESS( errorMsg, false );
642 return static_cast<bool>( res );
643#endif
644}
645
646sfcgal::shared_geom QgsSfcgalEngine::boundary( const sfcgal::geometry *geom, QString *errorMsg )
647{
648#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
649 ( void )geom;
650 ( void )errorMsg;
651 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "boundary" ) );
652#else
653 sfcgal::errorHandler()->clearText( errorMsg );
654 CHECK_NOT_NULL( geom, nullptr );
655
656 sfcgal::geometry *boundary = sfcgal_geometry_boundary( geom );
657 CHECK_SUCCESS( errorMsg, nullptr );
658
659 return sfcgal::make_shared_geom( boundary );
660#endif
661}
662
663QgsPoint QgsSfcgalEngine::centroid( const sfcgal::geometry *geom, QString *errorMsg )
664{
665#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
666 ( void )geom;
667 ( void )errorMsg;
668 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "centroid" ) );
669#else
670 sfcgal::errorHandler()->clearText( errorMsg );
671 CHECK_NOT_NULL( geom, QgsPoint() );
672
673 const sfcgal::geometry *result = nullptr;
674 if ( sfcgal_geometry_is_3d( geom ) )
675 result = sfcgal_geometry_centroid_3d( geom );
676 else
677 result = sfcgal_geometry_centroid( geom );
678
679 CHECK_SUCCESS( errorMsg, QgsPoint() );
680 CHECK_NOT_NULL( result, QgsPoint() );
681
682 QByteArray wkbArray = QgsSfcgalEngine::toWkb( result, errorMsg );
683 QgsConstWkbPtr wkbPtr( wkbArray );
684 QgsPoint out;
685 out.fromWkb( wkbPtr );
686
687 return out;
688#endif
689}
690
691sfcgal::shared_geom QgsSfcgalEngine::translate( const sfcgal::geometry *geom, const QgsVector3D &translation, QString *errorMsg )
692{
693#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
694 ( void )geom;
695 ( void )translation;
696 ( void )errorMsg;
697 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "translate" ) );
698#else
699 sfcgal::errorHandler()->clearText( errorMsg );
700 CHECK_NOT_NULL( geom, nullptr );
701
702 sfcgal::geometry *result;
703 if ( sfcgal_geometry_is_3d( geom ) )
704 result = sfcgal_geometry_translate_3d( geom, translation.x(), translation.y(), translation.z() );
705 else
706 result = sfcgal_geometry_translate_2d( geom, translation.x(), translation.y() );
707 CHECK_SUCCESS( errorMsg, nullptr );
708
709 return sfcgal::make_shared_geom( result );
710#endif
711}
712
713sfcgal::shared_geom QgsSfcgalEngine::scale( const sfcgal::geometry *geom, const QgsVector3D &scaleFactor, const QgsPoint &center, QString *errorMsg )
714{
715 sfcgal::errorHandler()->clearText( errorMsg );
716 CHECK_NOT_NULL( geom, nullptr );
717
718 sfcgal::geometry *result;
719 if ( center.isEmpty() )
720 {
721 result = sfcgal_geometry_scale_3d( geom, scaleFactor.x(), scaleFactor.y(), scaleFactor.z() );
722 }
723 else
724 {
725 const double centerZ = center.is3D() ? center.z() : 0;
726 result = sfcgal_geometry_scale_3d_around_center( geom, scaleFactor.x(), scaleFactor.y(), scaleFactor.z(), center.x(), center.y(), centerZ );
727 }
728
729 CHECK_SUCCESS( errorMsg, nullptr );
730 return sfcgal::make_shared_geom( result );
731}
732
733sfcgal::shared_geom QgsSfcgalEngine::rotate2D( const sfcgal::geometry *geom, double angle, const QgsPoint &center, QString *errorMsg )
734{
735 sfcgal::errorHandler()->clearText( errorMsg );
736 CHECK_NOT_NULL( geom, nullptr );
737
738 sfcgal::geometry *result = sfcgal_geometry_rotate_2d( geom, angle, center.x(), center.y() );
739
740 CHECK_SUCCESS( errorMsg, nullptr );
741 return sfcgal::make_shared_geom( result );
742}
743
744sfcgal::shared_geom QgsSfcgalEngine::rotate3D( const sfcgal::geometry *geom, double angle, const QgsVector3D &axisVector, const QgsPoint &center, QString *errorMsg )
745{
746 sfcgal::errorHandler()->clearText( errorMsg );
747 CHECK_NOT_NULL( geom, nullptr );
748
749 sfcgal::geometry *result;
750 if ( center.isEmpty() )
751 {
752 result = sfcgal_geometry_rotate_3d( geom, angle, axisVector.x(), axisVector.y(), axisVector.z() );
753 }
754 else
755 {
756 result = sfcgal_geometry_rotate_3d_around_center( geom, angle, axisVector.x(), axisVector.y(), axisVector.z(), center.x(), center.y(), center.z() );
757 }
758
759 CHECK_SUCCESS( errorMsg, nullptr );
760 return sfcgal::make_shared_geom( result );
761}
762
763double QgsSfcgalEngine::distance( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, QString *errorMsg )
764{
765 double out = geomgeom_to_primtype<double>( sfcgal_geometry_distance, sfcgal_geometry_distance_3d, geomA, geomB, errorMsg );
766 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
767 return out;
768}
769
770bool QgsSfcgalEngine::distanceWithin( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, double maxdistance, QString *errorMsg )
771{
772 double dist = QgsSfcgalEngine::distance( geomA, geomB, errorMsg );
773 CHECK_SUCCESS( errorMsg, false );
774
775 return dist <= maxdistance;
776}
777
778double QgsSfcgalEngine::area( const sfcgal::geometry *geom, QString *errorMsg )
779{
780 double out = geom_to_primtype<double>( sfcgal_geometry_area, sfcgal_geometry_area_3d, geom, errorMsg );
781 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
782 return out;
783}
784
785double QgsSfcgalEngine::length( const sfcgal::geometry *geom, QString *errorMsg )
786{
787#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
788 ( void )geom;
789 ( void )errorMsg;
790 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "length" ) );
791#else
792 double out = geom_to_primtype<double>( sfcgal_geometry_length, sfcgal_geometry_length_3d, geom, errorMsg );
793 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
794 return out;
795#endif
796}
797
798bool QgsSfcgalEngine::intersects( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, QString *errorMsg )
799{
800 int res = geomgeom_to_primtype<int>( sfcgal_geometry_intersects, sfcgal_geometry_intersects_3d, geomA, geomB, errorMsg );
801 CHECK_SUCCESS( errorMsg, false );
802 return static_cast<bool>( res );
803}
804
805sfcgal::shared_geom QgsSfcgalEngine::intersection( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, QString *errorMsg )
806{
807 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_intersection, sfcgal_geometry_intersection_3d, geomA, geomB, errorMsg );
808 CHECK_SUCCESS( errorMsg, nullptr );
809 return out;
810}
811
812sfcgal::shared_geom QgsSfcgalEngine::difference( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, QString *errorMsg )
813{
814 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_difference, sfcgal_geometry_difference_3d, geomA, geomB, errorMsg );
815 CHECK_SUCCESS( errorMsg, nullptr );
816 return out;
817}
818
819sfcgal::shared_geom QgsSfcgalEngine::combine( const QVector<sfcgal::shared_geom> &geomList, QString *errorMsg )
820{
821 sfcgal::errorHandler()->clearText( errorMsg );
822 sfcgal::geometry *combined = nullptr;
823 for ( sfcgal::shared_geom other : geomList )
824 {
825 if ( !combined )
826 {
827 combined = other.get();
828 continue;
829 }
830
831 if ( sfcgal_geometry_is_3d( other.get() ) || sfcgal_geometry_is_3d( combined ) )
832 combined = sfcgal_geometry_union_3d( combined, other.get() );
833 else
834 combined = sfcgal_geometry_union( combined, other.get() );
835
836 if ( !combined )
837 sfcgal::errorHandler()->addText( "SFCGAL produced null result." );
838
839 CHECK_SUCCESS( errorMsg, nullptr );
840 }
841
842 return sfcgal::make_shared_geom( combined );
843}
844
845sfcgal::shared_geom QgsSfcgalEngine::triangulate( const sfcgal::geometry *geom, QString *errorMsg )
846{
847 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_triangulate_2dz, nullptr, geom, errorMsg );
848 CHECK_SUCCESS( errorMsg, nullptr );
849 return out;
850}
851
852bool QgsSfcgalEngine::covers( const sfcgal::geometry *geomA, const sfcgal::geometry *geomB, QString *errorMsg )
853{
854 int res = geomgeom_to_primtype<int>( sfcgal_geometry_covers, sfcgal_geometry_covers_3d, geomA, geomB, errorMsg );
855 CHECK_SUCCESS( errorMsg, false );
856 return static_cast<bool>( res );
857}
858
859sfcgal::shared_geom QgsSfcgalEngine::envelope( const sfcgal::geometry *geom, QString *errorMsg )
860{
861#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
862 ( void )geom;
863 ( void )errorMsg;
864 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "envelope" ) );
865#else
866 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_envelope, sfcgal_geometry_envelope_3d, geom, errorMsg );
867 CHECK_SUCCESS( errorMsg, nullptr );
868 return out;
869#endif
870}
871
872sfcgal::shared_geom QgsSfcgalEngine::convexHull( const sfcgal::geometry *geom, QString *errorMsg )
873{
874 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_convexhull, sfcgal_geometry_convexhull_3d, geom, errorMsg );
875 CHECK_SUCCESS( errorMsg, nullptr );
876 return out;
877}
878
879sfcgal::shared_geom QgsSfcgalEngine::offsetCurve( const sfcgal::geometry *geom, double distance, int, Qgis::JoinStyle, QString *errorMsg )
880{
881 sfcgal::errorHandler()->clearText( errorMsg );
882 CHECK_NOT_NULL( geom, nullptr );
883
884 sfcgal::geometry *result = nullptr;
885 result = sfcgal_geometry_offset_polygon( geom, distance );
886
887 CHECK_SUCCESS( errorMsg, nullptr );
888
889 return sfcgal::make_shared_geom( result );
890}
891
892sfcgal::shared_geom QgsSfcgalEngine::buffer2D( const sfcgal::geometry *geom, double radius, int segments, Qgis::JoinStyle joinStyle, QString *errorMsg )
893{
894 if ( joinStyle != Qgis::JoinStyle::Round )
895 qWarning() << ( QStringLiteral( "Buffer not implemented for %1! Defaulting to round join." ) );
896
897 return offsetCurve( geom, radius, segments, joinStyle, errorMsg );
898}
899
900sfcgal::shared_geom QgsSfcgalEngine::buffer3D( const sfcgal::geometry *geom, double radius, int segments, Qgis::JoinStyle3D joinStyle3D, QString *errorMsg )
901{
902 sfcgal::errorHandler()->clearText( errorMsg );
903 CHECK_NOT_NULL( geom, nullptr );
904
905 sfcgal_buffer3d_type_t buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
906 switch ( joinStyle3D )
907 {
909 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
910 break;
912 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_ROUND;
913 break;
915 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_CYLSPHERE;
916 break;
917 }
918
919 sfcgal::geometry *result = sfcgal_geometry_buffer3d( geom, radius, segments, buffer_type );
920 CHECK_SUCCESS( errorMsg, nullptr );
921
922 return sfcgal::make_shared_geom( result );
923}
924
925sfcgal::shared_geom QgsSfcgalEngine::extrude( const sfcgal::geometry *geom, const QgsVector3D &extrusion, QString *errorMsg )
926{
927 sfcgal::errorHandler()->clearText( errorMsg );
928 CHECK_NOT_NULL( geom, nullptr );
929
930 sfcgal_geometry_t *solid = sfcgal_geometry_extrude( geom, extrusion.x(), extrusion.y(), extrusion.z() );
931
932 CHECK_SUCCESS( errorMsg, nullptr );
933
934 // sfcgal_geometry_extrude returns a SOLID
935 // This is not handled by QGIS
936 // convert it to a PolyhedralSurface
937 sfcgal_geometry_t *polySurface = sfcgal_polyhedral_surface_create();
938 for ( unsigned int shellIdx = 0; shellIdx < sfcgal_solid_num_shells( solid ); ++shellIdx )
939 {
940 const sfcgal_geometry_t *shell = sfcgal_solid_shell_n( solid, shellIdx );
941#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
942 for ( unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_patches( shell ); ++polyIdx )
943 {
944 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_patch_n( shell, polyIdx );
945 sfcgal_polyhedral_surface_add_patch( polySurface, sfcgal_geometry_clone( patch ) );
946 }
947#else
948 for ( unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_polygons( shell ); ++polyIdx )
949 {
950 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_polygon_n( shell, polyIdx );
951 sfcgal_polyhedral_surface_add_polygon( polySurface, sfcgal_geometry_clone( patch ) );
952 }
953#endif
954 }
955
956 sfcgal_geometry_delete( solid );
957
958 CHECK_SUCCESS( errorMsg, nullptr );
959
960 return sfcgal::make_shared_geom( polySurface );
961}
962
963sfcgal::shared_geom QgsSfcgalEngine::simplify( const sfcgal::geometry *geom, double tolerance, bool preserveTopology, QString *errorMsg )
964{
965#if SFCGAL_VERSION < SFCGAL_MAKE_VERSION( 2, 1, 0 )
966 ( void )geom;
967 ( void )tolerance;
968 ( void )preserveTopology;
969 ( void )errorMsg;
970 throw QgsNotSupportedException( QObject::tr( "Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg( "boundary" ) );
971#else
972 sfcgal::errorHandler()->clearText( errorMsg );
973 CHECK_NOT_NULL( geom, nullptr );
974
975 sfcgal::geometry *result = sfcgal_geometry_simplify( geom, tolerance, preserveTopology );
976 CHECK_SUCCESS( errorMsg, nullptr );
977
978 return sfcgal::make_shared_geom( result );
979#endif
980}
981
982sfcgal::shared_geom QgsSfcgalEngine::approximateMedialAxis( const sfcgal::geometry *geom, QString *errorMsg )
983{
984 sfcgal::errorHandler()->clearText( errorMsg );
985 CHECK_NOT_NULL( geom, nullptr );
986
987 sfcgal::geometry *result = sfcgal_geometry_approximate_medial_axis( geom );
988 CHECK_SUCCESS( errorMsg, nullptr );
989
990 return sfcgal::make_shared_geom( result );
991}
992
993
994#if SFCGAL_VERSION >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
995sfcgal::shared_geom QgsSfcgalEngine::transform( const sfcgal::geometry *geom, const QMatrix4x4 &mat, QString *errorMsg )
996{
997 sfcgal::errorHandler()->clearText( errorMsg );
998 CHECK_NOT_NULL( geom, nullptr );
999
1000 sfcgal::geometry *result;
1001 result = sfcgal_geometry_transform( geom, mat.constData() );
1002
1003 CHECK_SUCCESS( errorMsg, nullptr );
1004 return sfcgal::make_shared_geom( result );
1005}
1006
1007std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_prim &prim, sfcgal::primitiveType type, QString *errorMsg )
1008{
1009 sfcgal::errorHandler()->clearText( errorMsg );
1010 CHECK_NOT_NULL( prim.get(), nullptr );
1011
1012 return std::make_unique<QgsSfcgalGeometry>( prim, type );
1013}
1014
1015sfcgal::shared_prim QgsSfcgalEngine::createCube( double size, QString *errorMsg )
1016{
1017 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CUBE );
1018 CHECK_SUCCESS( errorMsg, nullptr );
1019
1020 sfcgal_primitive_set_parameter_double( result, "size", size );
1021 CHECK_SUCCESS( errorMsg, nullptr );
1022
1023 return sfcgal::make_shared_prim( result );
1024}
1025
1026sfcgal::shared_geom QgsSfcgalEngine::primitiveAsPolyhedral( const sfcgal::primitive *prim, const QMatrix4x4 &mat, QString *errorMsg )
1027{
1028 sfcgal::errorHandler()->clearText( errorMsg );
1029 CHECK_NOT_NULL( prim, nullptr );
1030
1031 sfcgal::geometry *result = sfcgal_primitive_as_polyhedral_surface( prim );
1032 CHECK_SUCCESS( errorMsg, nullptr );
1033
1034 if ( !mat.isIdentity() )
1035 {
1036 sfcgal::geometry *result2 = sfcgal_geometry_transform( result, mat.constData() );
1037 sfcgal_geometry_delete( result );
1038 result = result2;
1039 CHECK_SUCCESS( errorMsg, nullptr );
1040 }
1041
1042 return sfcgal::make_shared_geom( result );
1043}
1044
1045bool QgsSfcgalEngine::primitiveIsEqual( const sfcgal::primitive *primA, const sfcgal::primitive *primB, double tolerance, QString *errorMsg )
1046{
1047 sfcgal::errorHandler()->clearText( errorMsg );
1048 CHECK_NOT_NULL( primA, false );
1049 CHECK_NOT_NULL( primB, false );
1050
1051 bool result = sfcgal_primitive_is_almost_equals( primA, primB, tolerance );
1052 CHECK_SUCCESS( errorMsg, false );
1053
1054 return result;
1055}
1056
1057sfcgal::shared_prim QgsSfcgalEngine::primitiveClone( const sfcgal::primitive *prim, QString *errorMsg )
1058{
1059 sfcgal::errorHandler()->clearText( errorMsg );
1060 CHECK_NOT_NULL( prim, nullptr );
1061
1062 sfcgal::primitive *result = sfcgal_primitive_clone( prim );
1063
1064 CHECK_SUCCESS( errorMsg, nullptr );
1065 CHECK_NOT_NULL( result, nullptr );
1066
1067 return sfcgal::make_shared_prim( result );
1068}
1069
1070double QgsSfcgalEngine::primitiveArea( const sfcgal::primitive *prim, bool withDiscretization, QString *errorMsg )
1071{
1072 sfcgal::errorHandler()->clearText( errorMsg );
1073 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1074
1075 double out = sfcgal_primitive_area( prim, withDiscretization );
1076 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1077 return out;
1078}
1079
1080double QgsSfcgalEngine::primitiveVolume( const sfcgal::primitive *prim, bool withDiscretization, QString *errorMsg )
1081{
1082 sfcgal::errorHandler()->clearText( errorMsg );
1083 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1084
1085 double out = sfcgal_primitive_volume( prim, withDiscretization );
1086 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1087 return out;
1088}
1089
1090void sfcgal::to_json( json &j, const sfcgal::PrimitiveParameterDesc &p )
1091{
1092 j["name"] = p.name;
1093 j["type"] = p.type;
1094
1095 if ( std::holds_alternative<int>( p.value ) )
1096 {
1097 j["value"] = std::get<int>( p.value );
1098 }
1099 else if ( std::holds_alternative<double>( p.value ) )
1100 {
1101 j["value"] = std::get<double>( p.value );
1102 }
1103 else if ( std::holds_alternative<QgsPoint>( p.value ) )
1104 {
1105 QgsPoint point = std::get<QgsPoint>( p.value );
1106 double z = std::numeric_limits<double>::quiet_NaN();
1107 double m = std::numeric_limits<double>::quiet_NaN();
1108 if ( point.is3D() )
1109 z = point.z();
1110 if ( point.isMeasure() )
1111 m = point.m();
1112 j["value"] = std::vector<double> { point.x(), point.y(), z, m };
1113 }
1114 else if ( std::holds_alternative<QgsVector3D>( p.value ) )
1115 {
1116 QgsVector3D vect = std::get<QgsVector3D>( p.value );
1117 j["value"] = std::vector<double> { vect.x(), vect.y(), vect.z() };
1118 }
1119 else
1120 throw json::type_error::create( 306, QStringLiteral( "Unknown type '%1'." ).arg( p.type.c_str() ).toStdString(), nullptr );
1121}
1122
1123void sfcgal::from_json( const json &j, sfcgal::PrimitiveParameterDesc &p )
1124{
1125 j.at( "name" ).get_to( p.name );
1126 j.at( "type" ).get_to( p.type );
1127 if ( j.contains( "value" ) )
1128 {
1129 json value = j.at( "value" );
1130 if ( p.type == "int" )
1131 {
1132 p.value = value.get<int>();
1133 }
1134 else if ( p.type == "double" )
1135 {
1136 p.value = value.get<double>();
1137 }
1138 else if ( p.type == "point3" )
1139 {
1140 std::vector<double> vect;
1141 vect = value.get<std::vector<double>>();
1142 QgsPoint point( vect[0], vect[1], //
1143 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ), //
1144 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() ) );
1145 p.value = point;
1146 }
1147 else if ( p.type == "vector3" )
1148 {
1149 std::vector<double> vect;
1150 vect = value.get<std::vector<double>>();
1151 QgsPoint point( vect[0], vect[1], //
1152 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ), //
1153 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() ) );
1154 p.value = point;
1155 }
1156 else
1157 throw json::type_error::create( 306, QStringLiteral( "Unknown type '%1'." ).arg( p.type.c_str() ).toStdString(), nullptr );
1158 }
1159}
1160
1161QVector<sfcgal::PrimitiveParameterDesc> QgsSfcgalEngine::primitiveParameters( const sfcgal::primitive *prim, QString *errorMsg )
1162{
1163 sfcgal::errorHandler()->clearText( errorMsg );
1164 CHECK_NOT_NULL( prim, QVector<sfcgal::PrimitiveParameterDesc>() );
1165
1166 char *jsonChars = nullptr;
1167 size_t len = 0;
1168 sfcgal_primitive_parameters( prim, &jsonChars, &len );
1169 CHECK_SUCCESS( errorMsg, QVector<sfcgal::PrimitiveParameterDesc>() );
1170
1171 std::string jsonString( jsonChars, len );
1172 sfcgal_free_buffer( jsonChars );
1173
1174 QVector<sfcgal::PrimitiveParameterDesc> result;
1175 try
1176 {
1177 const auto jParams = json::parse( jsonString );
1178 for ( const auto &jParam : jParams )
1179 {
1180 result.append( jParam.get<sfcgal::PrimitiveParameterDesc>() );
1181 }
1182 }
1183 catch ( json::exception &e )
1184 {
1185 sfcgal::errorHandler()->addText( QStringLiteral( "Caught json exception for json: %1. Error: %2" ).arg( jsonString.c_str() ).arg( e.what() ) );
1186 }
1187
1188 return result;
1189}
1190
1191QVariant QgsSfcgalEngine::primitiveParameter( const sfcgal::primitive *prim, const QString &name, QString *errorMsg )
1192{
1193 sfcgal::errorHandler()->clearText( errorMsg );
1194 CHECK_NOT_NULL( prim, QVariant() );
1195
1196 char *jsonChars = nullptr;
1197 size_t len = 0;
1198 sfcgal_primitive_parameter( prim, name.toStdString().c_str(), &jsonChars, &len );
1199 CHECK_SUCCESS( errorMsg, QVariant() );
1200
1201 std::string jsonString( jsonChars, len );
1202 sfcgal_free_buffer( jsonChars );
1203
1204 QVariant result;
1205 try
1206 {
1207 const auto jParam = json::parse( jsonString );
1208 sfcgal::PrimitiveParameterDesc param = jParam.get<sfcgal::PrimitiveParameterDesc>();
1209 result = QVariant::fromStdVariant( param.value );
1210 }
1211 catch ( json::exception &e )
1212 {
1213 sfcgal::errorHandler()->addText( QStringLiteral( "Caught json exception for json: %1. Error: %2" ).arg( jsonString.c_str() ).arg( e.what() ) );
1214 }
1215
1216 return result;
1217}
1218
1219void QgsSfcgalEngine::primitiveSetParameter( sfcgal::primitive *prim, const QString &name, const QVariant &value, QString *errorMsg )
1220{
1221 sfcgal::errorHandler()->clearText( errorMsg );
1222 CHECK_NOT_NULL( prim, void() );
1223
1224 try
1225 {
1226 json jParam;
1227 sfcgal::PrimitiveParameterDesc paramDesc;
1228 paramDesc.name = name.toStdString();
1229 paramDesc.type = value.typeName();
1230 if ( paramDesc.type == "int" )
1231 paramDesc.value = value.toInt();
1232 else if ( paramDesc.type == "double" )
1233 paramDesc.value = value.toDouble();
1234 else if ( value.canConvert<QgsPoint>() )
1235 paramDesc.value = value.value<QgsPoint>();
1236 else if ( value.canConvert<QgsVector3D>() )
1237 paramDesc.value = value.value<QgsVector3D>();
1238
1239 sfcgal::to_json( jParam, paramDesc );
1240 std::string jsonStr = jParam.dump();
1241 sfcgal_primitive_set_parameter( prim, name.toStdString().c_str(), jsonStr.c_str() );
1242 CHECK_SUCCESS( errorMsg, void() );
1243 }
1244 catch ( ... )
1245 {
1246 sfcgal::errorHandler()->addText( QStringLiteral( "Caught json exception" ) );
1247 }
1248}
1249
1250#endif
1251
1252
1253#endif // #ifdef WITH_SFCGAL
JoinStyle3D
Join styles for 3D buffers.
Definition qgis.h:2134
@ CylindersAndSpheres
Cylinders along the linestring segments with spheres at the vertices.
Definition qgis.h:2137
@ Flat
Flat ends and constant width along the linestring.
Definition qgis.h:2136
@ Round
Smooth, rounded buffer around the input geometry.
Definition qgis.h:2135
JoinStyle
Join styles for buffers.
Definition qgis.h:2121
@ Round
Use rounded joins.
Definition qgis.h:2122
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:277
@ Unknown
Unknown.
Definition qgis.h:278
@ TriangleZM
TriangleZM.
Definition qgis.h:339
Abstract base class for all geometries.
bool isMeasure() const
Returns true if the geometry contains m values.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
virtual QByteArray asWkb(WkbFlags flags=QgsAbstractGeometry::WkbFlags()) const =0
Returns a WKB representation of the geometry.
A const WKB pointer.
Definition qgswkbptr.h:139
int remaining() const
remaining
Definition qgswkbptr.h:196
static std::unique_ptr< QgsAbstractGeometry > geomFromWkb(QgsConstWkbPtr &wkb)
Construct geometry from a WKB string.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult addPartV2(const QVector< QgsPointXY > &points, Qgis::WkbType wkbType=Qgis::WkbType::Unknown)
Adds a new part to a the geometry.
Custom exception class which is raised when an operation is not supported.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
Definition qgspoint.cpp:144
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:739
double m
Definition qgspoint.h:55
double y
Definition qgspoint.h:53
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:51
double x() const
Returns X coordinate.
Definition qgsvector3d.h:47