21#include <SFCGAL/capi/sfcgal_c.h>
22#include <nlohmann/json.hpp>
30using namespace Qt::StringLiterals;
36thread_local sfcgal::ErrorHandler sSfcgalErrorHandler;
38sfcgal::ErrorHandler *sfcgal::errorHandler()
40 return &sSfcgalErrorHandler;
43void sfcgal::GeometryDeleter::operator()( sfcgal::geometry *geom )
const
45 sfcgal_geometry_delete( geom );
48sfcgal::shared_geom sfcgal::make_shared_geom( sfcgal::geometry *geom )
50 return sfcgal::shared_geom( geom, sfcgal::GeometryDeleter() );
54#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
55void sfcgal::PrimitiveDeleter::operator()( sfcgal::primitive *prim )
const
57 sfcgal_primitive_delete( prim );
60sfcgal::shared_prim sfcgal::make_shared_prim( sfcgal::primitive *prim )
62 return sfcgal::shared_prim( prim, sfcgal::PrimitiveDeleter() );
66bool sfcgal::ErrorHandler::hasSucceedOrStack( QString *errorMsg,
const std::source_location &location )
68 bool succeed = isTextEmpty();
71 addText(
"relaying error from: ", location );
74 errorMsg->append( errorMessages.first() );
80int sfcgal::errorCallback(
const char *fmt, ... )
86 vsnprintf( buffer,
sizeof buffer, fmt, ap );
89 sfcgal::errorHandler()->addText( u
"SFCGAL error occurred: %1"_s.arg( buffer ) );
91 return static_cast<int>( strlen( buffer ) );
94int sfcgal::warningCallback(
const char *fmt, ... )
100 vsnprintf( buffer,
sizeof buffer, fmt, ap );
103 sfcgal::errorHandler()->addText( u
"SFCGAL warning occurred: %1"_s.arg( buffer ) );
105 return static_cast<int>( strlen( buffer ) );
109sfcgal::ErrorHandler::ErrorHandler()
112 sfcgal_set_error_handlers( sfcgal::warningCallback, sfcgal::errorCallback );
115void sfcgal::ErrorHandler::clearText( QString *errorMsg )
117 errorMessages.clear();
124QString sfcgal::ErrorHandler::getMainText()
const
126 return errorMessages.isEmpty() ? QString() : QString(
"Error occurred: " ) + errorMessages.last();
129QString sfcgal::ErrorHandler::getFullText()
const
131 return errorMessages.isEmpty() ? QString() : getMainText() +
"\n\t\t" + errorMessages.join(
"\n\t\t" );
134bool sfcgal::ErrorHandler::isTextEmpty()
const
136 return errorMessages.isEmpty();
139void sfcgal::ErrorHandler::addText(
const QString &msg,
const std::source_location &location )
141 QString txt = QString(
"%2 (%3:%4) %1" )
143 .arg( QString::fromStdString( location.function_name() ) )
144 .arg( QString::fromStdString( location.file_name() ) )
145 .arg( location.line() );
147 errorMessages.push_front( txt );
162template<
typename T>
static T geom_to_primtype( T ( *func_2d )(
const sfcgal_geometry_t * ), T ( *func_3d )(
const sfcgal_geometry_t * ),
const sfcgal::geometry *geom, QString *errorMsg )
164 sfcgal::errorHandler()->clearText( errorMsg );
165 CHECK_NOT_NULL( geom, std::numeric_limits<T>::quiet_NaN() );
168 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
169 result = func_3d( geom );
171 result = func_2d( geom );
173 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
187template<
typename T>
static T geomgeom_to_primtype(
188 T ( *func_2d )(
const sfcgal_geometry_t *,
const sfcgal_geometry_t * ),
189 T ( *func_3d )(
const sfcgal_geometry_t *,
const sfcgal_geometry_t * ),
190 const sfcgal::geometry *geomA,
191 const sfcgal::geometry *geomB,
195 sfcgal::errorHandler()->clearText( errorMsg );
196 CHECK_NOT_NULL( geomA,
false );
197 CHECK_NOT_NULL( geomB,
false );
200 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
201 result = func_3d( geomA, geomB );
203 result = func_2d( geomA, geomB );
205 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
218static sfcgal::shared_geom geom_to_geom( sfcgal::func_geom_to_geom func_2d, sfcgal::func_geom_to_geom func_3d,
const sfcgal::geometry *geom, QString *errorMsg )
220 sfcgal::errorHandler()->clearText( errorMsg );
221 CHECK_NOT_NULL( geom,
nullptr );
223 sfcgal::geometry *result =
nullptr;
224 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
225 result = func_3d( geom );
227 result = func_2d( geom );
229 CHECK_SUCCESS( errorMsg,
nullptr );
230 CHECK_NOT_NULL( result,
nullptr );
232 return sfcgal::make_shared_geom( result );
245static sfcgal::shared_geom geomgeom_to_geom( sfcgal::func_geomgeom_to_geom func_2d, sfcgal::func_geomgeom_to_geom func_3d,
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
247 sfcgal::errorHandler()->clearText( errorMsg );
248 CHECK_NOT_NULL( geomA,
nullptr );
249 CHECK_NOT_NULL( geomB,
nullptr );
251 sfcgal::geometry *result =
nullptr;
252 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
253 result = func_3d( geomA, geomB );
255 result = func_2d( geomA, geomB );
257 CHECK_SUCCESS( errorMsg,
nullptr );
258 CHECK_NOT_NULL( result,
nullptr );
260 return sfcgal::make_shared_geom( result );
269std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_geom &geom, QString *errorMsg )
271 sfcgal::errorHandler()->clearText( errorMsg );
272 CHECK_NOT_NULL( geom.get(),
nullptr );
274 return std::make_unique<QgsSfcgalGeometry>( geom );
277std::unique_ptr<QgsAbstractGeometry> QgsSfcgalEngine::toAbstractGeometry(
const sfcgal::geometry *geom, QString *errorMsg )
279 std::unique_ptr<QgsAbstractGeometry> out(
nullptr );
280 sfcgal::errorHandler()->clearText( errorMsg );
281 CHECK_NOT_NULL( geom, out );
283 QByteArray wkbArray = QgsSfcgalEngine::toWkb( geom, errorMsg );
284 CHECK_SUCCESS( errorMsg, out );
291 sfcgal::errorHandler()->addText(
292 u
"WKB contains unmanaged geometry type (WKB:%1 / SFCGAL:%2"_s
293 .arg(
static_cast<int>( wkbPtr.readHeader() ) )
294 .arg(
static_cast<int>( sfcgalType ) )
301sfcgal::shared_geom QgsSfcgalEngine::fromAbstractGeometry(
const QgsAbstractGeometry *geom, QString *errorMsg )
303 sfcgal::errorHandler()->clearText( errorMsg );
304 CHECK_NOT_NULL( geom, sfcgal::shared_geom(
nullptr ) );
306 QByteArray wkbBytes = geom->
asWkb();
308 sfcgal::geometry *out = sfcgal_io_read_wkb( wkbBytes.data(), wkbBytes.length() );
309 CHECK_SUCCESS( errorMsg,
nullptr );
311 return sfcgal::make_shared_geom( out );
314sfcgal::shared_geom QgsSfcgalEngine::cloneGeometry(
const sfcgal::geometry *geom, QString *errorMsg )
316 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_clone,
nullptr, geom, errorMsg );
317 CHECK_SUCCESS( errorMsg,
nullptr );
321QString QgsSfcgalEngine::geometryType(
const sfcgal::geometry *geom, QString *errorMsg )
323#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
326 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"geometryType" ) );
328 sfcgal::errorHandler()->clearText( errorMsg );
332 sfcgal_geometry_type( geom, &typeChar, &typeLen );
333 std::string typeStr( typeChar, typeLen );
334 sfcgal_free_buffer( typeChar );
336 return QString::fromStdString( typeStr );
340sfcgal::shared_geom QgsSfcgalEngine::fromWkb(
const QgsConstWkbPtr &wkbPtr, QString *errorMsg )
342 sfcgal::errorHandler()->clearText( errorMsg );
344 const unsigned char *wkbUnsignedPtr = wkbPtr;
345 sfcgal::geometry *out = sfcgal_io_read_wkb(
reinterpret_cast<const char *
>( wkbUnsignedPtr ), wkbPtr.
remaining() );
346 CHECK_SUCCESS( errorMsg,
nullptr );
348 return sfcgal::make_shared_geom( out );
351sfcgal::shared_geom QgsSfcgalEngine::fromWkt(
const QString &wkt, QString *errorMsg )
353 sfcgal::errorHandler()->clearText( errorMsg );
355 sfcgal::geometry *out = sfcgal_io_read_wkt( wkt.toStdString().c_str(), wkt.length() );
356 CHECK_SUCCESS( errorMsg,
nullptr );
358 return sfcgal::unique_geom( out );
361QByteArray QgsSfcgalEngine::toWkb(
const sfcgal::geometry *geom, QString *errorMsg )
363 sfcgal::errorHandler()->clearText( errorMsg );
364 CHECK_NOT_NULL( geom, QByteArray() );
368 sfcgal_geometry_as_wkb( geom, &wkbHex, &len );
369 CHECK_SUCCESS( errorMsg, QByteArray() );
370 QByteArray wkbArray( wkbHex,
static_cast<int>( len ) );
372#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
373 sfcgal_free_buffer( wkbHex );
381QString QgsSfcgalEngine::toWkt(
const sfcgal::geometry *geom,
int numDecimals, QString *errorMsg )
383 sfcgal::errorHandler()->clearText( errorMsg );
384 CHECK_NOT_NULL( geom, QString() );
388 sfcgal_geometry_as_text_decim( geom, numDecimals, &wkt, &len );
389 CHECK_SUCCESS( errorMsg, QString() );
391 std::string wktString( wkt, len );
392#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
393 sfcgal_free_buffer( wkt );
397 return QString::fromStdString( wktString );
400Qgis::WkbType QgsSfcgalEngine::wkbType(
const sfcgal::geometry *geom, QString *errorMsg )
402 sfcgal::errorHandler()->clearText( errorMsg );
405 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
409 if ( sfcgal_geometry_is_3d( geom ) )
412 if ( sfcgal_geometry_is_measured( geom ) )
419 sfcgal::errorHandler()->addText( u
"WKB type '%1' is not known from QGIS"_s.arg( wkbType ) );
423int QgsSfcgalEngine::dimension(
const sfcgal::geometry *geom, QString *errorMsg )
425#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
428 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dimension" ) );
430 int out = geom_to_primtype<int>( sfcgal_geometry_dimension,
nullptr, geom, errorMsg );
431 CHECK_SUCCESS( errorMsg, std::numeric_limits<int>::quiet_NaN() );
436int QgsSfcgalEngine::partCount(
const sfcgal::geometry *geom, QString *errorMsg )
439 sfcgal::errorHandler()->clearText( errorMsg );
440 CHECK_NOT_NULL( geom, -1 );
442 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
443 CHECK_SUCCESS( errorMsg, -1 );
447 case SFCGAL_TYPE_MULTIPOINT:
448 case SFCGAL_TYPE_MULTILINESTRING:
449 case SFCGAL_TYPE_MULTIPOLYGON:
450 case SFCGAL_TYPE_MULTISOLID:
451 case SFCGAL_TYPE_GEOMETRYCOLLECTION:
452#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
453 out = sfcgal_geometry_num_geometries( geom );
455 out = sfcgal_geometry_collection_num_geometries( geom );
458 case SFCGAL_TYPE_POLYGON:
459 out = sfcgal_polygon_num_interior_rings( geom ) + 1;
461 case SFCGAL_TYPE_SOLID:
462 out = sfcgal_solid_num_shells( geom );
464 case SFCGAL_TYPE_POLYHEDRALSURFACE:
465#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
466 out = sfcgal_polyhedral_surface_num_patches( geom );
468 out = sfcgal_polyhedral_surface_num_polygons( geom );
471 case SFCGAL_TYPE_TRIANGULATEDSURFACE:
472#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
473 out = sfcgal_triangulated_surface_num_patches( geom );
475 out = sfcgal_triangulated_surface_num_triangles( geom );
478 case SFCGAL_TYPE_LINESTRING:
479 out = sfcgal_linestring_num_points( geom );
481 case SFCGAL_TYPE_TRIANGLE:
484 case SFCGAL_TYPE_POINT:
491 CHECK_SUCCESS( errorMsg, -1 );
493 return static_cast<int>( out );
496bool QgsSfcgalEngine::addZValue( sfcgal::geometry *geom,
double zValue, QString *errorMsg )
498#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
502 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"addZValue" ) );
504 sfcgal::errorHandler()->clearText( errorMsg );
505 CHECK_NOT_NULL( geom,
false );
507 return sfcgal_geometry_force_z( geom, zValue );
511bool QgsSfcgalEngine::addMValue( sfcgal::geometry *geom,
double mValue, QString *errorMsg )
513#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
517 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"addMValue" ) );
519 sfcgal::errorHandler()->clearText( errorMsg );
520 CHECK_NOT_NULL( geom,
false );
522 return sfcgal_geometry_force_m( geom, mValue );
526bool QgsSfcgalEngine::dropZValue( sfcgal::geometry *geom, QString *errorMsg )
528#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
531 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dropZValue" ) );
533 sfcgal::errorHandler()->clearText( errorMsg );
534 CHECK_NOT_NULL( geom,
false );
536 return sfcgal_geometry_drop_z( geom );
540bool QgsSfcgalEngine::dropMValue( sfcgal::geometry *geom, QString *errorMsg )
542#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
545 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dropMValue" ) );
547 sfcgal::errorHandler()->clearText( errorMsg );
548 CHECK_NOT_NULL( geom,
false );
550 return sfcgal_geometry_drop_m( geom );
554void QgsSfcgalEngine::swapXy( sfcgal::geometry *geom, QString *errorMsg )
556#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
559 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"swapXy" ) );
561 sfcgal::errorHandler()->clearText( errorMsg );
562 CHECK_NOT_NULL( geom,
void() );
564 sfcgal_geometry_swap_xy( geom );
568bool QgsSfcgalEngine::isEqual(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB,
double tolerance, QString *errorMsg )
570#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
575 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"isEqual" ) );
577 sfcgal::errorHandler()->clearText( errorMsg );
578 CHECK_NOT_NULL( geomA,
false );
579 CHECK_NOT_NULL( geomB,
false );
581 bool result = sfcgal_geometry_is_almost_equals( geomA, geomB, tolerance );
582 CHECK_SUCCESS( errorMsg,
false );
588bool QgsSfcgalEngine::isEmpty(
const sfcgal::geometry *geom, QString *errorMsg )
590 int res = geom_to_primtype<int>( sfcgal_geometry_is_empty,
nullptr, geom, errorMsg );
591 CHECK_SUCCESS( errorMsg,
false );
592 return static_cast<bool>( res );
595bool QgsSfcgalEngine::isValid(
const sfcgal::geometry *geom, QString *errorMsg,
QgsGeometry *errorLoc )
597 sfcgal::errorHandler()->clearText( errorMsg );
598 CHECK_NOT_NULL( geom,
false );
602 sfcgal::geometry *location;
603 result = sfcgal_geometry_is_valid_detail( geom, &reason, &location );
605 CHECK_SUCCESS( errorMsg,
false );
607 if ( reason && strlen( reason ) )
609 sfcgal::errorHandler()->addText( QString( reason ) );
613 if ( location && errorLoc )
615 std::unique_ptr<QgsAbstractGeometry> locationGeom = toAbstractGeometry( location, errorMsg );
616 CHECK_SUCCESS( errorMsg,
false );
617 errorLoc->
addPartV2( locationGeom.release() );
623bool QgsSfcgalEngine::isSimple(
const sfcgal::geometry *geom, QString *errorMsg )
625#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
628 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"isSimple" ) );
630 int res = geom_to_primtype<int>( sfcgal_geometry_is_simple,
nullptr, geom, errorMsg );
631 CHECK_SUCCESS( errorMsg,
false );
632 return static_cast<bool>( res );
636sfcgal::shared_geom QgsSfcgalEngine::geometryN(
const sfcgal::geometry *geom,
unsigned int index, QString *errorMsg )
638 sfcgal::errorHandler()->clearText( errorMsg );
639 CHECK_NOT_NULL( geom,
nullptr );
641 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
642 CHECK_SUCCESS( errorMsg,
nullptr );
644 const sfcgal::geometry *out =
nullptr;
648 case SFCGAL_TYPE_GEOMETRYCOLLECTION:
649 case SFCGAL_TYPE_MULTILINESTRING:
650 case SFCGAL_TYPE_MULTIPOINT:
651 case SFCGAL_TYPE_MULTIPOLYGON:
652 case SFCGAL_TYPE_MULTISOLID:
654#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
657 const unsigned int nrGeoms = sfcgal_geometry_collection_num_geometries( geom );
658 if ( index < nrGeoms )
660 out = sfcgal_geometry_collection_geometry_n( geom, index );
664 sfcgal::errorHandler()->addText( u
"Cannot access geometry at position %s. GeometryCollection has only %d geometries."_s.arg( index ).arg( nrGeoms ) );
667 out = sfcgal_geometry_collection_geometry_n( geom, index );
671 case SFCGAL_TYPE_LINESTRING:
672 case SFCGAL_TYPE_POINT:
673 case SFCGAL_TYPE_POLYGON:
674 case SFCGAL_TYPE_POLYHEDRALSURFACE:
675 case SFCGAL_TYPE_SOLID:
676 case SFCGAL_TYPE_TRIANGLE:
677 case SFCGAL_TYPE_TRIANGULATEDSURFACE:
687 CHECK_SUCCESS( errorMsg,
nullptr );
689 sfcgal::shared_geom result = cloneGeometry( out, errorMsg );
690 CHECK_SUCCESS( errorMsg,
nullptr );
695sfcgal::shared_geom QgsSfcgalEngine::boundary(
const sfcgal::geometry *geom, QString *errorMsg )
697#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
700 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
702 sfcgal::errorHandler()->clearText( errorMsg );
703 CHECK_NOT_NULL( geom,
nullptr );
705 sfcgal::geometry *boundary = sfcgal_geometry_boundary( geom );
706 CHECK_SUCCESS( errorMsg,
nullptr );
708 return sfcgal::make_shared_geom( boundary );
712QgsPoint QgsSfcgalEngine::centroid(
const sfcgal::geometry *geom, QString *errorMsg )
714#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
717 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"centroid" ) );
719 sfcgal::errorHandler()->clearText( errorMsg );
722 const sfcgal::geometry *result =
nullptr;
723 if ( sfcgal_geometry_is_3d( geom ) )
724 result = sfcgal_geometry_centroid_3d( geom );
726 result = sfcgal_geometry_centroid( geom );
728 CHECK_SUCCESS( errorMsg,
QgsPoint() );
729 CHECK_NOT_NULL( result,
QgsPoint() );
731 QByteArray wkbArray = QgsSfcgalEngine::toWkb( result, errorMsg );
740sfcgal::shared_geom QgsSfcgalEngine::translate(
const sfcgal::geometry *geom,
const QgsVector3D &translation, QString *errorMsg )
742#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
744 ( void ) translation;
746 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"translate" ) );
748 sfcgal::errorHandler()->clearText( errorMsg );
749 CHECK_NOT_NULL( geom,
nullptr );
751 sfcgal::geometry *result;
752 if ( sfcgal_geometry_is_3d( geom ) )
753 result = sfcgal_geometry_translate_3d( geom, translation.
x(), translation.
y(), translation.
z() );
755 result = sfcgal_geometry_translate_2d( geom, translation.
x(), translation.
y() );
756 CHECK_SUCCESS( errorMsg,
nullptr );
758 return sfcgal::make_shared_geom( result );
762sfcgal::shared_geom QgsSfcgalEngine::scale(
const sfcgal::geometry *geom,
const QgsVector3D &scaleFactor,
const QgsPoint ¢er, QString *errorMsg )
764 sfcgal::errorHandler()->clearText( errorMsg );
765 CHECK_NOT_NULL( geom,
nullptr );
767 sfcgal::geometry *result;
770 result = sfcgal_geometry_scale_3d( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z() );
774 const double centerZ = center.
is3D() ? center.
z() : 0;
775 result = sfcgal_geometry_scale_3d_around_center( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z(), center.
x(), center.
y(), centerZ );
778 CHECK_SUCCESS( errorMsg,
nullptr );
779 return sfcgal::make_shared_geom( result );
782sfcgal::shared_geom QgsSfcgalEngine::rotate2D(
const sfcgal::geometry *geom,
double angle,
const QgsPoint ¢er, QString *errorMsg )
784 sfcgal::errorHandler()->clearText( errorMsg );
785 CHECK_NOT_NULL( geom,
nullptr );
787 sfcgal::geometry *result = sfcgal_geometry_rotate_2d( geom, angle, center.
x(), center.
y() );
789 CHECK_SUCCESS( errorMsg,
nullptr );
790 return sfcgal::make_shared_geom( result );
793sfcgal::shared_geom QgsSfcgalEngine::rotate3D(
const sfcgal::geometry *geom,
double angle,
const QgsVector3D &axisVector,
const QgsPoint ¢er, QString *errorMsg )
795 sfcgal::errorHandler()->clearText( errorMsg );
796 CHECK_NOT_NULL( geom,
nullptr );
798 sfcgal::geometry *result;
801 result = sfcgal_geometry_rotate_3d( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z() );
805 result = sfcgal_geometry_rotate_3d_around_center( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z(), center.
x(), center.
y(), center.
z() );
808 CHECK_SUCCESS( errorMsg,
nullptr );
809 return sfcgal::make_shared_geom( result );
812double QgsSfcgalEngine::distance(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
814 double out = geomgeom_to_primtype<double>( sfcgal_geometry_distance, sfcgal_geometry_distance_3d, geomA, geomB, errorMsg );
815 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
819bool QgsSfcgalEngine::distanceWithin(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB,
double maxdistance, QString *errorMsg )
821 double dist = QgsSfcgalEngine::distance( geomA, geomB, errorMsg );
822 CHECK_SUCCESS( errorMsg,
false );
824 return dist <= maxdistance;
827double QgsSfcgalEngine::area(
const sfcgal::geometry *geom, QString *errorMsg )
829 double out = geom_to_primtype<double>( sfcgal_geometry_area, sfcgal_geometry_area_3d, geom, errorMsg );
830 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
834double QgsSfcgalEngine::length(
const sfcgal::geometry *geom, QString *errorMsg )
836#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
839 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"length" ) );
841 double out = geom_to_primtype<double>( sfcgal_geometry_length, sfcgal_geometry_length_3d, geom, errorMsg );
842 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
847bool QgsSfcgalEngine::intersects(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
849 int res = geomgeom_to_primtype<int>( sfcgal_geometry_intersects, sfcgal_geometry_intersects_3d, geomA, geomB, errorMsg );
850 CHECK_SUCCESS( errorMsg,
false );
851 return static_cast<bool>( res );
854sfcgal::shared_geom QgsSfcgalEngine::intersection(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
856 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_intersection, sfcgal_geometry_intersection_3d, geomA, geomB, errorMsg );
857 CHECK_SUCCESS( errorMsg,
nullptr );
861sfcgal::shared_geom QgsSfcgalEngine::difference(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
863 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_difference, sfcgal_geometry_difference_3d, geomA, geomB, errorMsg );
864 CHECK_SUCCESS( errorMsg,
nullptr );
868sfcgal::shared_geom QgsSfcgalEngine::combine(
const QVector<sfcgal::shared_geom> &geomList, QString *errorMsg )
870 sfcgal::errorHandler()->clearText( errorMsg );
871 sfcgal::geometry *combined =
nullptr;
872 for ( sfcgal::shared_geom other : geomList )
876 combined = other.get();
880 if ( sfcgal_geometry_is_3d( other.get() ) || sfcgal_geometry_is_3d( combined ) )
881 combined = sfcgal_geometry_union_3d( combined, other.get() );
883 combined = sfcgal_geometry_union( combined, other.get() );
886 sfcgal::errorHandler()->addText(
"SFCGAL produced null result." );
888 CHECK_SUCCESS( errorMsg,
nullptr );
891 return sfcgal::make_shared_geom( combined );
894sfcgal::shared_geom QgsSfcgalEngine::triangulate(
const sfcgal::geometry *geom, QString *errorMsg )
896 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_triangulate_2dz,
nullptr, geom, errorMsg );
897 CHECK_SUCCESS( errorMsg,
nullptr );
901bool QgsSfcgalEngine::covers(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
903 int res = geomgeom_to_primtype<int>( sfcgal_geometry_covers, sfcgal_geometry_covers_3d, geomA, geomB, errorMsg );
904 CHECK_SUCCESS( errorMsg,
false );
905 return static_cast<bool>( res );
908sfcgal::shared_geom QgsSfcgalEngine::envelope(
const sfcgal::geometry *geom, QString *errorMsg )
910#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
913 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"envelope" ) );
915 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_envelope, sfcgal_geometry_envelope_3d, geom, errorMsg );
916 CHECK_SUCCESS( errorMsg,
nullptr );
921sfcgal::shared_geom QgsSfcgalEngine::convexHull(
const sfcgal::geometry *geom, QString *errorMsg )
923 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_convexhull, sfcgal_geometry_convexhull_3d, geom, errorMsg );
924 CHECK_SUCCESS( errorMsg,
nullptr );
928sfcgal::shared_geom QgsSfcgalEngine::offsetCurve(
const sfcgal::geometry *geom,
double distance,
int,
Qgis::JoinStyle, QString *errorMsg )
930 sfcgal::errorHandler()->clearText( errorMsg );
931 CHECK_NOT_NULL( geom,
nullptr );
933 sfcgal::geometry *result =
nullptr;
934 result = sfcgal_geometry_offset_polygon( geom, distance );
936 CHECK_SUCCESS( errorMsg,
nullptr );
938 return sfcgal::make_shared_geom( result );
941sfcgal::shared_geom QgsSfcgalEngine::buffer2D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle joinStyle, QString *errorMsg )
944 qWarning() << ( u
"Buffer not implemented for %1! Defaulting to round join."_s );
946 return offsetCurve( geom, radius, segments, joinStyle, errorMsg );
949sfcgal::shared_geom QgsSfcgalEngine::buffer3D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle3D joinStyle3D, QString *errorMsg )
951 sfcgal::errorHandler()->clearText( errorMsg );
952 CHECK_NOT_NULL( geom,
nullptr );
954 sfcgal_buffer3d_type_t buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
955 switch ( joinStyle3D )
958 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
961 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_ROUND;
964 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_CYLSPHERE;
968 sfcgal::geometry *result = sfcgal_geometry_buffer3d( geom, radius, segments, buffer_type );
969 CHECK_SUCCESS( errorMsg,
nullptr );
971 return sfcgal::make_shared_geom( result );
974sfcgal::shared_geom QgsSfcgalEngine::extrude(
const sfcgal::geometry *geom,
const QgsVector3D &extrusion, QString *errorMsg )
976 sfcgal::errorHandler()->clearText( errorMsg );
977 CHECK_NOT_NULL( geom,
nullptr );
979 sfcgal_geometry_t *solid = sfcgal_geometry_extrude( geom, extrusion.
x(), extrusion.
y(), extrusion.
z() );
981 CHECK_SUCCESS( errorMsg,
nullptr );
986 sfcgal::shared_geom polySurface = QgsSfcgalEngine::toPolyhedralSurface( solid, errorMsg );
987 sfcgal_geometry_delete( solid );
989 CHECK_SUCCESS( errorMsg,
nullptr );
994sfcgal::shared_geom QgsSfcgalEngine::simplify(
const sfcgal::geometry *geom,
double tolerance,
bool preserveTopology, QString *errorMsg )
996#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
999 ( void ) preserveTopology;
1001 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
1003 sfcgal::errorHandler()->clearText( errorMsg );
1004 CHECK_NOT_NULL( geom,
nullptr );
1006 sfcgal::geometry *result = sfcgal_geometry_simplify( geom, tolerance, preserveTopology );
1007 CHECK_SUCCESS( errorMsg,
nullptr );
1009 return sfcgal::make_shared_geom( result );
1013sfcgal::shared_geom QgsSfcgalEngine::approximateMedialAxis(
const sfcgal::geometry *geom,
bool extendToEdges, QString *errorMsg )
1015 sfcgal::errorHandler()->clearText( errorMsg );
1016 CHECK_NOT_NULL( geom,
nullptr );
1018#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
1019 sfcgal::geometry *result =
nullptr;
1020 if ( extendToEdges )
1022 result = sfcgal_geometry_projected_medial_axis( geom );
1026 result = sfcgal_geometry_approximate_medial_axis( geom );
1029 Q_UNUSED( extendToEdges )
1030 sfcgal::geometry *result = sfcgal_geometry_approximate_medial_axis( geom );
1032 CHECK_SUCCESS( errorMsg,
nullptr );
1034 return sfcgal::make_shared_geom( result );
1037sfcgal::shared_geom QgsSfcgalEngine::toSolid(
const sfcgal::geometry *geom, QString *errorMsg )
1039 sfcgal::errorHandler()->clearText( errorMsg );
1040 CHECK_NOT_NULL( geom,
nullptr );
1042 sfcgal::geometry *solid = sfcgal_geometry_make_solid( geom );
1043 CHECK_SUCCESS( errorMsg,
nullptr );
1045 return sfcgal::make_shared_geom( solid );
1048sfcgal::shared_geom QgsSfcgalEngine::toPolyhedralSurface(
const sfcgal::geometry *geom, QString *errorMsg )
1050 sfcgal::errorHandler()->clearText( errorMsg );
1051 CHECK_NOT_NULL( geom,
nullptr );
1053 if ( sfcgal_geometry_type_id( geom ) != SFCGAL_TYPE_SOLID )
1055 sfcgal::errorHandler()->addText( u
"toPolyhedralSurface() only applies to solids"_s );
1059 sfcgal_geometry_t *polySurface = sfcgal_polyhedral_surface_create();
1060 for (
unsigned int shellIdx = 0; shellIdx < sfcgal_solid_num_shells( geom ); ++shellIdx )
1062 const sfcgal_geometry_t *shell = sfcgal_solid_shell_n( geom, shellIdx );
1063#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
1064 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_patches( shell ); ++polyIdx )
1066 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_patch_n( shell, polyIdx );
1067 sfcgal_polyhedral_surface_add_patch( polySurface, sfcgal_geometry_clone( patch ) );
1070 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_polygons( shell ); ++polyIdx )
1072 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_polygon_n( shell, polyIdx );
1073 sfcgal_polyhedral_surface_add_polygon( polySurface, sfcgal_geometry_clone( patch ) );
1078 CHECK_SUCCESS( errorMsg,
nullptr );
1079 return sfcgal::make_shared_geom( polySurface );
1082#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
1083sfcgal::shared_geom QgsSfcgalEngine::transform(
const sfcgal::geometry *geom,
const QgsMatrix4x4 &mat, QString *errorMsg )
1085 sfcgal::errorHandler()->clearText( errorMsg );
1086 CHECK_NOT_NULL( geom,
nullptr );
1088 sfcgal::geometry *result;
1089 result = sfcgal_geometry_transform( geom, mat.
constData() );
1091 CHECK_SUCCESS( errorMsg,
nullptr );
1092 return sfcgal::make_shared_geom( result );
1095sfcgal::shared_geom QgsSfcgalEngine::split3D(
const sfcgal::geometry *geom,
const QgsPoint &planePoint,
const QgsVector3D &planeNormal,
bool closeGeometries, QString *errorMsg )
1097 sfcgal::errorHandler()->clearText( errorMsg );
1098 CHECK_NOT_NULL( geom,
nullptr );
1100 sfcgal::geometry *result = sfcgal_geometry_split_3d( geom, planePoint.
x(), planePoint.
y(), planePoint.
z(), planeNormal.
x(), planeNormal.
y(), planeNormal.
z(), closeGeometries );
1102 CHECK_SUCCESS( errorMsg,
nullptr );
1103 return sfcgal::make_shared_geom( result );
1106std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_prim &prim, sfcgal::primitiveType type, QString *errorMsg )
1108 sfcgal::errorHandler()->clearText( errorMsg );
1109 CHECK_NOT_NULL( prim.get(),
nullptr );
1111 return std::make_unique<QgsSfcgalGeometry>( prim, type );
1114sfcgal::shared_prim QgsSfcgalEngine::createBox(
double sizeX,
double sizeY,
double sizeZ, QString *errorMsg )
1116 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_BOX );
1117 CHECK_SUCCESS( errorMsg,
nullptr );
1119 sfcgal_primitive_set_parameter_double( result,
"x_extent", sizeX );
1120 sfcgal_primitive_set_parameter_double( result,
"y_extent", sizeY );
1121 sfcgal_primitive_set_parameter_double( result,
"z_extent", sizeZ );
1122 CHECK_SUCCESS( errorMsg,
nullptr );
1124 return sfcgal::make_shared_prim( result );
1127sfcgal::shared_prim QgsSfcgalEngine::createCone(
double bottomRadius,
double height,
double topRadius,
unsigned int radial, QString *errorMsg )
1129 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CONE );
1130 CHECK_SUCCESS( errorMsg,
nullptr );
1132 sfcgal_primitive_set_parameter_double( result,
"bottom_radius", bottomRadius );
1133 sfcgal_primitive_set_parameter_double( result,
"height", height );
1134 sfcgal_primitive_set_parameter_double( result,
"top_radius", topRadius );
1135 sfcgal_primitive_set_parameter_int( result,
"num_radial", radial );
1136 CHECK_SUCCESS( errorMsg,
nullptr );
1138 return sfcgal::make_shared_prim( result );
1141sfcgal::shared_prim QgsSfcgalEngine::createCube(
double size, QString *errorMsg )
1143 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CUBE );
1144 CHECK_SUCCESS( errorMsg,
nullptr );
1146 sfcgal_primitive_set_parameter_double( result,
"size", size );
1147 CHECK_SUCCESS( errorMsg,
nullptr );
1149 return sfcgal::make_shared_prim( result );
1152sfcgal::shared_prim QgsSfcgalEngine::createCylinder(
double radius,
double height,
unsigned int radial, QString *errorMsg )
1154 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CYLINDER );
1155 CHECK_SUCCESS( errorMsg,
nullptr );
1157 sfcgal_primitive_set_parameter_double( result,
"radius", radius );
1158 sfcgal_primitive_set_parameter_double( result,
"height", height );
1159 sfcgal_primitive_set_parameter_int( result,
"num_radial", radial );
1160 CHECK_SUCCESS( errorMsg,
nullptr );
1162 return sfcgal::make_shared_prim( result );
1165sfcgal::shared_prim QgsSfcgalEngine::createSphere(
double radius,
unsigned int subdivisions, QString *errorMsg )
1167 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_SPHERE );
1168 CHECK_SUCCESS( errorMsg,
nullptr );
1170 sfcgal_primitive_set_parameter_double( result,
"radius", radius );
1171 sfcgal_primitive_set_parameter_int( result,
"num_subdivisions", subdivisions );
1172 CHECK_SUCCESS( errorMsg,
nullptr );
1174 return sfcgal::make_shared_prim( result );
1177sfcgal::shared_prim QgsSfcgalEngine::createTorus(
double mainRadius,
double tubeRadius,
unsigned int mainRadial,
unsigned int tubeRadial, QString *errorMsg )
1179 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_TORUS );
1180 CHECK_SUCCESS( errorMsg,
nullptr );
1182 sfcgal_primitive_set_parameter_double( result,
"main_radius", mainRadius );
1183 sfcgal_primitive_set_parameter_double( result,
"tube_radius", tubeRadius );
1184 sfcgal_primitive_set_parameter_int( result,
"main_num_radial", mainRadial );
1185 sfcgal_primitive_set_parameter_int( result,
"tube_num_radial", tubeRadial );
1186 CHECK_SUCCESS( errorMsg,
nullptr );
1188 return sfcgal::make_shared_prim( result );
1191sfcgal::shared_geom QgsSfcgalEngine::primitiveAsPolyhedral(
const sfcgal::primitive *prim,
const QgsMatrix4x4 &mat, QString *errorMsg )
1193 sfcgal::errorHandler()->clearText( errorMsg );
1194 CHECK_NOT_NULL( prim,
nullptr );
1196 sfcgal::geometry *result = sfcgal_primitive_as_polyhedral_surface( prim );
1197 CHECK_SUCCESS( errorMsg,
nullptr );
1201 sfcgal::geometry *result2 = sfcgal_geometry_transform( result, mat.
constData() );
1202 sfcgal_geometry_delete( result );
1204 CHECK_SUCCESS( errorMsg,
nullptr );
1207 return sfcgal::make_shared_geom( result );
1210bool QgsSfcgalEngine::primitiveIsEqual(
const sfcgal::primitive *primA,
const sfcgal::primitive *primB,
double tolerance, QString *errorMsg )
1212 sfcgal::errorHandler()->clearText( errorMsg );
1213 CHECK_NOT_NULL( primA,
false );
1214 CHECK_NOT_NULL( primB,
false );
1216 bool result = sfcgal_primitive_is_almost_equals( primA, primB, tolerance );
1217 CHECK_SUCCESS( errorMsg,
false );
1222sfcgal::shared_prim QgsSfcgalEngine::primitiveClone(
const sfcgal::primitive *prim, QString *errorMsg )
1224 sfcgal::errorHandler()->clearText( errorMsg );
1225 CHECK_NOT_NULL( prim,
nullptr );
1227 sfcgal::primitive *result = sfcgal_primitive_clone( prim );
1229 CHECK_SUCCESS( errorMsg,
nullptr );
1230 CHECK_NOT_NULL( result,
nullptr );
1232 return sfcgal::make_shared_prim( result );
1235double QgsSfcgalEngine::primitiveArea(
const sfcgal::primitive *prim,
const QgsMatrix4x4 &primTransform,
bool withDiscretization, QString *errorMsg )
1237 sfcgal::errorHandler()->clearText( errorMsg );
1238 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1243 const double area = sfcgal_primitive_area( prim, withDiscretization );
1244 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1248 double baseArea = 0.0;
1259 QgsDebugMsgLevel( u
"The primitive has a non-uniform scale. Falling back to polyhedral surface approximation for computation."_s, 2 );
1260 sfcgal::shared_geom phs = primitiveAsPolyhedral( prim, primTransform );
1261 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1262 baseArea = area( phs.get() );
1267 scale = std::cbrt( std::abs( primTransform.
determinant() ) );
1268 baseArea = sfcgal_primitive_area( prim, withDiscretization );
1271 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1272 return baseArea * scale * scale;
1275double QgsSfcgalEngine::primitiveVolume(
const sfcgal::primitive *prim,
const QgsMatrix4x4 &primTransform,
bool withDiscretization, QString *errorMsg )
1277 sfcgal::errorHandler()->clearText( errorMsg );
1278 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1280 const double baseVolume = sfcgal_primitive_volume( prim, withDiscretization );
1281 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1283 return primTransform.
isIdentity() ? baseVolume : baseVolume * std::abs( primTransform.
determinant() );
1286void sfcgal::to_json( json &j,
const sfcgal::PrimitiveParameterDesc &p )
1291 if ( std::holds_alternative<int>( p.value ) )
1293 j[
"value"] = std::get<int>( p.value );
1295 else if ( std::holds_alternative<double>( p.value ) )
1297 j[
"value"] = std::get<double>( p.value );
1299 else if ( std::holds_alternative<QgsPoint>( p.value ) )
1301 QgsPoint point = std::get<QgsPoint>( p.value );
1302 double z = std::numeric_limits<double>::quiet_NaN();
1303 double m = std::numeric_limits<double>::quiet_NaN();
1308 j[
"value"] = std::vector<double> { point.
x(), point.
y(), z, m };
1310 else if ( std::holds_alternative<QgsVector3D>( p.value ) )
1312 QgsVector3D vect = std::get<QgsVector3D>( p.value );
1313 j[
"value"] = std::vector<double> { vect.
x(), vect.
y(), vect.
z() };
1316 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1319void sfcgal::from_json(
const json &j, sfcgal::PrimitiveParameterDesc &p )
1321 j.at(
"name" ).get_to( p.name );
1322 j.at(
"type" ).get_to( p.type );
1323 if ( j.contains(
"value" ) )
1325 json value = j.at(
"value" );
1326 if ( p.type ==
"int" )
1328 p.value = value.get<
int>();
1330 else if ( p.type ==
"double" )
1332 p.value = value.get<
double>();
1334 else if ( p.type ==
"point3" )
1336 std::vector<double> vect;
1337 vect = value.get<std::vector<double>>();
1341 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1342 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1346 else if ( p.type ==
"vector3" )
1348 std::vector<double> vect;
1349 vect = value.get<std::vector<double>>();
1353 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1354 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1359 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1363QVector<sfcgal::PrimitiveParameterDesc> QgsSfcgalEngine::primitiveParameters(
const sfcgal::primitive *prim, QString *errorMsg )
1365 sfcgal::errorHandler()->clearText( errorMsg );
1366 CHECK_NOT_NULL( prim, QVector<sfcgal::PrimitiveParameterDesc>() );
1368 char *jsonChars =
nullptr;
1370 sfcgal_primitive_parameters( prim, &jsonChars, &len );
1371 CHECK_SUCCESS( errorMsg, QVector<sfcgal::PrimitiveParameterDesc>() );
1373 std::string jsonString( jsonChars, len );
1374 sfcgal_free_buffer( jsonChars );
1376 QVector<sfcgal::PrimitiveParameterDesc> result;
1379 const auto jParams = json::parse( jsonString );
1380 for (
const auto &jParam : jParams )
1382 result.append( jParam.get<sfcgal::PrimitiveParameterDesc>() );
1385 catch ( json::exception &e )
1387 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1393QVariant QgsSfcgalEngine::primitiveParameter(
const sfcgal::primitive *prim,
const QString &name, QString *errorMsg )
1395 sfcgal::errorHandler()->clearText( errorMsg );
1396 CHECK_NOT_NULL( prim, QVariant() );
1398 char *jsonChars =
nullptr;
1400 sfcgal_primitive_parameter( prim, name.toStdString().c_str(), &jsonChars, &len );
1401 CHECK_SUCCESS( errorMsg, QVariant() );
1403 std::string jsonString( jsonChars, len );
1404 sfcgal_free_buffer( jsonChars );
1409 const auto jParam = json::parse( jsonString );
1410 sfcgal::PrimitiveParameterDesc param = jParam.get<sfcgal::PrimitiveParameterDesc>();
1411 result = QVariant::fromStdVariant( param.value );
1413 catch ( json::exception &e )
1415 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1421void QgsSfcgalEngine::primitiveSetParameter( sfcgal::primitive *prim,
const QString &name,
const QVariant &value, QString *errorMsg )
1423 sfcgal::errorHandler()->clearText( errorMsg );
1424 CHECK_NOT_NULL( prim,
void() );
1429 sfcgal::PrimitiveParameterDesc paramDesc;
1430 paramDesc.name = name.toStdString();
1431 paramDesc.type = value.typeName();
1432 if ( paramDesc.type ==
"int" )
1433 paramDesc.value = value.toInt();
1434 else if ( paramDesc.type ==
"double" )
1435 paramDesc.value = value.toDouble();
1436 else if ( value.canConvert<
QgsPoint>() )
1437 paramDesc.value = value.value<
QgsPoint>();
1441 sfcgal::to_json( jParam, paramDesc );
1442 std::string jsonStr = jParam.dump();
1443 sfcgal_primitive_set_parameter( prim, name.toStdString().c_str(), jsonStr.c_str() );
1444 CHECK_SUCCESS( errorMsg,
void() );
1448 sfcgal::errorHandler()->addText( u
"Caught json exception"_s );
JoinStyle3D
Join styles for 3D buffers.
@ CylindersAndSpheres
Cylinders along the linestring segments with spheres at the vertices.
@ Flat
Flat ends and constant width along the linestring.
@ Round
Smooth, rounded buffer around the input geometry.
JoinStyle
Join styles for buffers.
@ Round
Use rounded joins.
WkbType
The WKB type describes the number of dimensions a geometry has.
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.
int remaining() const
remaining
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.
A simple 4x4 matrix implementation useful for transformation in 3D space.
QgsVector3D mapVector(const QgsVector3D &vector) const
Maps vector by multiplying the top 3x3 portion of this matrix by vector.
bool isIdentity() const
Returns whether this matrix is an identity matrix.
double determinant() const
Returns the determinant of this matrix.
const double * constData() const
Returns pointer to the matrix data (stored in column-major order).
Custom exception class which is raised when an operation is not supported.
Point geometry type, with support for z-dimension and m-values.
bool fromWkb(QgsConstWkbPtr &wkb) override
Sets the geometry from a WKB string.
bool isEmpty() const override
Returns true if the geometry is empty.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugMsgLevel(str, level)