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, QString *reasonMsg,
QgsGeometry *errorLoc )
597 sfcgal::errorHandler()->clearText( errorMsg );
598 CHECK_NOT_NULL( geom,
false );
601 char *reason =
nullptr;
602 sfcgal::geometry *location =
nullptr;
603 result = sfcgal_geometry_is_valid_detail( geom, &reason, &location );
605 CHECK_SUCCESS( errorMsg,
false );
607 if ( reason && strlen( reason ) )
611 *reasonMsg = QString( reason );
616 if ( location && errorLoc )
618 std::unique_ptr<QgsAbstractGeometry> locationGeom = toAbstractGeometry( location, errorMsg );
619 CHECK_SUCCESS( errorMsg,
false );
620 errorLoc->
addPartV2( locationGeom.release() );
626bool QgsSfcgalEngine::isSimple(
const sfcgal::geometry *geom, QString *errorMsg )
628#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
631 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"isSimple" ) );
633 int res = geom_to_primtype<int>( sfcgal_geometry_is_simple,
nullptr, geom, errorMsg );
634 CHECK_SUCCESS( errorMsg,
false );
635 return static_cast<bool>( res );
639sfcgal::shared_geom QgsSfcgalEngine::geometryN(
const sfcgal::geometry *geom,
unsigned int index, QString *errorMsg )
641 sfcgal::errorHandler()->clearText( errorMsg );
642 CHECK_NOT_NULL( geom,
nullptr );
644 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
645 CHECK_SUCCESS( errorMsg,
nullptr );
647 const sfcgal::geometry *out =
nullptr;
651 case SFCGAL_TYPE_GEOMETRYCOLLECTION:
652 case SFCGAL_TYPE_MULTILINESTRING:
653 case SFCGAL_TYPE_MULTIPOINT:
654 case SFCGAL_TYPE_MULTIPOLYGON:
655 case SFCGAL_TYPE_MULTISOLID:
657#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
660 const unsigned int nrGeoms = sfcgal_geometry_collection_num_geometries( geom );
661 if ( index < nrGeoms )
663 out = sfcgal_geometry_collection_geometry_n( geom, index );
667 sfcgal::errorHandler()->addText( u
"Cannot access geometry at position %s. GeometryCollection has only %d geometries."_s.arg( index ).arg( nrGeoms ) );
670 out = sfcgal_geometry_collection_geometry_n( geom, index );
674 case SFCGAL_TYPE_LINESTRING:
675 case SFCGAL_TYPE_POINT:
676 case SFCGAL_TYPE_POLYGON:
677 case SFCGAL_TYPE_POLYHEDRALSURFACE:
678 case SFCGAL_TYPE_SOLID:
679 case SFCGAL_TYPE_TRIANGLE:
680 case SFCGAL_TYPE_TRIANGULATEDSURFACE:
690 CHECK_SUCCESS( errorMsg,
nullptr );
692 sfcgal::shared_geom result = cloneGeometry( out, errorMsg );
693 CHECK_SUCCESS( errorMsg,
nullptr );
698sfcgal::shared_geom QgsSfcgalEngine::boundary(
const sfcgal::geometry *geom, QString *errorMsg )
700#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
703 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
705 sfcgal::errorHandler()->clearText( errorMsg );
706 CHECK_NOT_NULL( geom,
nullptr );
708 sfcgal::geometry *boundary = sfcgal_geometry_boundary( geom );
709 CHECK_SUCCESS( errorMsg,
nullptr );
711 return sfcgal::make_shared_geom( boundary );
715QgsPoint QgsSfcgalEngine::centroid(
const sfcgal::geometry *geom, QString *errorMsg )
717#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
720 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"centroid" ) );
722 sfcgal::errorHandler()->clearText( errorMsg );
725 const sfcgal::geometry *result =
nullptr;
726 if ( sfcgal_geometry_is_3d( geom ) )
727 result = sfcgal_geometry_centroid_3d( geom );
729 result = sfcgal_geometry_centroid( geom );
731 CHECK_SUCCESS( errorMsg,
QgsPoint() );
732 CHECK_NOT_NULL( result,
QgsPoint() );
734 QByteArray wkbArray = QgsSfcgalEngine::toWkb( result, errorMsg );
743sfcgal::shared_geom QgsSfcgalEngine::translate(
const sfcgal::geometry *geom,
const QgsVector3D &translation, QString *errorMsg )
745#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
747 ( void ) translation;
749 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"translate" ) );
751 sfcgal::errorHandler()->clearText( errorMsg );
752 CHECK_NOT_NULL( geom,
nullptr );
754 sfcgal::geometry *result;
755 if ( sfcgal_geometry_is_3d( geom ) )
756 result = sfcgal_geometry_translate_3d( geom, translation.
x(), translation.
y(), translation.
z() );
758 result = sfcgal_geometry_translate_2d( geom, translation.
x(), translation.
y() );
759 CHECK_SUCCESS( errorMsg,
nullptr );
761 return sfcgal::make_shared_geom( result );
765sfcgal::shared_geom QgsSfcgalEngine::scale(
const sfcgal::geometry *geom,
const QgsVector3D &scaleFactor,
const QgsPoint ¢er, QString *errorMsg )
767 sfcgal::errorHandler()->clearText( errorMsg );
768 CHECK_NOT_NULL( geom,
nullptr );
770 sfcgal::geometry *result;
773 result = sfcgal_geometry_scale_3d( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z() );
777 const double centerZ = center.
is3D() ? center.
z() : 0;
778 result = sfcgal_geometry_scale_3d_around_center( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z(), center.
x(), center.
y(), centerZ );
781 CHECK_SUCCESS( errorMsg,
nullptr );
782 return sfcgal::make_shared_geom( result );
785sfcgal::shared_geom QgsSfcgalEngine::rotate2D(
const sfcgal::geometry *geom,
double angle,
const QgsPoint ¢er, QString *errorMsg )
787 sfcgal::errorHandler()->clearText( errorMsg );
788 CHECK_NOT_NULL( geom,
nullptr );
790 sfcgal::geometry *result = sfcgal_geometry_rotate_2d( geom, angle, center.
x(), center.
y() );
792 CHECK_SUCCESS( errorMsg,
nullptr );
793 return sfcgal::make_shared_geom( result );
796sfcgal::shared_geom QgsSfcgalEngine::rotate3D(
const sfcgal::geometry *geom,
double angle,
const QgsVector3D &axisVector,
const QgsPoint ¢er, QString *errorMsg )
798 sfcgal::errorHandler()->clearText( errorMsg );
799 CHECK_NOT_NULL( geom,
nullptr );
801 sfcgal::geometry *result;
804 result = sfcgal_geometry_rotate_3d( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z() );
808 result = sfcgal_geometry_rotate_3d_around_center( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z(), center.
x(), center.
y(), center.
z() );
811 CHECK_SUCCESS( errorMsg,
nullptr );
812 return sfcgal::make_shared_geom( result );
815double QgsSfcgalEngine::distance(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
817 double out = geomgeom_to_primtype<double>( sfcgal_geometry_distance, sfcgal_geometry_distance_3d, geomA, geomB, errorMsg );
818 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
822bool QgsSfcgalEngine::distanceWithin(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB,
double maxdistance, QString *errorMsg )
824 double dist = QgsSfcgalEngine::distance( geomA, geomB, errorMsg );
825 CHECK_SUCCESS( errorMsg,
false );
827 return dist <= maxdistance;
830double QgsSfcgalEngine::area(
const sfcgal::geometry *geom, QString *errorMsg )
832 double out = geom_to_primtype<double>( sfcgal_geometry_area, sfcgal_geometry_area_3d, geom, errorMsg );
833 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
837double QgsSfcgalEngine::length(
const sfcgal::geometry *geom, QString *errorMsg )
839#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
842 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"length" ) );
844 double out = geom_to_primtype<double>( sfcgal_geometry_length, sfcgal_geometry_length_3d, geom, errorMsg );
845 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
850bool QgsSfcgalEngine::intersects(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
852 int res = geomgeom_to_primtype<int>( sfcgal_geometry_intersects, sfcgal_geometry_intersects_3d, geomA, geomB, errorMsg );
853 CHECK_SUCCESS( errorMsg,
false );
854 return static_cast<bool>( res );
857sfcgal::shared_geom QgsSfcgalEngine::intersection(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
859 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_intersection, sfcgal_geometry_intersection_3d, geomA, geomB, errorMsg );
860 CHECK_SUCCESS( errorMsg,
nullptr );
864sfcgal::shared_geom QgsSfcgalEngine::difference(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
866 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_difference, sfcgal_geometry_difference_3d, geomA, geomB, errorMsg );
867 CHECK_SUCCESS( errorMsg,
nullptr );
871sfcgal::shared_geom QgsSfcgalEngine::combine(
const QVector<sfcgal::shared_geom> &geomList, QString *errorMsg )
873 sfcgal::errorHandler()->clearText( errorMsg );
874 sfcgal::geometry *combined =
nullptr;
875 for ( sfcgal::shared_geom other : geomList )
879 combined = other.get();
883 if ( sfcgal_geometry_is_3d( other.get() ) || sfcgal_geometry_is_3d( combined ) )
884 combined = sfcgal_geometry_union_3d( combined, other.get() );
886 combined = sfcgal_geometry_union( combined, other.get() );
889 sfcgal::errorHandler()->addText(
"SFCGAL produced null result." );
891 CHECK_SUCCESS( errorMsg,
nullptr );
894 return sfcgal::make_shared_geom( combined );
897sfcgal::shared_geom QgsSfcgalEngine::triangulate(
const sfcgal::geometry *geom, QString *errorMsg )
899 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_triangulate_2dz,
nullptr, geom, errorMsg );
900 CHECK_SUCCESS( errorMsg,
nullptr );
904bool QgsSfcgalEngine::covers(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
906 int res = geomgeom_to_primtype<int>( sfcgal_geometry_covers, sfcgal_geometry_covers_3d, geomA, geomB, errorMsg );
907 CHECK_SUCCESS( errorMsg,
false );
908 return static_cast<bool>( res );
911sfcgal::shared_geom QgsSfcgalEngine::envelope(
const sfcgal::geometry *geom, QString *errorMsg )
913#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
916 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"envelope" ) );
918 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_envelope, sfcgal_geometry_envelope_3d, geom, errorMsg );
919 CHECK_SUCCESS( errorMsg,
nullptr );
924sfcgal::shared_geom QgsSfcgalEngine::convexHull(
const sfcgal::geometry *geom, QString *errorMsg )
926 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_convexhull, sfcgal_geometry_convexhull_3d, geom, errorMsg );
927 CHECK_SUCCESS( errorMsg,
nullptr );
931sfcgal::shared_geom QgsSfcgalEngine::offsetCurve(
const sfcgal::geometry *geom,
double distance,
int,
Qgis::JoinStyle, QString *errorMsg )
933 sfcgal::errorHandler()->clearText( errorMsg );
934 CHECK_NOT_NULL( geom,
nullptr );
936 sfcgal::geometry *result =
nullptr;
937 result = sfcgal_geometry_offset_polygon( geom, distance );
939 CHECK_SUCCESS( errorMsg,
nullptr );
941 return sfcgal::make_shared_geom( result );
944sfcgal::shared_geom QgsSfcgalEngine::buffer2D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle joinStyle, QString *errorMsg )
947 qWarning() << ( u
"Buffer not implemented for %1! Defaulting to round join."_s );
949 return offsetCurve( geom, radius, segments, joinStyle, errorMsg );
952sfcgal::shared_geom QgsSfcgalEngine::buffer3D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle3D joinStyle3D, QString *errorMsg )
954 sfcgal::errorHandler()->clearText( errorMsg );
955 CHECK_NOT_NULL( geom,
nullptr );
957 sfcgal_buffer3d_type_t buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
958 switch ( joinStyle3D )
961 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
964 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_ROUND;
967 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_CYLSPHERE;
971 sfcgal::geometry *result = sfcgal_geometry_buffer3d( geom, radius, segments, buffer_type );
972 CHECK_SUCCESS( errorMsg,
nullptr );
974 return sfcgal::make_shared_geom( result );
977sfcgal::shared_geom QgsSfcgalEngine::extrude(
const sfcgal::geometry *geom,
const QgsVector3D &extrusion, QString *errorMsg )
979 sfcgal::errorHandler()->clearText( errorMsg );
980 CHECK_NOT_NULL( geom,
nullptr );
982 sfcgal_geometry_t *solid = sfcgal_geometry_extrude( geom, extrusion.
x(), extrusion.
y(), extrusion.
z() );
984 CHECK_SUCCESS( errorMsg,
nullptr );
989 sfcgal::shared_geom polySurface = QgsSfcgalEngine::toPolyhedralSurface( solid, errorMsg );
990 sfcgal_geometry_delete( solid );
992 CHECK_SUCCESS( errorMsg,
nullptr );
997sfcgal::shared_geom QgsSfcgalEngine::simplify(
const sfcgal::geometry *geom,
double tolerance,
bool preserveTopology, QString *errorMsg )
999#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
1002 ( void ) preserveTopology;
1004 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
1006 sfcgal::errorHandler()->clearText( errorMsg );
1007 CHECK_NOT_NULL( geom,
nullptr );
1009 sfcgal::geometry *result = sfcgal_geometry_simplify( geom, tolerance, preserveTopology );
1010 CHECK_SUCCESS( errorMsg,
nullptr );
1012 return sfcgal::make_shared_geom( result );
1016sfcgal::shared_geom QgsSfcgalEngine::approximateMedialAxis(
const sfcgal::geometry *geom,
bool extendToEdges, QString *errorMsg )
1018 sfcgal::errorHandler()->clearText( errorMsg );
1019 CHECK_NOT_NULL( geom,
nullptr );
1021#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
1022 sfcgal::geometry *result =
nullptr;
1023 if ( extendToEdges )
1025 result = sfcgal_geometry_projected_medial_axis( geom );
1029 result = sfcgal_geometry_approximate_medial_axis( geom );
1032 Q_UNUSED( extendToEdges )
1033 sfcgal::geometry *result = sfcgal_geometry_approximate_medial_axis( geom );
1035 CHECK_SUCCESS( errorMsg,
nullptr );
1037 return sfcgal::make_shared_geom( result );
1040sfcgal::shared_geom QgsSfcgalEngine::toSolid(
const sfcgal::geometry *geom, QString *errorMsg )
1042 sfcgal::errorHandler()->clearText( errorMsg );
1043 CHECK_NOT_NULL( geom,
nullptr );
1045 sfcgal::geometry *solid = sfcgal_geometry_make_solid( geom );
1046 CHECK_SUCCESS( errorMsg,
nullptr );
1048 return sfcgal::make_shared_geom( solid );
1051sfcgal::shared_geom QgsSfcgalEngine::toPolyhedralSurface(
const sfcgal::geometry *geom, QString *errorMsg )
1053 sfcgal::errorHandler()->clearText( errorMsg );
1054 CHECK_NOT_NULL( geom,
nullptr );
1056 if ( sfcgal_geometry_type_id( geom ) != SFCGAL_TYPE_SOLID )
1058 sfcgal::errorHandler()->addText( u
"toPolyhedralSurface() only applies to solids"_s );
1062 sfcgal_geometry_t *polySurface = sfcgal_polyhedral_surface_create();
1063 for (
unsigned int shellIdx = 0; shellIdx < sfcgal_solid_num_shells( geom ); ++shellIdx )
1065 const sfcgal_geometry_t *shell = sfcgal_solid_shell_n( geom, shellIdx );
1066#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
1067 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_patches( shell ); ++polyIdx )
1069 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_patch_n( shell, polyIdx );
1070 sfcgal_polyhedral_surface_add_patch( polySurface, sfcgal_geometry_clone( patch ) );
1073 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_polygons( shell ); ++polyIdx )
1075 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_polygon_n( shell, polyIdx );
1076 sfcgal_polyhedral_surface_add_polygon( polySurface, sfcgal_geometry_clone( patch ) );
1081 CHECK_SUCCESS( errorMsg,
nullptr );
1082 return sfcgal::make_shared_geom( polySurface );
1085#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
1086sfcgal::shared_geom QgsSfcgalEngine::transform(
const sfcgal::geometry *geom,
const QgsMatrix4x4 &mat, QString *errorMsg )
1088 sfcgal::errorHandler()->clearText( errorMsg );
1089 CHECK_NOT_NULL( geom,
nullptr );
1091 sfcgal::geometry *result;
1092 result = sfcgal_geometry_transform( geom, mat.
constData() );
1094 CHECK_SUCCESS( errorMsg,
nullptr );
1095 return sfcgal::make_shared_geom( result );
1098sfcgal::shared_geom QgsSfcgalEngine::split3D(
const sfcgal::geometry *geom,
const QgsPoint &planePoint,
const QgsVector3D &planeNormal,
bool closeGeometries, QString *errorMsg )
1100 sfcgal::errorHandler()->clearText( errorMsg );
1101 CHECK_NOT_NULL( geom,
nullptr );
1103 sfcgal::geometry *result = sfcgal_geometry_split_3d( geom, planePoint.
x(), planePoint.
y(), planePoint.
z(), planeNormal.
x(), planeNormal.
y(), planeNormal.
z(), closeGeometries );
1105 CHECK_SUCCESS( errorMsg,
nullptr );
1106 return sfcgal::make_shared_geom( result );
1109std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_prim &prim, sfcgal::primitiveType type, QString *errorMsg )
1111 sfcgal::errorHandler()->clearText( errorMsg );
1112 CHECK_NOT_NULL( prim.get(),
nullptr );
1114 return std::make_unique<QgsSfcgalGeometry>( prim, type );
1117sfcgal::shared_prim QgsSfcgalEngine::createBox(
double sizeX,
double sizeY,
double sizeZ, QString *errorMsg )
1119 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_BOX );
1120 CHECK_SUCCESS( errorMsg,
nullptr );
1122 sfcgal_primitive_set_parameter_double( result,
"x_extent", sizeX );
1123 sfcgal_primitive_set_parameter_double( result,
"y_extent", sizeY );
1124 sfcgal_primitive_set_parameter_double( result,
"z_extent", sizeZ );
1125 CHECK_SUCCESS( errorMsg,
nullptr );
1127 return sfcgal::make_shared_prim( result );
1130sfcgal::shared_prim QgsSfcgalEngine::createCone(
double bottomRadius,
double height,
double topRadius,
unsigned int radial, QString *errorMsg )
1132 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CONE );
1133 CHECK_SUCCESS( errorMsg,
nullptr );
1135 sfcgal_primitive_set_parameter_double( result,
"bottom_radius", bottomRadius );
1136 sfcgal_primitive_set_parameter_double( result,
"height", height );
1137 sfcgal_primitive_set_parameter_double( result,
"top_radius", topRadius );
1138 sfcgal_primitive_set_parameter_int( result,
"num_radial", radial );
1139 CHECK_SUCCESS( errorMsg,
nullptr );
1141 return sfcgal::make_shared_prim( result );
1144sfcgal::shared_prim QgsSfcgalEngine::createCube(
double size, QString *errorMsg )
1146 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CUBE );
1147 CHECK_SUCCESS( errorMsg,
nullptr );
1149 sfcgal_primitive_set_parameter_double( result,
"size", size );
1150 CHECK_SUCCESS( errorMsg,
nullptr );
1152 return sfcgal::make_shared_prim( result );
1155sfcgal::shared_prim QgsSfcgalEngine::createCylinder(
double radius,
double height,
unsigned int radial, QString *errorMsg )
1157 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CYLINDER );
1158 CHECK_SUCCESS( errorMsg,
nullptr );
1160 sfcgal_primitive_set_parameter_double( result,
"radius", radius );
1161 sfcgal_primitive_set_parameter_double( result,
"height", height );
1162 sfcgal_primitive_set_parameter_int( result,
"num_radial", radial );
1163 CHECK_SUCCESS( errorMsg,
nullptr );
1165 return sfcgal::make_shared_prim( result );
1168sfcgal::shared_prim QgsSfcgalEngine::createSphere(
double radius,
unsigned int subdivisions, QString *errorMsg )
1170 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_SPHERE );
1171 CHECK_SUCCESS( errorMsg,
nullptr );
1173 sfcgal_primitive_set_parameter_double( result,
"radius", radius );
1174 sfcgal_primitive_set_parameter_int( result,
"num_subdivisions", subdivisions );
1175 CHECK_SUCCESS( errorMsg,
nullptr );
1177 return sfcgal::make_shared_prim( result );
1180sfcgal::shared_prim QgsSfcgalEngine::createTorus(
double mainRadius,
double tubeRadius,
unsigned int mainRadial,
unsigned int tubeRadial, QString *errorMsg )
1182 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_TORUS );
1183 CHECK_SUCCESS( errorMsg,
nullptr );
1185 sfcgal_primitive_set_parameter_double( result,
"main_radius", mainRadius );
1186 sfcgal_primitive_set_parameter_double( result,
"tube_radius", tubeRadius );
1187 sfcgal_primitive_set_parameter_int( result,
"main_num_radial", mainRadial );
1188 sfcgal_primitive_set_parameter_int( result,
"tube_num_radial", tubeRadial );
1189 CHECK_SUCCESS( errorMsg,
nullptr );
1191 return sfcgal::make_shared_prim( result );
1194sfcgal::shared_geom QgsSfcgalEngine::primitiveAsPolyhedral(
const sfcgal::primitive *prim, QString *errorMsg )
1196 sfcgal::errorHandler()->clearText( errorMsg );
1197 CHECK_NOT_NULL( prim,
nullptr );
1199 sfcgal::geometry *result = sfcgal_primitive_as_polyhedral_surface( prim );
1200 CHECK_SUCCESS( errorMsg,
nullptr );
1202 return sfcgal::make_shared_geom( result );
1205bool QgsSfcgalEngine::primitiveIsEqual(
const sfcgal::primitive *primA,
const sfcgal::primitive *primB,
double tolerance, QString *errorMsg )
1207 sfcgal::errorHandler()->clearText( errorMsg );
1208 CHECK_NOT_NULL( primA,
false );
1209 CHECK_NOT_NULL( primB,
false );
1211 bool result = sfcgal_primitive_is_almost_equals( primA, primB, tolerance );
1212 CHECK_SUCCESS( errorMsg,
false );
1217sfcgal::shared_prim QgsSfcgalEngine::primitiveClone(
const sfcgal::primitive *prim, QString *errorMsg )
1219 sfcgal::errorHandler()->clearText( errorMsg );
1220 CHECK_NOT_NULL( prim,
nullptr );
1222 sfcgal::primitive *result = sfcgal_primitive_clone( prim );
1224 CHECK_SUCCESS( errorMsg,
nullptr );
1225 CHECK_NOT_NULL( result,
nullptr );
1227 return sfcgal::make_shared_prim( result );
1230double QgsSfcgalEngine::primitiveArea(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1232 sfcgal::errorHandler()->clearText( errorMsg );
1233 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1235 double area = sfcgal_primitive_area( prim, withDiscretization );
1237 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1241double QgsSfcgalEngine::primitiveVolume(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1243 sfcgal::errorHandler()->clearText( errorMsg );
1244 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1246 const double volume = sfcgal_primitive_volume( prim, withDiscretization );
1247 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1252void sfcgal::to_json( json &j,
const sfcgal::PrimitiveParameterDesc &p )
1257 if ( std::holds_alternative<int>( p.value ) )
1259 j[
"value"] = std::get<int>( p.value );
1261 else if ( std::holds_alternative<double>( p.value ) )
1263 j[
"value"] = std::get<double>( p.value );
1265 else if ( std::holds_alternative<QgsPoint>( p.value ) )
1267 QgsPoint point = std::get<QgsPoint>( p.value );
1268 double z = std::numeric_limits<double>::quiet_NaN();
1269 double m = std::numeric_limits<double>::quiet_NaN();
1274 j[
"value"] = std::vector<double> { point.
x(), point.
y(), z, m };
1276 else if ( std::holds_alternative<QgsVector3D>( p.value ) )
1278 QgsVector3D vect = std::get<QgsVector3D>( p.value );
1279 j[
"value"] = std::vector<double> { vect.
x(), vect.
y(), vect.
z() };
1282 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1285void sfcgal::from_json(
const json &j, sfcgal::PrimitiveParameterDesc &p )
1287 j.at(
"name" ).get_to( p.name );
1288 j.at(
"type" ).get_to( p.type );
1289 if ( j.contains(
"value" ) )
1291 json value = j.at(
"value" );
1292 if ( p.type ==
"int" )
1294 p.value = value.get<
int>();
1296 else if ( p.type ==
"double" )
1298 p.value = value.get<
double>();
1300 else if ( p.type ==
"point3" )
1302 std::vector<double> vect;
1303 vect = value.get<std::vector<double>>();
1307 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1308 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1312 else if ( p.type ==
"vector3" )
1314 std::vector<double> vect;
1315 vect = value.get<std::vector<double>>();
1319 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1320 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() )
1325 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1329QVector<sfcgal::PrimitiveParameterDesc> QgsSfcgalEngine::primitiveParameters(
const sfcgal::primitive *prim, QString *errorMsg )
1331 sfcgal::errorHandler()->clearText( errorMsg );
1332 CHECK_NOT_NULL( prim, QVector<sfcgal::PrimitiveParameterDesc>() );
1334 char *jsonChars =
nullptr;
1336 sfcgal_primitive_parameters( prim, &jsonChars, &len );
1337 CHECK_SUCCESS( errorMsg, QVector<sfcgal::PrimitiveParameterDesc>() );
1339 std::string jsonString( jsonChars, len );
1340 sfcgal_free_buffer( jsonChars );
1342 QVector<sfcgal::PrimitiveParameterDesc> result;
1345 const auto jParams = json::parse( jsonString );
1346 for (
const auto &jParam : jParams )
1348 result.append( jParam.get<sfcgal::PrimitiveParameterDesc>() );
1351 catch ( json::exception &e )
1353 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1359QVariant QgsSfcgalEngine::primitiveParameter(
const sfcgal::primitive *prim,
const QString &name, QString *errorMsg )
1361 sfcgal::errorHandler()->clearText( errorMsg );
1362 CHECK_NOT_NULL( prim, QVariant() );
1364 char *jsonChars =
nullptr;
1366 sfcgal_primitive_parameter( prim, name.toStdString().c_str(), &jsonChars, &len );
1367 CHECK_SUCCESS( errorMsg, QVariant() );
1369 std::string jsonString( jsonChars, len );
1370 sfcgal_free_buffer( jsonChars );
1375 const auto jParam = json::parse( jsonString );
1376 sfcgal::PrimitiveParameterDesc param = jParam.get<sfcgal::PrimitiveParameterDesc>();
1377 result = QVariant::fromStdVariant( param.value );
1379 catch ( json::exception &e )
1381 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1387void QgsSfcgalEngine::primitiveSetParameter( sfcgal::primitive *prim,
const QString &name,
const QVariant &value, QString *errorMsg )
1389 sfcgal::errorHandler()->clearText( errorMsg );
1390 CHECK_NOT_NULL( prim,
void() );
1395 sfcgal::PrimitiveParameterDesc paramDesc;
1396 paramDesc.name = name.toStdString();
1397 paramDesc.type = value.typeName();
1398 if ( paramDesc.type ==
"int" )
1399 paramDesc.value = value.toInt();
1400 else if ( paramDesc.type ==
"double" )
1401 paramDesc.value = value.toDouble();
1402 else if ( value.canConvert<
QgsPoint>() )
1403 paramDesc.value = value.value<
QgsPoint>();
1407 sfcgal::to_json( jParam, paramDesc );
1408 std::string jsonStr = jParam.dump();
1409 sfcgal_primitive_set_parameter( prim, name.toStdString().c_str(), jsonStr.c_str() );
1410 CHECK_SUCCESS( errorMsg,
void() );
1414 sfcgal::errorHandler()->addText( u
"Caught json exception"_s );
1418sfcgal::shared_prim QgsSfcgalEngine::primitiveTranslate(
const sfcgal::primitive *prim,
const QgsVector3D &translation, QString *errorMsg )
1420 sfcgal::primitive *result = sfcgal_primitive_translate( prim, translation.
x(), translation.
y(), translation.
z() );
1421 CHECK_SUCCESS( errorMsg,
nullptr );
1423 return sfcgal::make_shared_prim( result );
1426sfcgal::shared_geom QgsSfcgalEngine::primitiveRotate(
const sfcgal::primitive *prim,
double angle,
const QgsVector3D &axisVector,
const QgsPoint ¢er, QString *errorMsg )
1430 sfcgal::primitive *result = sfcgal_primitive_rotate( prim, angle, axisVector.
x(), axisVector.
y(), axisVector.
z(), rotationCenter.
x(), rotationCenter.
y(), rotationCenter.
z() );
1431 CHECK_SUCCESS( errorMsg,
nullptr );
1433 return sfcgal::make_shared_prim( result );
1436sfcgal::shared_geom QgsSfcgalEngine::primitiveScale(
const sfcgal::primitive *prim,
const QgsVector3D &scaleFactor,
const QgsPoint ¢er, QString *errorMsg )
1440 sfcgal::primitive *result = sfcgal_primitive_scale( prim, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z(), scaleCenter.
x(), scaleCenter.
y(), scaleCenter.
z() );
1441 CHECK_SUCCESS( errorMsg,
nullptr );
1443 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.