Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b605a09
feat(core): add QgsNurbsCurve class for NURBS curve support
lbartoletti Dec 17, 2025
ae14d77
test(core): add C++ unit tests for QgsNurbsCurve
lbartoletti Dec 17, 2025
266210c
test(core): add Python tests for QgsNurbsCurve
lbartoletti Dec 17, 2025
0afba63
feat(gui): add Poly-Bézier data structures for NURBS digitizing
lbartoletti Dec 17, 2025
ce976c3
feat(gui): add NURBS support to map tool capture rubberband
lbartoletti Dec 17, 2025
2cb4e73
feat(gui): implement NURBS digitizing in MapToolCapture
lbartoletti Dec 17, 2025
02f37d1
feat(gui): add NurbsCurve support in dialogs and digitize tools
lbartoletti Dec 17, 2025
f76751f
test(gui): add QgsBezierData unit tests
lbartoletti Dec 17, 2025
2499a75
feat(app): add NURBS digitizing technique manager
lbartoletti Dec 17, 2025
9e0903d
feat(app): add NURBS support to editing map tools
lbartoletti Dec 17, 2025
8e49823
feat(app): add NURBS support to vertex tool
lbartoletti Dec 17, 2025
b35a7d2
test(app): add QgsMapToolNurbs unit tests
lbartoletti Dec 17, 2025
e9317dc
test(app): add NURBS tests to vertex editor
lbartoletti Dec 17, 2025
2ca0e85
feat(postgres): allow NurbsCurve creation
lbartoletti Dec 17, 2025
505f374
feat(gui): add NURBS digitizing icon
lbartoletti Dec 17, 2025
daa8207
refactor(gui): centralize NURBS control polygon style with settings
lbartoletti Dec 18, 2025
9bb6b48
fix(nurbs): add todo for oracle provider
lbartoletti Dec 19, 2025
7ff2f4e
feat(core): add ControlPoint snapping support
lbartoletti Dec 24, 2025
fc9c3ac
fixup! feat(core): add QgsNurbsCurve class for NURBS curve support
lbartoletti Dec 24, 2025
47921b1
fixup! feat(gui): add Poly-Bézier data structures for NURBS digitizing
lbartoletti Dec 24, 2025
537963f
fixup! feat(gui): add NURBS support to map tool capture rubberband
lbartoletti Dec 24, 2025
5c6c5ff
fixup! feat(core): add QgsNurbsCurve class for NURBS curve support
lbartoletti Dec 24, 2025
2706ba9
feat(geometry): add interpolatePointOnCubicBezier
lbartoletti Dec 24, 2025
380eb72
refactor(qgslinestring): use QgsGeometryUtils::interpolatePointOnCubi…
lbartoletti Dec 24, 2025
a6d5a9b
fixup! feat(app): add NURBS support to vertex tool
lbartoletti Dec 24, 2025
e82b90c
fixup! feat(gui): implement NURBS digitizing in MapToolCapture
lbartoletti Dec 24, 2025
0514ced
refactor(qgsbezierdata): use QgsGeometryUtils::interpolatePointOnCubi…
lbartoletti Dec 24, 2025
a36c7d6
fixup! test(app): add NURBS tests to vertex editor
lbartoletti Dec 24, 2025
cf41604
fixup! test(core): add C++ unit tests for QgsNurbsCurve
lbartoletti Dec 24, 2025
df30c0e
fixup! test(core): add Python tests for QgsNurbsCurve
lbartoletti Dec 24, 2025
527ed22
fix(snapindicator): use ICON_BOX for control point
lbartoletti Dec 24, 2025
10fa0af
fix(vertextool): improve polybézier moving anchor
lbartoletti Dec 24, 2025
7b72dc3
tests(vertextool): add alt+drag moving anchor
lbartoletti Dec 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions images/images.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
<file>themes/default/mActionDeselectActiveLayer.svg</file>
<file>themes/default/mActionDigitizeShape.svg</file>
<file>themes/default/mActionDigitizeWithCurve.svg</file>
<file>themes/default/mActionDigitizeWithNURBS.svg</file>
<file>themes/default/mActionDigitizeWithSegment.svg</file>
<file>themes/default/mActionDuplicateLayer.svg</file>
<file>themes/default/mActionDuplicateComposer.svg</file>
Expand Down Expand Up @@ -744,6 +745,7 @@
<file>themes/default/mIconSnappingAllLayers.svg</file>
<file>themes/default/mIconSnappingArea.svg</file>
<file>themes/default/mIconSnappingCentroid.svg</file>
<file>themes/default/mIconSnappingControlPoint.svg</file>
<file>themes/default/mIconSnappingMiddle.svg</file>
<file>themes/default/mIconSnappingOnScale.svg</file>
<file>themes/default/mIconSnappingVertex.svg</file>
Expand Down
82 changes: 82 additions & 0 deletions images/themes/default/mActionDigitizeWithNURBS.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions images/themes/default/mIconSnappingControlPoint.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions python/PyQt6/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,9 @@
QgsWkbTypes.TIN = Qgis.WkbType.TIN
QgsWkbTypes.TIN.is_monkey_patched = True
QgsWkbTypes.TIN.__doc__ = "TIN \n.. versionadded:: 3.40"
QgsWkbTypes.NurbsCurve = Qgis.WkbType.NurbsCurve
QgsWkbTypes.NurbsCurve.is_monkey_patched = True
QgsWkbTypes.NurbsCurve.__doc__ = "NurbsCurve \n.. versionadded:: 4.0"
QgsWkbTypes.NoGeometry = Qgis.WkbType.NoGeometry
QgsWkbTypes.NoGeometry.is_monkey_patched = True
QgsWkbTypes.NoGeometry.__doc__ = "No geometry"
Expand Down Expand Up @@ -373,6 +376,9 @@
QgsWkbTypes.TINZ = Qgis.WkbType.TINZ
QgsWkbTypes.TINZ.is_monkey_patched = True
QgsWkbTypes.TINZ.__doc__ = "TINZ"
QgsWkbTypes.NurbsCurveZ = Qgis.WkbType.NurbsCurveZ
QgsWkbTypes.NurbsCurveZ.is_monkey_patched = True
QgsWkbTypes.NurbsCurveZ.__doc__ = "NurbsCurveZ \n.. versionadded:: 4.0"
QgsWkbTypes.PointM = Qgis.WkbType.PointM
QgsWkbTypes.PointM.is_monkey_patched = True
QgsWkbTypes.PointM.__doc__ = "PointM"
Expand Down Expand Up @@ -418,6 +424,9 @@
QgsWkbTypes.TINM = Qgis.WkbType.TINM
QgsWkbTypes.TINM.is_monkey_patched = True
QgsWkbTypes.TINM.__doc__ = "TINM"
QgsWkbTypes.NurbsCurveM = Qgis.WkbType.NurbsCurveM
QgsWkbTypes.NurbsCurveM.is_monkey_patched = True
QgsWkbTypes.NurbsCurveM.__doc__ = "NurbsCurveM \n.. versionadded:: 4.0"
QgsWkbTypes.PointZM = Qgis.WkbType.PointZM
QgsWkbTypes.PointZM.is_monkey_patched = True
QgsWkbTypes.PointZM.__doc__ = "PointZM"
Expand Down Expand Up @@ -463,6 +472,9 @@
QgsWkbTypes.TriangleZM = Qgis.WkbType.TriangleZM
QgsWkbTypes.TriangleZM.is_monkey_patched = True
QgsWkbTypes.TriangleZM.__doc__ = "TriangleZM"
QgsWkbTypes.NurbsCurveZM = Qgis.WkbType.NurbsCurveZM
QgsWkbTypes.NurbsCurveZM.is_monkey_patched = True
QgsWkbTypes.NurbsCurveZM.__doc__ = "NurbsCurveZM \n.. versionadded:: 4.0"
QgsWkbTypes.Point25D = Qgis.WkbType.Point25D
QgsWkbTypes.Point25D.is_monkey_patched = True
QgsWkbTypes.Point25D.__doc__ = "Point25D"
Expand Down Expand Up @@ -531,6 +543,10 @@

