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, 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 );
1199 return sfcgal::make_shared_geom( result );
1202bool QgsSfcgalEngine::primitiveIsEqual(
const sfcgal::primitive *primA,
const sfcgal::primitive *primB,
double tolerance, QString *errorMsg )
1204 sfcgal::errorHandler()->clearText( errorMsg );
1205 CHECK_NOT_NULL( primA,
false );
1206 CHECK_NOT_NULL( primB,
false );
1208 bool result = sfcgal_primitive_is_almost_equals( primA, primB, tolerance );
1209 CHECK_SUCCESS( errorMsg,
false );
1214sfcgal::shared_prim QgsSfcgalEngine::primitiveClone(
const sfcgal::primitive *prim, QString *errorMsg )
1216 sfcgal::errorHandler()->clearText( errorMsg );
1217 CHECK_NOT_NULL( prim,
nullptr );
1219 sfcgal::primitive *result = sfcgal_primitive_clone( prim );
1221 CHECK_SUCCESS( errorMsg,
nullptr );
1222 CHECK_NOT_NULL( result,
nullptr );
1224 return sfcgal::make_shared_prim( result );
1227double QgsSfcgalEngine::primitiveArea(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1229 sfcgal::errorHandler()->clearText( errorMsg );
1230 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1232 double area = sfcgal_primitive_area( prim, withDiscretization );
1234 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1238double QgsSfcgalEngine::primitiveVolume(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1240 sfcgal::errorHandler()->clearText( errorMsg );
1241 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1243 const double volume = sfcgal_primitive_volume( prim, withDiscretization );
1244 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1249void sfcgal::to_json( json &j,
const sfcgal::PrimitiveParameterDesc &p )
1254 if ( std::holds_alternative<int>( p.value ) )
1256 j[
"value"] = std::get<int>( p.value );
1258 else if ( std::holds_alternative<double>( p.value ) )
1260 j[
"value"] = std::get<double>( p.value );
1262 else if ( std::holds_alternative<QgsPoint>( p.value ) )
1264 QgsPoint point = std::get<QgsPoint>( p.value );
1265 double z = std::numeric_limits<double>::quiet_NaN();
1266 double m = std::numeric_limits<double>::quiet_NaN();
1271 j[
"value"] = std::vector<double> { point.
x(), point.
y(), z, m };
1273 else if ( std::holds_alternative<QgsVector3D>( p.value ) )
1275 QgsVector3D vect = std::get<QgsVector3D>( p.value );
1276 j[
"value"] = std::vector<double> { vect.
x(), vect.
y(), vect.
z() };
1279 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1282void sfcgal::from_json(
const json &j, sfcgal::PrimitiveParameterDesc &p )
1284 j.at(
"name" ).get_to( p.name );
1285 j.at(
"type" ).get_to( p.type );
1286 if ( j.contains(
"value" ) )
1288 json value = j.at(
"value" );
1289 if ( p.type ==
"int" )
1291 p.value = value.get<
int>();
1293 else if ( p.type ==
"double" )
1295 p.value = value.get<
double>();
1297 else if ( p.type ==
"point3" )
1299 std::vector<double> vect;
1300 vect = value.get<std::vector<double>>();
1304 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1305 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1309 else if ( p.type ==
"vector3" )
1311 std::vector<double> vect;
1312 vect = value.get<std::vector<double>>();
1316 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1317 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1322 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1326QVector<sfcgal::PrimitiveParameterDesc> QgsSfcgalEngine::primitiveParameters(
const sfcgal::primitive *prim, QString *errorMsg )
1328 sfcgal::errorHandler()->clearText( errorMsg );
1329 CHECK_NOT_NULL( prim, QVector<sfcgal::PrimitiveParameterDesc>() );
1331 char *jsonChars =
nullptr;
1333 sfcgal_primitive_parameters( prim, &jsonChars, &len );
1334 CHECK_SUCCESS( errorMsg, QVector<sfcgal::PrimitiveParameterDesc>() );
1336 std::string jsonString( jsonChars, len );
1337 sfcgal_free_buffer( jsonChars );
1339 QVector<sfcgal::PrimitiveParameterDesc> result;
1342 const auto jParams = json::parse( jsonString );
1343 for (
const auto &jParam : jParams )
1345 result.append( jParam.get<sfcgal::PrimitiveParameterDesc>() );
1348 catch ( json::exception &e )
1350 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1356QVariant QgsSfcgalEngine::primitiveParameter(
const sfcgal::primitive *prim,
const QString &name, QString *errorMsg )
1358 sfcgal::errorHandler()->clearText( errorMsg );
1359 CHECK_NOT_NULL( prim, QVariant() );
1361 char *jsonChars =
nullptr;
1363 sfcgal_primitive_parameter( prim, name.toStdString().c_str(), &jsonChars, &len );
1364 CHECK_SUCCESS( errorMsg, QVariant() );
1366 std::string jsonString( jsonChars, len );
1367 sfcgal_free_buffer( jsonChars );
1372 const auto jParam = json::parse( jsonString );
1373 sfcgal::PrimitiveParameterDesc param = jParam.get<sfcgal::PrimitiveParameterDesc>();
1374 result = QVariant::fromStdVariant( param.value );
1376 catch ( json::exception &e )
1378 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1384void QgsSfcgalEngine::primitiveSetParameter( sfcgal::primitive *prim,
const QString &name,
const QVariant &value, QString *errorMsg )
1386 sfcgal::errorHandler()->clearText( errorMsg );
1387 CHECK_NOT_NULL( prim,
void() );
1392 sfcgal::PrimitiveParameterDesc paramDesc;
1393 paramDesc.name = name.toStdString();
1394 paramDesc.type = value.typeName();
1395 if ( paramDesc.type ==
"int" )
1396 paramDesc.value = value.toInt();
1397 else if ( paramDesc.type ==
"double" )
1398 paramDesc.value = value.toDouble();
1399 else if ( value.canConvert<
QgsPoint>() )
1400 paramDesc.value = value.value<
QgsPoint>();
1404 sfcgal::to_json( jParam, paramDesc );
1405 std::string jsonStr = jParam.dump();
1406 sfcgal_primitive_set_parameter( prim, name.toStdString().c_str(), jsonStr.c_str() );
1407 CHECK_SUCCESS( errorMsg,
void() );
1411 sfcgal::errorHandler()->addText( u
"Caught json exception"_s );
1415sfcgal::shared_prim QgsSfcgalEngine::primitiveTranslate(
const sfcgal::primitive *prim,
const QgsVector3D &translation, QString *errorMsg )
1417 sfcgal::primitive *result = sfcgal_primitive_translate( prim, translation.
x(), translation.
y(), translation.
z() );
1418 CHECK_SUCCESS( errorMsg,
nullptr );
1420 return sfcgal::make_shared_prim( result );
1423sfcgal::shared_geom QgsSfcgalEngine::primitiveRotate(
const sfcgal::primitive *prim,
double angle,
const QgsVector3D &axisVector,
const QgsPoint ¢er, QString *errorMsg )
1427 sfcgal::primitive *result = sfcgal_primitive_rotate( prim, angle, axisVector.
x(), axisVector.
y(), axisVector.
z(), rotationCenter.
x(), rotationCenter.
y(), rotationCenter.
z() );
1428 CHECK_SUCCESS( errorMsg,
nullptr );
1430 return sfcgal::make_shared_prim( result );
1433sfcgal::shared_geom QgsSfcgalEngine::primitiveScale(
const sfcgal::primitive *prim,
const QgsVector3D &scaleFactor,
const QgsPoint ¢er, QString *errorMsg )
1437 sfcgal::primitive *result = sfcgal_primitive_scale( prim, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z(), scaleCenter.
x(), scaleCenter.
y(), scaleCenter.
z() );
1438 CHECK_SUCCESS( errorMsg,
nullptr );
1440 return sfcgal::make_shared_prim( result );
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.
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.