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" ).arg( msg )
142 .arg( QString::fromStdString( location.function_name() ) )
143 .arg( QString::fromStdString( location.file_name() ) )
144 .arg( location.line() );
146 errorMessages.push_front( txt );
162static T geom_to_primtype(
163 T( *func_2d )(
const sfcgal_geometry_t * ),
164 T( *func_3d )(
const sfcgal_geometry_t * ),
165 const sfcgal::geometry *geom,
169 sfcgal::errorHandler()->clearText( errorMsg );
170 CHECK_NOT_NULL( geom, std::numeric_limits<T>::quiet_NaN() );
173 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
174 result = func_3d( geom );
176 result = func_2d( geom );
178 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
193static T geomgeom_to_primtype(
194 T( *func_2d )(
const sfcgal_geometry_t *,
const sfcgal_geometry_t * ),
195 T( *func_3d )(
const sfcgal_geometry_t *,
const sfcgal_geometry_t * ),
196 const sfcgal::geometry *geomA,
197 const sfcgal::geometry *geomB,
201 sfcgal::errorHandler()->clearText( errorMsg );
202 CHECK_NOT_NULL( geomA,
false );
203 CHECK_NOT_NULL( geomB,
false );
206 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
207 result = func_3d( geomA, geomB );
209 result = func_2d( geomA, geomB );
211 CHECK_SUCCESS( errorMsg, std::numeric_limits<T>::quiet_NaN() );
224static sfcgal::shared_geom geom_to_geom(
225 sfcgal::func_geom_to_geom func_2d,
226 sfcgal::func_geom_to_geom func_3d,
227 const sfcgal::geometry *geom,
231 sfcgal::errorHandler()->clearText( errorMsg );
232 CHECK_NOT_NULL( geom,
nullptr );
234 sfcgal::geometry *result =
nullptr;
235 if ( func_3d && sfcgal_geometry_is_3d( geom ) )
236 result = func_3d( geom );
238 result = func_2d( geom );
240 CHECK_SUCCESS( errorMsg,
nullptr );
241 CHECK_NOT_NULL( result,
nullptr );
243 return sfcgal::make_shared_geom( result );
256static sfcgal::shared_geom geomgeom_to_geom(
257 sfcgal::func_geomgeom_to_geom func_2d,
258 sfcgal::func_geomgeom_to_geom func_3d,
259 const sfcgal::geometry *geomA,
260 const sfcgal::geometry *geomB,
264 sfcgal::errorHandler()->clearText( errorMsg );
265 CHECK_NOT_NULL( geomA,
nullptr );
266 CHECK_NOT_NULL( geomB,
nullptr );
268 sfcgal::geometry *result =
nullptr;
269 if ( func_3d && ( sfcgal_geometry_is_3d( geomA ) || sfcgal_geometry_is_3d( geomB ) ) )
270 result = func_3d( geomA, geomB );
272 result = func_2d( geomA, geomB );
274 CHECK_SUCCESS( errorMsg,
nullptr );
275 CHECK_NOT_NULL( result,
nullptr );
277 return sfcgal::make_shared_geom( result );
286std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_geom &geom, QString *errorMsg )
288 sfcgal::errorHandler()->clearText( errorMsg );
289 CHECK_NOT_NULL( geom.get(),
nullptr );
291 return std::make_unique<QgsSfcgalGeometry>( geom );
294std::unique_ptr<QgsAbstractGeometry> QgsSfcgalEngine::toAbstractGeometry(
const sfcgal::geometry *geom, QString *errorMsg )
296 std::unique_ptr<QgsAbstractGeometry> out(
nullptr );
297 sfcgal::errorHandler()->clearText( errorMsg );
298 CHECK_NOT_NULL( geom, out );
300 QByteArray wkbArray = QgsSfcgalEngine::toWkb( geom, errorMsg );
301 CHECK_SUCCESS( errorMsg, out );
308 sfcgal::errorHandler()->addText( u
"WKB contains unmanaged geometry type (WKB:%1 / SFCGAL:%2"_s
309 .arg(
static_cast<int>( wkbPtr.readHeader() ) )
310 .arg(
static_cast<int>( sfcgalType ) ) );
316sfcgal::shared_geom QgsSfcgalEngine::fromAbstractGeometry(
const QgsAbstractGeometry *geom, QString *errorMsg )
318 sfcgal::errorHandler()->clearText( errorMsg );
319 CHECK_NOT_NULL( geom, sfcgal::shared_geom(
nullptr ) );
321 QByteArray wkbBytes = geom->
asWkb();
323 sfcgal::geometry *out = sfcgal_io_read_wkb( wkbBytes.data(), wkbBytes.length() );
324 CHECK_SUCCESS( errorMsg,
nullptr );
326 return sfcgal::make_shared_geom( out );
329sfcgal::shared_geom QgsSfcgalEngine::cloneGeometry(
const sfcgal::geometry *geom, QString *errorMsg )
331 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_clone,
nullptr, geom, errorMsg );
332 CHECK_SUCCESS( errorMsg,
nullptr );
336QString QgsSfcgalEngine::geometryType(
const sfcgal::geometry *geom, QString *errorMsg )
338#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
341 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"geometryType" ) );
343 sfcgal::errorHandler()->clearText( errorMsg );
347 sfcgal_geometry_type( geom, &typeChar, &typeLen );
348 std::string typeStr( typeChar, typeLen );
349 sfcgal_free_buffer( typeChar );
351 return QString::fromStdString( typeStr );
355sfcgal::shared_geom QgsSfcgalEngine::fromWkb(
const QgsConstWkbPtr &wkbPtr, QString *errorMsg )
357 sfcgal::errorHandler()->clearText( errorMsg );
359 const unsigned char *wkbUnsignedPtr = wkbPtr;
360 sfcgal::geometry *out = sfcgal_io_read_wkb(
reinterpret_cast<const char *
>( wkbUnsignedPtr ), wkbPtr.
remaining() );
361 CHECK_SUCCESS( errorMsg,
nullptr );
363 return sfcgal::make_shared_geom( out );
366sfcgal::shared_geom QgsSfcgalEngine::fromWkt(
const QString &wkt, QString *errorMsg )
368 sfcgal::errorHandler()->clearText( errorMsg );
370 sfcgal::geometry *out = sfcgal_io_read_wkt( wkt.toStdString().c_str(), wkt.length() );
371 CHECK_SUCCESS( errorMsg,
nullptr );
373 return sfcgal::unique_geom( out );
376QByteArray QgsSfcgalEngine::toWkb(
const sfcgal::geometry *geom, QString *errorMsg )
378 sfcgal::errorHandler()->clearText( errorMsg );
379 CHECK_NOT_NULL( geom, QByteArray() );
383 sfcgal_geometry_as_wkb( geom, &wkbHex, &len );
384 QByteArray wkbArray( wkbHex,
static_cast<int>( len ) );
386#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
387 sfcgal_free_buffer( wkbHex );
392 CHECK_SUCCESS( errorMsg, QByteArray() );
396QString QgsSfcgalEngine::toWkt(
const sfcgal::geometry *geom,
int numDecimals, QString *errorMsg )
398 sfcgal::errorHandler()->clearText( errorMsg );
399 CHECK_NOT_NULL( geom, QString() );
403 sfcgal_geometry_as_text_decim( geom, numDecimals, &wkt, &len );
404 CHECK_SUCCESS( errorMsg, QString() );
406 std::string wktString( wkt, len );
407#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
408 sfcgal_free_buffer( wkt );
412 return QString::fromStdString( wktString );
415Qgis::WkbType QgsSfcgalEngine::wkbType(
const sfcgal::geometry *geom, QString *errorMsg )
417 sfcgal::errorHandler()->clearText( errorMsg );
420 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
424 if ( sfcgal_geometry_is_3d( geom ) )
427 if ( sfcgal_geometry_is_measured( geom ) )
434 sfcgal::errorHandler()->addText( u
"WKB type '%1' is not known from QGIS"_s.arg( wkbType ) );
438int QgsSfcgalEngine::dimension(
const sfcgal::geometry *geom, QString *errorMsg )
440#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
443 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dimension" ) );
445 int out = geom_to_primtype<int>( sfcgal_geometry_dimension,
nullptr, geom, errorMsg );
446 CHECK_SUCCESS( errorMsg, std::numeric_limits<int>::quiet_NaN() );
451int QgsSfcgalEngine::partCount(
const sfcgal::geometry *geom, QString *errorMsg )
454 sfcgal::errorHandler()->clearText( errorMsg );
455 CHECK_NOT_NULL( geom, -1 );
457 sfcgal_geometry_type_t type = sfcgal_geometry_type_id( geom );
458 CHECK_SUCCESS( errorMsg, -1 );
462 case SFCGAL_TYPE_MULTIPOINT:
463 case SFCGAL_TYPE_MULTILINESTRING:
464 case SFCGAL_TYPE_MULTIPOLYGON:
465 case SFCGAL_TYPE_MULTISOLID:
466 case SFCGAL_TYPE_GEOMETRYCOLLECTION:
467#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
468 out = sfcgal_geometry_num_geometries( geom );
470 out = sfcgal_geometry_collection_num_geometries( geom );
473 case SFCGAL_TYPE_POLYGON:
474 out = sfcgal_polygon_num_interior_rings( geom ) + 1;
476 case SFCGAL_TYPE_SOLID:
477 out = sfcgal_solid_num_shells( geom );
479 case SFCGAL_TYPE_POLYHEDRALSURFACE:
480#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
481 out = sfcgal_polyhedral_surface_num_patches( geom );
483 out = sfcgal_polyhedral_surface_num_polygons( geom );
486 case SFCGAL_TYPE_TRIANGULATEDSURFACE:
487#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
488 out = sfcgal_triangulated_surface_num_patches( geom );
490 out = sfcgal_triangulated_surface_num_triangles( geom );
493 case SFCGAL_TYPE_LINESTRING:
494 out = sfcgal_linestring_num_points( geom );
496 case SFCGAL_TYPE_TRIANGLE:
499 case SFCGAL_TYPE_POINT:
506 CHECK_SUCCESS( errorMsg, -1 );
508 return static_cast<int>( out );
511bool QgsSfcgalEngine::addZValue( sfcgal::geometry *geom,
double zValue, 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(
"addZValue" ) );
519 sfcgal::errorHandler()->clearText( errorMsg );
520 CHECK_NOT_NULL( geom,
false );
522 return sfcgal_geometry_force_z( geom, zValue );
526bool QgsSfcgalEngine::addMValue( sfcgal::geometry *geom,
double mValue, QString *errorMsg )
528#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
532 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"addMValue" ) );
534 sfcgal::errorHandler()->clearText( errorMsg );
535 CHECK_NOT_NULL( geom,
false );
537 return sfcgal_geometry_force_m( geom, mValue );
541bool QgsSfcgalEngine::dropZValue( sfcgal::geometry *geom, QString *errorMsg )
543#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
546 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dropZValue" ) );
548 sfcgal::errorHandler()->clearText( errorMsg );
549 CHECK_NOT_NULL( geom,
false );
551 return sfcgal_geometry_drop_z( geom );
555bool QgsSfcgalEngine::dropMValue( sfcgal::geometry *geom, QString *errorMsg )
557#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
560 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"dropMValue" ) );
562 sfcgal::errorHandler()->clearText( errorMsg );
563 CHECK_NOT_NULL( geom,
false );
565 return sfcgal_geometry_drop_m( geom );
569void QgsSfcgalEngine::swapXy( sfcgal::geometry *geom, QString *errorMsg )
571#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
574 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"swapXy" ) );
576 sfcgal::errorHandler()->clearText( errorMsg );
577 CHECK_NOT_NULL( geom,
void() );
579 sfcgal_geometry_swap_xy( geom );
583bool QgsSfcgalEngine::isEqual(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB,
double tolerance, QString *errorMsg )
585#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
590 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"isEqual" ) );
592 sfcgal::errorHandler()->clearText( errorMsg );
593 CHECK_NOT_NULL( geomA,
false );
594 CHECK_NOT_NULL( geomB,
false );
596 bool result = sfcgal_geometry_is_almost_equals( geomA, geomB, tolerance );
597 CHECK_SUCCESS( errorMsg,
false );
603bool QgsSfcgalEngine::isEmpty(
const sfcgal::geometry *geom, QString *errorMsg )
605 int res = geom_to_primtype<int>( sfcgal_geometry_is_empty,
nullptr, geom, errorMsg );
606 CHECK_SUCCESS( errorMsg,
false );
607 return static_cast<bool>( res );
610bool QgsSfcgalEngine::isValid(
const sfcgal::geometry *geom, QString *errorMsg,
QgsGeometry *errorLoc )
612 sfcgal::errorHandler()->clearText( errorMsg );
613 CHECK_NOT_NULL( geom,
false );
617 sfcgal::geometry *location;
618 result = sfcgal_geometry_is_valid_detail( geom, &reason, &location );
620 CHECK_SUCCESS( errorMsg,
false );
622 if ( reason && strlen( reason ) )
624 sfcgal::errorHandler()->addText( QString( reason ) );
628 if ( location && errorLoc )
630 std::unique_ptr<QgsAbstractGeometry> locationGeom = toAbstractGeometry( location, errorMsg );
631 CHECK_SUCCESS( errorMsg,
false );
632 errorLoc->
addPartV2( locationGeom.release() );
638bool QgsSfcgalEngine::isSimple(
const sfcgal::geometry *geom, QString *errorMsg )
640#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
643 throw QgsNotSupportedException( QObject::tr(
"Using %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"isSimple" ) );
645 int res = geom_to_primtype<int>( sfcgal_geometry_is_simple,
nullptr, geom, errorMsg );
646 CHECK_SUCCESS( errorMsg,
false );
647 return static_cast<bool>( res );
651sfcgal::shared_geom QgsSfcgalEngine::boundary(
const sfcgal::geometry *geom, QString *errorMsg )
653#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
656 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
658 sfcgal::errorHandler()->clearText( errorMsg );
659 CHECK_NOT_NULL( geom,
nullptr );
661 sfcgal::geometry *boundary = sfcgal_geometry_boundary( geom );
662 CHECK_SUCCESS( errorMsg,
nullptr );
664 return sfcgal::make_shared_geom( boundary );
668QgsPoint QgsSfcgalEngine::centroid(
const sfcgal::geometry *geom, QString *errorMsg )
670#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
673 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"centroid" ) );
675 sfcgal::errorHandler()->clearText( errorMsg );
678 const sfcgal::geometry *result =
nullptr;
679 if ( sfcgal_geometry_is_3d( geom ) )
680 result = sfcgal_geometry_centroid_3d( geom );
682 result = sfcgal_geometry_centroid( geom );
684 CHECK_SUCCESS( errorMsg,
QgsPoint() );
685 CHECK_NOT_NULL( result,
QgsPoint() );
687 QByteArray wkbArray = QgsSfcgalEngine::toWkb( result, errorMsg );
696sfcgal::shared_geom QgsSfcgalEngine::translate(
const sfcgal::geometry *geom,
const QgsVector3D &translation, QString *errorMsg )
698#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
702 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"translate" ) );
704 sfcgal::errorHandler()->clearText( errorMsg );
705 CHECK_NOT_NULL( geom,
nullptr );
707 sfcgal::geometry *result;
708 if ( sfcgal_geometry_is_3d( geom ) )
709 result = sfcgal_geometry_translate_3d( geom, translation.
x(), translation.
y(), translation.
z() );
711 result = sfcgal_geometry_translate_2d( geom, translation.
x(), translation.
y() );
712 CHECK_SUCCESS( errorMsg,
nullptr );
714 return sfcgal::make_shared_geom( result );
718sfcgal::shared_geom QgsSfcgalEngine::scale(
const sfcgal::geometry *geom,
const QgsVector3D &scaleFactor,
const QgsPoint ¢er, QString *errorMsg )
720 sfcgal::errorHandler()->clearText( errorMsg );
721 CHECK_NOT_NULL( geom,
nullptr );
723 sfcgal::geometry *result;
726 result = sfcgal_geometry_scale_3d( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z() );
730 const double centerZ = center.
is3D() ? center.
z() : 0;
731 result = sfcgal_geometry_scale_3d_around_center( geom, scaleFactor.
x(), scaleFactor.
y(), scaleFactor.
z(), center.
x(), center.
y(), centerZ );
734 CHECK_SUCCESS( errorMsg,
nullptr );
735 return sfcgal::make_shared_geom( result );
738sfcgal::shared_geom QgsSfcgalEngine::rotate2D(
const sfcgal::geometry *geom,
double angle,
const QgsPoint ¢er, QString *errorMsg )
740 sfcgal::errorHandler()->clearText( errorMsg );
741 CHECK_NOT_NULL( geom,
nullptr );
743 sfcgal::geometry *result = sfcgal_geometry_rotate_2d( geom, angle, center.
x(), center.
y() );
745 CHECK_SUCCESS( errorMsg,
nullptr );
746 return sfcgal::make_shared_geom( result );
749sfcgal::shared_geom QgsSfcgalEngine::rotate3D(
const sfcgal::geometry *geom,
double angle,
const QgsVector3D &axisVector,
const QgsPoint ¢er, QString *errorMsg )
751 sfcgal::errorHandler()->clearText( errorMsg );
752 CHECK_NOT_NULL( geom,
nullptr );
754 sfcgal::geometry *result;
757 result = sfcgal_geometry_rotate_3d( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z() );
761 result = sfcgal_geometry_rotate_3d_around_center( geom, angle, axisVector.
x(), axisVector.
y(), axisVector.
z(), center.
x(), center.
y(), center.
z() );
764 CHECK_SUCCESS( errorMsg,
nullptr );
765 return sfcgal::make_shared_geom( result );
768double QgsSfcgalEngine::distance(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
770 double out = geomgeom_to_primtype<double>( sfcgal_geometry_distance, sfcgal_geometry_distance_3d, geomA, geomB, errorMsg );
771 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
775bool QgsSfcgalEngine::distanceWithin(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB,
double maxdistance, QString *errorMsg )
777 double dist = QgsSfcgalEngine::distance( geomA, geomB, errorMsg );
778 CHECK_SUCCESS( errorMsg,
false );
780 return dist <= maxdistance;
783double QgsSfcgalEngine::area(
const sfcgal::geometry *geom, QString *errorMsg )
785 double out = geom_to_primtype<double>( sfcgal_geometry_area, sfcgal_geometry_area_3d, geom, errorMsg );
786 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
790double QgsSfcgalEngine::length(
const sfcgal::geometry *geom, QString *errorMsg )
792#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
795 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"length" ) );
797 double out = geom_to_primtype<double>( sfcgal_geometry_length, sfcgal_geometry_length_3d, geom, errorMsg );
798 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
803bool QgsSfcgalEngine::intersects(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
805 int res = geomgeom_to_primtype<int>( sfcgal_geometry_intersects, sfcgal_geometry_intersects_3d, geomA, geomB, errorMsg );
806 CHECK_SUCCESS( errorMsg,
false );
807 return static_cast<bool>( res );
810sfcgal::shared_geom QgsSfcgalEngine::intersection(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
812 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_intersection, sfcgal_geometry_intersection_3d, geomA, geomB, errorMsg );
813 CHECK_SUCCESS( errorMsg,
nullptr );
817sfcgal::shared_geom QgsSfcgalEngine::difference(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
819 sfcgal::shared_geom out = geomgeom_to_geom( sfcgal_geometry_difference, sfcgal_geometry_difference_3d, geomA, geomB, errorMsg );
820 CHECK_SUCCESS( errorMsg,
nullptr );
824sfcgal::shared_geom QgsSfcgalEngine::combine(
const QVector<sfcgal::shared_geom> &geomList, QString *errorMsg )
826 sfcgal::errorHandler()->clearText( errorMsg );
827 sfcgal::geometry *combined =
nullptr;
828 for ( sfcgal::shared_geom other : geomList )
832 combined = other.get();
836 if ( sfcgal_geometry_is_3d( other.get() ) || sfcgal_geometry_is_3d( combined ) )
837 combined = sfcgal_geometry_union_3d( combined, other.get() );
839 combined = sfcgal_geometry_union( combined, other.get() );
842 sfcgal::errorHandler()->addText(
"SFCGAL produced null result." );
844 CHECK_SUCCESS( errorMsg,
nullptr );
847 return sfcgal::make_shared_geom( combined );
850sfcgal::shared_geom QgsSfcgalEngine::triangulate(
const sfcgal::geometry *geom, QString *errorMsg )
852 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_triangulate_2dz,
nullptr, geom, errorMsg );
853 CHECK_SUCCESS( errorMsg,
nullptr );
857bool QgsSfcgalEngine::covers(
const sfcgal::geometry *geomA,
const sfcgal::geometry *geomB, QString *errorMsg )
859 int res = geomgeom_to_primtype<int>( sfcgal_geometry_covers, sfcgal_geometry_covers_3d, geomA, geomB, errorMsg );
860 CHECK_SUCCESS( errorMsg,
false );
861 return static_cast<bool>( res );
864sfcgal::shared_geom QgsSfcgalEngine::envelope(
const sfcgal::geometry *geom, QString *errorMsg )
866#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
869 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"envelope" ) );
871 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_envelope, sfcgal_geometry_envelope_3d, geom, errorMsg );
872 CHECK_SUCCESS( errorMsg,
nullptr );
877sfcgal::shared_geom QgsSfcgalEngine::convexHull(
const sfcgal::geometry *geom, QString *errorMsg )
879 sfcgal::shared_geom out = geom_to_geom( sfcgal_geometry_convexhull, sfcgal_geometry_convexhull_3d, geom, errorMsg );
880 CHECK_SUCCESS( errorMsg,
nullptr );
884sfcgal::shared_geom QgsSfcgalEngine::offsetCurve(
const sfcgal::geometry *geom,
double distance,
int,
Qgis::JoinStyle, QString *errorMsg )
886 sfcgal::errorHandler()->clearText( errorMsg );
887 CHECK_NOT_NULL( geom,
nullptr );
889 sfcgal::geometry *result =
nullptr;
890 result = sfcgal_geometry_offset_polygon( geom, distance );
892 CHECK_SUCCESS( errorMsg,
nullptr );
894 return sfcgal::make_shared_geom( result );
897sfcgal::shared_geom QgsSfcgalEngine::buffer2D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle joinStyle, QString *errorMsg )
900 qWarning() << ( u
"Buffer not implemented for %1! Defaulting to round join."_s );
902 return offsetCurve( geom, radius, segments, joinStyle, errorMsg );
905sfcgal::shared_geom QgsSfcgalEngine::buffer3D(
const sfcgal::geometry *geom,
double radius,
int segments,
Qgis::JoinStyle3D joinStyle3D, QString *errorMsg )
907 sfcgal::errorHandler()->clearText( errorMsg );
908 CHECK_NOT_NULL( geom,
nullptr );
910 sfcgal_buffer3d_type_t buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
911 switch ( joinStyle3D )
914 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_FLAT;
917 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_ROUND;
920 buffer_type = sfcgal_buffer3d_type_t::SFCGAL_BUFFER3D_CYLSPHERE;
924 sfcgal::geometry *result = sfcgal_geometry_buffer3d( geom, radius, segments, buffer_type );
925 CHECK_SUCCESS( errorMsg,
nullptr );
927 return sfcgal::make_shared_geom( result );
930sfcgal::shared_geom QgsSfcgalEngine::extrude(
const sfcgal::geometry *geom,
const QgsVector3D &extrusion, QString *errorMsg )
932 sfcgal::errorHandler()->clearText( errorMsg );
933 CHECK_NOT_NULL( geom,
nullptr );
935 sfcgal_geometry_t *solid = sfcgal_geometry_extrude( geom, extrusion.
x(), extrusion.
y(), extrusion.
z() );
937 CHECK_SUCCESS( errorMsg,
nullptr );
942 sfcgal_geometry_t *polySurface = sfcgal_polyhedral_surface_create();
943 for (
unsigned int shellIdx = 0; shellIdx < sfcgal_solid_num_shells( solid ); ++shellIdx )
945 const sfcgal_geometry_t *shell = sfcgal_solid_shell_n( solid, shellIdx );
946#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 1, 0 )
947 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_patches( shell ); ++polyIdx )
949 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_patch_n( shell, polyIdx );
950 sfcgal_polyhedral_surface_add_patch( polySurface, sfcgal_geometry_clone( patch ) );
953 for (
unsigned int polyIdx = 0; polyIdx < sfcgal_polyhedral_surface_num_polygons( shell ); ++polyIdx )
955 const sfcgal_geometry_t *patch = sfcgal_polyhedral_surface_polygon_n( shell, polyIdx );
956 sfcgal_polyhedral_surface_add_polygon( polySurface, sfcgal_geometry_clone( patch ) );
961 sfcgal_geometry_delete( solid );
963 CHECK_SUCCESS( errorMsg,
nullptr );
965 return sfcgal::make_shared_geom( polySurface );
968sfcgal::shared_geom QgsSfcgalEngine::simplify(
const sfcgal::geometry *geom,
double tolerance,
bool preserveTopology, QString *errorMsg )
970#if SFCGAL_VERSION_NUM < SFCGAL_MAKE_VERSION( 2, 1, 0 )
973 ( void )preserveTopology;
975 throw QgsNotSupportedException( QObject::tr(
"Calculating %1 requires a QGIS build based on SFCGAL 2.1 or later" ).arg(
"boundary" ) );
977 sfcgal::errorHandler()->clearText( errorMsg );
978 CHECK_NOT_NULL( geom,
nullptr );
980 sfcgal::geometry *result = sfcgal_geometry_simplify( geom, tolerance, preserveTopology );
981 CHECK_SUCCESS( errorMsg,
nullptr );
983 return sfcgal::make_shared_geom( result );
987sfcgal::shared_geom QgsSfcgalEngine::approximateMedialAxis(
const sfcgal::geometry *geom, QString *errorMsg )
989 sfcgal::errorHandler()->clearText( errorMsg );
990 CHECK_NOT_NULL( geom,
nullptr );
992 sfcgal::geometry *result = sfcgal_geometry_approximate_medial_axis( geom );
993 CHECK_SUCCESS( errorMsg,
nullptr );
995 return sfcgal::make_shared_geom( result );
999#if SFCGAL_VERSION_NUM >= SFCGAL_MAKE_VERSION( 2, 3, 0 )
1000sfcgal::shared_geom QgsSfcgalEngine::transform(
const sfcgal::geometry *geom,
const QMatrix4x4 &mat, QString *errorMsg )
1002 sfcgal::errorHandler()->clearText( errorMsg );
1003 CHECK_NOT_NULL( geom,
nullptr );
1005 sfcgal::geometry *result;
1006 result = sfcgal_geometry_transform( geom, mat.constData() );
1008 CHECK_SUCCESS( errorMsg,
nullptr );
1009 return sfcgal::make_shared_geom( result );
1012std::unique_ptr<QgsSfcgalGeometry> QgsSfcgalEngine::toSfcgalGeometry( sfcgal::shared_prim &prim, sfcgal::primitiveType type, QString *errorMsg )
1014 sfcgal::errorHandler()->clearText( errorMsg );
1015 CHECK_NOT_NULL( prim.get(),
nullptr );
1017 return std::make_unique<QgsSfcgalGeometry>( prim, type );
1020sfcgal::shared_prim QgsSfcgalEngine::createCube(
double size, QString *errorMsg )
1022 sfcgal::primitive *result = sfcgal_primitive_create( SFCGAL_TYPE_CUBE );
1023 CHECK_SUCCESS( errorMsg,
nullptr );
1025 sfcgal_primitive_set_parameter_double( result,
"size", size );
1026 CHECK_SUCCESS( errorMsg,
nullptr );
1028 return sfcgal::make_shared_prim( result );
1031sfcgal::shared_geom QgsSfcgalEngine::primitiveAsPolyhedral(
const sfcgal::primitive *prim,
const QMatrix4x4 &mat, QString *errorMsg )
1033 sfcgal::errorHandler()->clearText( errorMsg );
1034 CHECK_NOT_NULL( prim,
nullptr );
1036 sfcgal::geometry *result = sfcgal_primitive_as_polyhedral_surface( prim );
1037 CHECK_SUCCESS( errorMsg,
nullptr );
1039 if ( !mat.isIdentity() )
1041 sfcgal::geometry *result2 = sfcgal_geometry_transform( result, mat.constData() );
1042 sfcgal_geometry_delete( result );
1044 CHECK_SUCCESS( errorMsg,
nullptr );
1047 return sfcgal::make_shared_geom( result );
1050bool QgsSfcgalEngine::primitiveIsEqual(
const sfcgal::primitive *primA,
const sfcgal::primitive *primB,
double tolerance, QString *errorMsg )
1052 sfcgal::errorHandler()->clearText( errorMsg );
1053 CHECK_NOT_NULL( primA,
false );
1054 CHECK_NOT_NULL( primB,
false );
1056 bool result = sfcgal_primitive_is_almost_equals( primA, primB, tolerance );
1057 CHECK_SUCCESS( errorMsg,
false );
1062sfcgal::shared_prim QgsSfcgalEngine::primitiveClone(
const sfcgal::primitive *prim, QString *errorMsg )
1064 sfcgal::errorHandler()->clearText( errorMsg );
1065 CHECK_NOT_NULL( prim,
nullptr );
1067 sfcgal::primitive *result = sfcgal_primitive_clone( prim );
1069 CHECK_SUCCESS( errorMsg,
nullptr );
1070 CHECK_NOT_NULL( result,
nullptr );
1072 return sfcgal::make_shared_prim( result );
1075double QgsSfcgalEngine::primitiveArea(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1077 sfcgal::errorHandler()->clearText( errorMsg );
1078 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1080 double out = sfcgal_primitive_area( prim, withDiscretization );
1081 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1085double QgsSfcgalEngine::primitiveVolume(
const sfcgal::primitive *prim,
bool withDiscretization, QString *errorMsg )
1087 sfcgal::errorHandler()->clearText( errorMsg );
1088 CHECK_NOT_NULL( prim, std::numeric_limits<double>::quiet_NaN() );
1090 double out = sfcgal_primitive_volume( prim, withDiscretization );
1091 CHECK_SUCCESS( errorMsg, std::numeric_limits<double>::quiet_NaN() );
1095void sfcgal::to_json( json &j,
const sfcgal::PrimitiveParameterDesc &p )
1100 if ( std::holds_alternative<int>( p.value ) )
1102 j[
"value"] = std::get<int>( p.value );
1104 else if ( std::holds_alternative<double>( p.value ) )
1106 j[
"value"] = std::get<double>( p.value );
1108 else if ( std::holds_alternative<QgsPoint>( p.value ) )
1110 QgsPoint point = std::get<QgsPoint>( p.value );
1111 double z = std::numeric_limits<double>::quiet_NaN();
1112 double m = std::numeric_limits<double>::quiet_NaN();
1117 j[
"value"] = std::vector<double> { point.
x(), point.
y(), z, m };
1119 else if ( std::holds_alternative<QgsVector3D>( p.value ) )
1121 QgsVector3D vect = std::get<QgsVector3D>( p.value );
1122 j[
"value"] = std::vector<double> { vect.
x(), vect.
y(), vect.
z() };
1125 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1128void sfcgal::from_json(
const json &j, sfcgal::PrimitiveParameterDesc &p )
1130 j.at(
"name" ).get_to( p.name );
1131 j.at(
"type" ).get_to( p.type );
1132 if ( j.contains(
"value" ) )
1134 json value = j.at(
"value" );
1135 if ( p.type ==
"int" )
1137 p.value = value.get<
int>();
1139 else if ( p.type ==
"double" )
1141 p.value = value.get<
double>();
1143 else if ( p.type ==
"point3" )
1145 std::vector<double> vect;
1146 vect = value.get<std::vector<double>>();
1148 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1149 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() ) );
1152 else if ( p.type ==
"vector3" )
1154 std::vector<double> vect;
1155 vect = value.get<std::vector<double>>();
1157 ( vect.size() > 2 ? vect[2] : std::numeric_limits<double>::quiet_NaN() ),
1158 ( vect.size() > 3 ? vect[3] : std::numeric_limits<double>::quiet_NaN() ) );
1162 throw json::type_error::create( 306, u
"Unknown type '%1'."_s.arg( p.type.c_str() ).toStdString(),
nullptr );
1166QVector<sfcgal::PrimitiveParameterDesc> QgsSfcgalEngine::primitiveParameters(
const sfcgal::primitive *prim, QString *errorMsg )
1168 sfcgal::errorHandler()->clearText( errorMsg );
1169 CHECK_NOT_NULL( prim, QVector<sfcgal::PrimitiveParameterDesc>() );
1171 char *jsonChars =
nullptr;
1173 sfcgal_primitive_parameters( prim, &jsonChars, &len );
1174 CHECK_SUCCESS( errorMsg, QVector<sfcgal::PrimitiveParameterDesc>() );
1176 std::string jsonString( jsonChars, len );
1177 sfcgal_free_buffer( jsonChars );
1179 QVector<sfcgal::PrimitiveParameterDesc> result;
1182 const auto jParams = json::parse( jsonString );
1183 for (
const auto &jParam : jParams )
1185 result.append( jParam.get<sfcgal::PrimitiveParameterDesc>() );
1188 catch ( json::exception &e )
1190 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1196QVariant QgsSfcgalEngine::primitiveParameter(
const sfcgal::primitive *prim,
const QString &name, QString *errorMsg )
1198 sfcgal::errorHandler()->clearText( errorMsg );
1199 CHECK_NOT_NULL( prim, QVariant() );
1201 char *jsonChars =
nullptr;
1203 sfcgal_primitive_parameter( prim, name.toStdString().c_str(), &jsonChars, &len );
1204 CHECK_SUCCESS( errorMsg, QVariant() );
1206 std::string jsonString( jsonChars, len );
1207 sfcgal_free_buffer( jsonChars );
1212 const auto jParam = json::parse( jsonString );
1213 sfcgal::PrimitiveParameterDesc param = jParam.get<sfcgal::PrimitiveParameterDesc>();
1214 result = QVariant::fromStdVariant( param.value );
1216 catch ( json::exception &e )
1218 sfcgal::errorHandler()->addText( u
"Caught json exception for json: %1. Error: %2"_s.arg( jsonString.c_str() ).arg( e.what() ) );
1224void QgsSfcgalEngine::primitiveSetParameter( sfcgal::primitive *prim,
const QString &name,
const QVariant &value, QString *errorMsg )
1226 sfcgal::errorHandler()->clearText( errorMsg );
1227 CHECK_NOT_NULL( prim,
void() );
1232 sfcgal::PrimitiveParameterDesc paramDesc;
1233 paramDesc.name = name.toStdString();
1234 paramDesc.type = value.typeName();
1235 if ( paramDesc.type ==
"int" )
1236 paramDesc.value = value.toInt();
1237 else if ( paramDesc.type ==
"double" )
1238 paramDesc.value = value.toDouble();
1239 else if ( value.canConvert<
QgsPoint>() )
1240 paramDesc.value = value.value<
QgsPoint>();
1244 sfcgal::to_json( jParam, paramDesc );
1245 std::string jsonStr = jParam.dump();
1246 sfcgal_primitive_set_parameter( prim, name.toStdString().c_str(), jsonStr.c_str() );
1247 CHECK_SUCCESS( errorMsg,
void() );
1251 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.
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.