.. versionadded:: 3.40

* ``NurbsCurve``: NurbsCurve

.. versionadded:: 4.0

* ``NoGeometry``: No geometry
* ``PointZ``: PointZ
* ``LineStringZ``: LineStringZ
Expand All @@ -547,6 +563,10 @@
* ``MultiSurfaceZ``: MultiSurfaceZ
* ``PolyhedralSurfaceZ``: PolyhedralSurfaceZ
* ``TINZ``: TINZ
* ``NurbsCurveZ``: NurbsCurveZ

.. versionadded:: 4.0

* ``PointM``: PointM
* ``LineStringM``: LineStringM
* ``PolygonM``: PolygonM
Expand All @@ -562,6 +582,10 @@
* ``MultiSurfaceM``: MultiSurfaceM
* ``PolyhedralSurfaceM``: PolyhedralSurfaceM
* ``TINM``: TINM
* ``NurbsCurveM``: NurbsCurveM

.. versionadded:: 4.0

* ``PointZM``: PointZM
* ``LineStringZM``: LineStringZM
* ``PolygonZM``: PolygonZM
Expand All @@ -577,6 +601,10 @@
* ``PolyhedralSurfaceZM``: PolyhedralSurfaceM
* ``TINZM``: TINZM
* ``TriangleZM``: TriangleZM
* ``NurbsCurveZM``: NurbsCurveZM

.. versionadded:: 4.0

* ``Point25D``: Point25D
* ``LineString25D``: LineString25D
* ``Polygon25D``: Polygon25D
Expand Down Expand Up @@ -715,6 +743,7 @@
Qgis.CaptureTechnique.CircularString.__doc__ = "Capture in circular strings"
Qgis.CaptureTechnique.Streaming.__doc__ = "Streaming points digitizing mode (points are automatically added as the mouse cursor moves)."
Qgis.CaptureTechnique.Shape.__doc__ = "Digitize shapes."
Qgis.CaptureTechnique.NurbsCurve.__doc__ = "Digitizes NURBS curves with control points. \n.. versionadded:: 4.0"
Qgis.CaptureTechnique.__doc__ = """Capture technique.

.. versionadded:: 3.26
Expand All @@ -723,11 +752,28 @@
* ``CircularString``: Capture in circular strings
* ``Streaming``: Streaming points digitizing mode (points are automatically added as the mouse cursor moves).
* ``Shape``: Digitize shapes.
* ``NurbsCurve``: Digitizes NURBS curves with control points.

.. versionadded:: 4.0


"""
# --
Qgis.CaptureTechnique.baseClass = Qgis
# monkey patching scoped based enum
Qgis.NurbsMode.ControlPoints.__doc__ = "Direct control points mode - the curve is attracted to control points but does not pass through them"
Qgis.NurbsMode.PolyBezier.__doc__ = "Poly-Bézier mode (vector graphics style) - anchors with tangent handles, the curve passes through anchor points"
Qgis.NurbsMode.__doc__ = """NURBS digitizing mode.

.. versionadded:: 4.0

* ``ControlPoints``: Direct control points mode - the curve is attracted to control points but does not pass through them
* ``PolyBezier``: Poly-Bézier mode (vector graphics style) - anchors with tangent handles, the curve passes through anchor points

"""
# --
Qgis.NurbsMode.baseClass = Qgis
# monkey patching scoped based enum
Qgis.VectorLayerTypeFlag.SqlQuery.__doc__ = "SQL query layer"
Qgis.VectorLayerTypeFlag.__doc__ = """Vector layer type flags.

Expand Down Expand Up @@ -1390,6 +1436,9 @@
QgsSnappingConfig.SnappingTypes.LineEndpointFlag = Qgis.SnappingType.LineEndpoint
QgsSnappingConfig.LineEndpointFlag.is_monkey_patched = True
QgsSnappingConfig.LineEndpointFlag.__doc__ = "Start or end points of lines, or first vertex in polygon rings only \n.. versionadded:: 3.20"
QgsSnappingConfig.ControlPoint = Qgis.SnappingType.ControlPoint
QgsSnappingConfig.ControlPoint.is_monkey_patched = True
QgsSnappingConfig.ControlPoint.__doc__ = "On control points (for NURBS curves) \n.. versionadded:: 4.0"
Qgis.SnappingType.__doc__ = """SnappingTypeFlag defines on what object the snapping is performed

.. versionadded:: 3.26
Expand Down Expand Up @@ -1425,6 +1474,10 @@

Available as ``QgsSnappingConfig.LineEndpointFlag`` in older QGIS releases.

* ``ControlPoint``: On control points (for NURBS curves)

.. versionadded:: 4.0


"""
# --
Expand Down Expand Up @@ -5672,6 +5725,10 @@
QgsVertexId.VertexType.CurveVertex = Qgis.VertexType.Curve
QgsVertexId.CurveVertex.is_monkey_patched = True
QgsVertexId.CurveVertex.__doc__ = "An intermediate point on a segment defining the curvature of the segment"
QgsVertexId.ControlPointVertex = Qgis.VertexType.ControlPoint
QgsVertexId.VertexType.ControlPointVertex = Qgis.VertexType.ControlPoint
QgsVertexId.ControlPointVertex.is_monkey_patched = True
QgsVertexId.ControlPointVertex.__doc__ = "A NURBS control point (does not lie on the curve) \n.. versionadded:: 4.0"
Qgis.VertexType.__doc__ = """Types of vertex.

.. versionadded:: 3.22
Expand All @@ -5684,6 +5741,13 @@

Available as ``QgsVertexId.CurveVertex`` in older QGIS releases.

* ``ControlPoint``: A NURBS control point (does not lie on the curve)

.. versionadded:: 4.0


Available as ``QgsVertexId.ControlPointVertex`` in older QGIS releases.


"""
# --
Expand Down
1 change: 1 addition & 0 deletions python/PyQt6/core/auto_additions/qgsgeometryutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
QgsGeometryUtils.projectPointOnSegment = staticmethod(QgsGeometryUtils.projectPointOnSegment)
QgsGeometryUtils.leftOfLine = staticmethod(QgsGeometryUtils.leftOfLine)
QgsGeometryUtils.interpolatePointOnArc = staticmethod(QgsGeometryUtils.interpolatePointOnArc)
QgsGeometryUtils.interpolatePointOnCubicBezier = staticmethod(QgsGeometryUtils.interpolatePointOnCubicBezier)
QgsGeometryUtils.segmentMidPoint = staticmethod(QgsGeometryUtils.segmentMidPoint)
QgsGeometryUtils.segmentMidPointFromCenter = staticmethod(QgsGeometryUtils.segmentMidPointFromCenter)
QgsGeometryUtils.circleTangentDirection = staticmethod(QgsGeometryUtils.circleTangentDirection)
Expand Down
6 changes: 6 additions & 0 deletions python/PyQt6/core/auto_additions/qgsnurbscurve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following has been generated automatically from src/core/geometry/qgsnurbscurve.h
try:
QgsNurbsCurve.__overridden_methods__ = ['clone', 'isClosed', 'isClosed2D', 'curveToLine', 'draw', 'drawAsPolygon', 'endPoint', 'equals', 'indexOf', 'interpolatePoint', 'numPoints', 'pointAt', 'points', 'reversed', 'scroll', 'startPoint', 'sumUpArea', 'xAt', 'yAt', 'zAt', 'mAt', 'asQPolygonF', 'addToPainterPath', 'curveSubstring', 'length', 'segmentLength', 'distanceBetweenVertices', 'snappedToGrid', 'simplifyByDistance', 'removeDuplicateNodes', 'vertexAngle', 'swapXy', 'transform', 'createEmptyWithSameType', 'closestSegment', 'boundingBox', 'boundingBox3D', 'moveVertex', 'insertVertex', 'wkbSize', 'asWkb', 'asWkt', 'asGml2', 'asGml3', 'asKml', 'dimension', 'isEmpty', 'clear', 'boundingBoxIntersects', 'centroid', 'addZValue', 'addMValue', 'dropZValue', 'dropMValue', 'deleteVertex', 'fromWkb', 'fromWkt', 'fuzzyEqual', 'fuzzyDistanceEqual', 'geometryType', 'hasCurvedSegments', 'partCount', 'toCurveType', 'vertexAt', 'vertexCount', 'vertexNumberFromVertexId', 'isValid', 'clearCache', 'compareToSameClass', 'calculateBoundingBox3D']
QgsNurbsCurve.__group__ = ['geometry']
except (NameError, AttributeError):
pass
7 changes: 7 additions & 0 deletions python/PyQt6/core/auto_additions/qgsnurbsutils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The following has been generated automatically from src/core/geometry/qgsnurbsutils.h
try:
QgsNurbsUtils.containsNurbsCurve = staticmethod(QgsNurbsUtils.containsNurbsCurve)
QgsNurbsUtils.findMutableNurbsCurveForVertex = staticmethod(QgsNurbsUtils.findMutableNurbsCurveForVertex)
QgsNurbsUtils.__group__ = ['geometry']
except (NameError, AttributeError):
pass
1 change: 1 addition & 0 deletions python/PyQt6/core/auto_additions/qgspointlocator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
QgsPointLocator.Centroid = QgsPointLocator.Type.Centroid
QgsPointLocator.MiddleOfSegment = QgsPointLocator.Type.MiddleOfSegment
QgsPointLocator.LineEndpoint = QgsPointLocator.Type.LineEndpoint
QgsPointLocator.ControlPoint = QgsPointLocator.Type.ControlPoint
QgsPointLocator.All = QgsPointLocator.Type.All
QgsPointLocator.Types = lambda flags=0: QgsPointLocator.Type(flags)
try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Abstract base class for all geometries.
sipType = sipType_QgsCircularString;
else if ( qgsgeometry_cast<QgsCompoundCurve *>( sipCpp ) != nullptr )
sipType = sipType_QgsCompoundCurve;
else if ( qgsgeometry_cast<QgsNurbsCurve *>( sipCpp ) != nullptr )
sipType = sipType_QgsNurbsCurve;
else if ( qgsgeometry_cast<QgsTriangle *>( sipCpp ) != nullptr )
sipType = sipType_QgsTriangle;
else if ( qgsgeometry_cast<QgsPolygon *>( sipCpp ) != nullptr )
Expand Down
Loading
Loading