Compare commits

...

25 Commits

Author SHA1 Message Date
Loïc Bartoletti
707188e078
Merge c08ce4c652e110d957e8374929f27d7fd40b468f into 2e07d9829f38ea12661050415d09e28675c961bd 2025-10-01 16:25:21 +00:00
Nyall Dawson
2e07d9829f Fix build 2025-10-01 18:00:57 +10:00
Nyall Dawson
7d31cc1dab Run code_cleanup script 2025-10-01 18:00:57 +10:00
qgis-bot
e28d12d058 auto sipify 🍺 2025-10-01 05:54:45 +00:00
Denis Rouzaud
0f1c0de0f8
Merge pull request #63214 from nyalldawson/annotation3d
Support rendering annotation layer markers as 3d billboards
2025-10-01 07:52:11 +02:00
qgis-bot
82d7ab9b62 auto sipify 🍺 2025-10-01 03:49:17 +00:00
Nyall Dawson
8bc1f639bb Update tests 2025-10-01 13:46:41 +10:00
Nyall Dawson
928bdd8cc4 Fix tooltip formatting for console actions
The tooltip text should be in bold, not the shortcut
2025-10-01 13:46:41 +10:00
Nyall Dawson
1db5d1d246 Add common actions for run script, run selection
Allows these shortcuts to be customised
2025-10-01 13:46:41 +10:00
Nyall Dawson
1e3167082c Use correct shortcut in context menu for console script editor
And add reformat code action to menu
2025-10-01 13:46:41 +10:00
Nyall Dawson
c6accd8517 Fix more misleading names
These objects are actions, not buttons
2025-10-01 13:46:41 +10:00
Nyall Dawson
41962a0a24 Use correct user-set shortcuts for console script editor code actions
And rename member variable for correctness
2025-10-01 13:46:41 +10:00
Nyall Dawson
0fc8b37487 Set shortcut for script editor toggle comment action 2025-10-01 13:46:41 +10:00
Nyall Dawson
1c53ab415b Use registered shortcuts for code editor comment/reformat actions 2025-10-01 13:46:41 +10:00
Nyall Dawson
579a818ce7 Add sequenceForCommonAction 2025-10-01 13:46:41 +10:00
Nyall Dawson
5ed5f51121 [api] Add framework to handle common actions in shortcuts manager
Common actions allow for shortcuts to be registered for actions
which do not yet exist, or which are not associated with a single
global QAction object attached to at the application level. For
example, code editor actions which will be created as children
of individual code editor widgets, but which should have
shortcuts available for user configuration via the shortcuts
manager dialog.
2025-10-01 13:46:41 +10:00
Nyall Dawson
62fbbe6392 Refactor to extract utility function 2025-10-01 13:46:41 +10:00
Nyall Dawson
ac1260d159 Handle shortcut manager sections which don't have trailing "/" 2025-10-01 13:46:41 +10:00
Nyall Dawson
d9a4c80aa9 Rename method to more generic name 2025-10-01 13:46:41 +10:00
Nyall Dawson
af0208c974
Refine z range logic 2025-10-01 12:47:45 +10:00
Nyall Dawson
a01770e5cb
[feature] Support rendering annotation layer markers as 3d billboards
This commit adds a new option for showing annotation layers in 3d maps.

From the annotation layer properties, 3d tab, users can set the layer
to render as 3d billboards. When activated, all MARKERS from the layer
will be shown as floating billboard symbols above the 3d map. (lines,
polygons, and text items from the layer are ignored).

Users have control over the terrain clamping and offset for the
billboards, and whether or not "callout" lines should be shown (vertical
lines joining the billboard to the corresponding location on the
terrain surface)
2025-10-01 10:56:50 +10:00
Loïc Bartoletti
c08ce4c652 refactor(processing): add QgsAlgorithmTransectBase used for fixed distance and 'normal' one 2025-09-30 16:45:15 +02:00
Loïc Bartoletti
31a20c0cad test(transect): Add a test for transect with fixed distance 2025-09-30 16:45:15 +02:00
Loïc Bartoletti
37c5b4d6c0 feat(processing): add separate transect fixed distance algorithm
Split transect functionality into two algorithms per PR feedback - keep
original transect algorithm for vertices, add dedicated "Transect (fixed
distance)" algorithm for regular interval sampling along lines.
2025-09-30 09:14:51 +02:00
Loïc Bartoletti
02b0ee02e9 feat(Transect): Add option to generate a transect at a fixed distance (ignore existing vertices) 2025-09-30 09:14:51 +02:00
122 changed files with 3968 additions and 670 deletions

View File

@ -6,6 +6,7 @@
%Include auto_generated/qgs3dtypes.sip %Include auto_generated/qgs3dtypes.sip
%Include auto_generated/qgs3dmapcanvas.sip %Include auto_generated/qgs3dmapcanvas.sip
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip %Include auto_generated/qgsabstractvectorlayer3drenderer.sip
%Include auto_generated/qgsannotationlayer3drenderer.sip
%Include auto_generated/qgscameracontroller.sip %Include auto_generated/qgscameracontroller.sip
%Include auto_generated/qgscamerapose.sip %Include auto_generated/qgscamerapose.sip
%Include auto_generated/qgslayoutitem3dmap.sip %Include auto_generated/qgslayoutitem3dmap.sip

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
try:
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
except (NameError, AttributeError):
pass
try:
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,154 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
// this is needed for the "convert to subclass" code below to compile
%ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
{
%Docstring(signature="appended")
Metadata for annotation layer 3D renderer to allow creation of its
instances from XML.
.. warning::
This is not considered stable API, and may change in future QGIS releases. It is
exposed to the Python bindings as a tech preview only.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
public:
QgsAnnotationLayer3DRendererMetadata();
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates an instance of a 3D renderer based on a DOM element with
renderer configuration
%End
};
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
{
%Docstring(signature="appended")
3D renderers for annotation layers.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
%ConvertToSubClassCode
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
%End
public:
QgsAnnotationLayer3DRenderer();
void setLayer( QgsAnnotationLayer *layer );
%Docstring
Sets the annotation layer associated with the renderer.
.. seealso:: :py:func:`layer`
%End
QgsAnnotationLayer *layer() const;
%Docstring
Returns the annotation layer associated with the renderer.
.. seealso:: :py:func:`setLayer`
%End
virtual QString type() const;
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
virtual void resolveReferences( const QgsProject &project );
Qgis::AltitudeClamping altitudeClamping() const;
%Docstring
Returns the altitude clamping method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`setAltitudeClamping`
%End
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
%Docstring
Sets the altitude ``clamping`` method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`altitudeClamping`
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be
added to z values for the annotations.
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be
added to z values for the annotations.
.. seealso:: :py:func:`zOffset`
%End
bool showCalloutLines() const;
%Docstring
Returns ``True`` if callout lines are shown, vertically joining the
annotations to the terrain.
.. seealso:: :py:func:`setShowCalloutLines`
%End
void setShowCalloutLines( bool show );
%Docstring
Sets whether callout lines are shown, vertically joining the annotations
to the terrain.
.. seealso:: :py:func:`showCalloutLines`
%End
private:
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89 QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97 QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76 QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360 QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89 QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368 QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368

View File

@ -6,6 +6,7 @@
%Include auto_generated/qgs3dtypes.sip %Include auto_generated/qgs3dtypes.sip
%Include auto_generated/qgs3dmapcanvas.sip %Include auto_generated/qgs3dmapcanvas.sip
%Include auto_generated/qgsabstractvectorlayer3drenderer.sip %Include auto_generated/qgsabstractvectorlayer3drenderer.sip
%Include auto_generated/qgsannotationlayer3drenderer.sip
%Include auto_generated/qgscameracontroller.sip %Include auto_generated/qgscameracontroller.sip
%Include auto_generated/qgscamerapose.sip %Include auto_generated/qgscamerapose.sip
%Include auto_generated/qgslayoutitem3dmap.sip %Include auto_generated/qgslayoutitem3dmap.sip

View File

@ -0,0 +1,9 @@
# The following has been generated automatically from src/3d/qgsannotationlayer3drenderer.h
try:
QgsAnnotationLayer3DRendererMetadata.__overridden_methods__ = ['createRenderer']
except (NameError, AttributeError):
pass
try:
QgsAnnotationLayer3DRenderer.__overridden_methods__ = ['type', 'clone', 'writeXml', 'readXml', 'resolveReferences']
except (NameError, AttributeError):
pass

View File

@ -0,0 +1,154 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/
// this is needed for the "convert to subclass" code below to compile
%ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
class QgsAnnotationLayer3DRendererMetadata : Qgs3DRendererAbstractMetadata
{
%Docstring(signature="appended")
Metadata for annotation layer 3D renderer to allow creation of its
instances from XML.
.. warning::
This is not considered stable API, and may change in future QGIS releases. It is
exposed to the Python bindings as a tech preview only.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
public:
QgsAnnotationLayer3DRendererMetadata();
virtual QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates an instance of a 3D renderer based on a DOM element with
renderer configuration
%End
};
class QgsAnnotationLayer3DRenderer : QgsAbstract3DRenderer
{
%Docstring(signature="appended")
3D renderers for annotation layers.
.. versionadded:: 4.0
%End
%TypeHeaderCode
#include "qgsannotationlayer3drenderer.h"
%End
%ConvertToSubClassCode
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
%End
public:
QgsAnnotationLayer3DRenderer();
void setLayer( QgsAnnotationLayer *layer );
%Docstring
Sets the annotation layer associated with the renderer.
.. seealso:: :py:func:`layer`
%End
QgsAnnotationLayer *layer() const;
%Docstring
Returns the annotation layer associated with the renderer.
.. seealso:: :py:func:`setLayer`
%End
virtual QString type() const;
virtual QgsAnnotationLayer3DRenderer *clone() const /Factory/;
virtual void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const;
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
virtual void resolveReferences( const QgsProject &project );
Qgis::AltitudeClamping altitudeClamping() const;
%Docstring
Returns the altitude clamping method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`setAltitudeClamping`
%End
void setAltitudeClamping( Qgis::AltitudeClamping clamping );
%Docstring
Sets the altitude ``clamping`` method, which determines the vertical
position of annotations.
.. seealso:: :py:func:`altitudeClamping`
%End
double zOffset() const;
%Docstring
Returns the z offset, which is a fixed offset amount which should be
added to z values for the annotations.
.. seealso:: :py:func:`setZOffset`
%End
void setZOffset( double offset );
%Docstring
Sets the z ``offset``, which is a fixed offset amount which will be
added to z values for the annotations.
.. seealso:: :py:func:`zOffset`
%End
bool showCalloutLines() const;
%Docstring
Returns ``True`` if callout lines are shown, vertically joining the
annotations to the terrain.
.. seealso:: :py:func:`setShowCalloutLines`
%End
void setShowCalloutLines( bool show );
%Docstring
Sets whether callout lines are shown, vertically joining the annotations
to the terrain.
.. seealso:: :py:func:`showCalloutLines`
%End
private:
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/3d/qgsannotationlayer3drenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.py again *
************************************************************************/

View File

@ -246,6 +246,22 @@ QgsAbstractVectorLayer3DRenderer.setTilingSettings: src/3d/qgsabstractvectorlaye
QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89 QgsAbstractVectorLayer3DRenderer.tilingSettings: src/3d/qgsabstractvectorlayer3drenderer.h#L89
QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97 QgsAbstractVectorLayer3DRenderer.writeXmlBaseProperties: src/3d/qgsabstractvectorlayer3drenderer.h#L97
QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76 QgsAbstractVectorLayer3DRenderer: src/3d/qgsabstractvectorlayer3drenderer.h#L76
QgsAnnotationLayer3DRenderer.altitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L100
QgsAnnotationLayer3DRenderer.clone: src/3d/qgsannotationlayer3drenderer.h#L88
QgsAnnotationLayer3DRenderer.layer: src/3d/qgsannotationlayer3drenderer.h#L85
QgsAnnotationLayer3DRenderer.readXml: src/3d/qgsannotationlayer3drenderer.h#L92
QgsAnnotationLayer3DRenderer.resolveReferences: src/3d/qgsannotationlayer3drenderer.h#L93
QgsAnnotationLayer3DRenderer.setAltitudeClamping: src/3d/qgsannotationlayer3drenderer.h#L107
QgsAnnotationLayer3DRenderer.setLayer: src/3d/qgsannotationlayer3drenderer.h#L78
QgsAnnotationLayer3DRenderer.setShowCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L135
QgsAnnotationLayer3DRenderer.setZOffset: src/3d/qgsannotationlayer3drenderer.h#L121
QgsAnnotationLayer3DRenderer.showCalloutLines: src/3d/qgsannotationlayer3drenderer.h#L128
QgsAnnotationLayer3DRenderer.type: src/3d/qgsannotationlayer3drenderer.h#L87
QgsAnnotationLayer3DRenderer.writeXml: src/3d/qgsannotationlayer3drenderer.h#L91
QgsAnnotationLayer3DRenderer.zOffset: src/3d/qgsannotationlayer3drenderer.h#L114
QgsAnnotationLayer3DRenderer: src/3d/qgsannotationlayer3drenderer.h#L59
QgsAnnotationLayer3DRendererMetadata.createRenderer: src/3d/qgsannotationlayer3drenderer.h#L50
QgsAnnotationLayer3DRendererMetadata: src/3d/qgsannotationlayer3drenderer.h#L44
QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360 QgsCameraController.cameraChanged: src/3d/qgscameracontroller.h#L360
QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89 QgsCameraController.cameraMovementSpeed: src/3d/qgscameracontroller.h#L89
QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368 QgsCameraController.cameraMovementSpeedChanged: src/3d/qgscameracontroller.h#L368

View File

@ -0,0 +1,18 @@
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
# monkey patching scoped based enum
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
.. versionadded:: 4.0
* ``CodeToggleComment``: Toggle code comments
* ``CodeReformat``: Reformat code
* ``CodeRunScript``: Run script
* ``CodeRunSelection``: Run selection from script
"""
# --
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager

View File

@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
#include "qgsshortcutsmanager.h" #include "qgsshortcutsmanager.h"
%End %End
public: public:
enum class CommonAction
{
CodeToggleComment,
CodeReformat,
CodeRunScript,
CodeRunSelection,
};
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" ); QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
%Docstring %Docstring
Constructor for QgsShortcutsManager. Constructor for QgsShortcutsManager.
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
QGIS actions. QGIS actions.
%End %End
~QgsShortcutsManager();
void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() ); void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() );
%Docstring %Docstring
Automatically registers all QActions and QShortcuts which are children Automatically registers all QActions and QShortcuts which are children
@ -105,6 +115,16 @@ in GUI.
.. seealso:: :py:func:`unregisterAction` .. seealso:: :py:func:`unregisterAction`
.. seealso:: :py:func:`registerAllChildActions` .. seealso:: :py:func:`registerAllChildActions`
%End
void initializeCommonAction( QAction *action, CommonAction commonAction );
%Docstring
Initializes an ``action`` as a common action.
This automatically configures the ``action`` to use the properties for
the common action, such as setting the action's tooltip and shortcut.
.. versionadded:: 4.0
%End %End
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() ); bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() );
@ -290,6 +310,14 @@ if no shortcut is associated.
.. seealso:: :py:func:`objectForSequence` .. seealso:: :py:func:`objectForSequence`
.. seealso:: :py:func:`actionForSequence` .. seealso:: :py:func:`actionForSequence`
%End
QKeySequence sequenceForCommonAction( CommonAction action ) const;
%Docstring
Returns the key sequence which is associated with a common ``action``,
or an empty sequence if no shortcut is assigned to that action.
.. versionadded:: 4.0
%End %End
QAction *actionByName( const QString &name ) const; QAction *actionByName( const QString &name ) const;

View File

@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426 QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427 QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407 QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236 QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221 QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163 QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171 QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155 QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213 QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262 QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254 QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94 QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70 QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81 QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59 QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106 QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180 QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196 QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243 QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229 QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116 QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126 QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36 QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264 QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271 QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271

View File

@ -19,5 +19,4 @@ __author__ = "Salvatore Larosa"
__date__ = "September 2012" __date__ = "September 2012"
__copyright__ = "(C) 2012, Salvatore Larosa" __copyright__ = "(C) 2012, Salvatore Larosa"
from .console import show_console # NOQA from .console import show_console, init_console # NOQA
from .console import init_options_widget

View File

@ -61,6 +61,7 @@ from qgis.gui import (
QgsGui, QgsGui,
QgsApplicationExitBlockerInterface, QgsApplicationExitBlockerInterface,
QgsCodeEditorDockWidget, QgsCodeEditorDockWidget,
QgsShortcutsManager,
) )
from functools import partial from functools import partial
@ -106,8 +107,11 @@ def console_displayhook(obj):
_console_output = obj _console_output = obj
def init_options_widget(): def init_console():
"""called from QGIS to add the console options widget""" """
Called from QGIS to initialize the console related options and shortcuts,
before the dock is shown
"""
global _options_factory global _options_factory
_options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python")) _options_factory.setTitle(QCoreApplication.translate("PythonConsole", "Python"))
iface.registerOptionsWidgetFactory(_options_factory) iface.registerOptionsWidgetFactory(_options_factory)
@ -221,169 +225,137 @@ class PythonConsoleWidget(QWidget):
# Action for Open File # Action for Open File
openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…") openFileBt = QCoreApplication.translate("PythonConsole", "Open Script…")
self.openFileButton = QAction(self) self.open_file_action = QAction(self)
self.openFileButton.setCheckable(False) self.open_file_action.setIcon(
self.openFileButton.setEnabled(True)
self.openFileButton.setIcon(
QgsApplication.getThemeIcon("mActionScriptOpen.svg") QgsApplication.getThemeIcon("mActionScriptOpen.svg")
) )
self.openFileButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.open_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openFileButton.setIconVisibleInMenu(True) self.open_file_action.setIconVisibleInMenu(True)
self.openFileButton.setToolTip(openFileBt + " <b>Ctrl+O</b>") self.open_file_action.setToolTip(f"<b>{openFileBt}</b> (Ctrl+O)")
self.openFileButton.setText(openFileBt) self.open_file_action.setText(openFileBt)
openExtEditorBt = QCoreApplication.translate( openExtEditorBt = QCoreApplication.translate(
"PythonConsole", "Open in External Editor" "PythonConsole", "Open in External Editor"
) )
self.openInEditorButton = QAction(self) self.open_in_editor_action = QAction(self)
self.openInEditorButton.setCheckable(False) self.open_in_editor_action.setIcon(
self.openInEditorButton.setEnabled(True)
self.openInEditorButton.setIcon(
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg") QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
) )
self.openInEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.open_in_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.openInEditorButton.setIconVisibleInMenu(True) self.open_in_editor_action.setIconVisibleInMenu(True)
self.openInEditorButton.setToolTip(openExtEditorBt) self.open_in_editor_action.setToolTip(openExtEditorBt)
self.openInEditorButton.setText(openExtEditorBt) self.open_in_editor_action.setText(openExtEditorBt)
# Action for Save File # Action for Save File
saveFileBt = QCoreApplication.translate("PythonConsole", "Save") saveFileBt = QCoreApplication.translate("PythonConsole", "Save")
self.saveFileButton = QAction(self) self.save_file_action = QAction(self)
self.saveFileButton.setCheckable(False) self.save_file_action.setEnabled(False)
self.saveFileButton.setEnabled(False) self.save_file_action.setIcon(
self.saveFileButton.setIcon(QgsApplication.getThemeIcon("mActionFileSave.svg")) QgsApplication.getThemeIcon("mActionFileSave.svg")
self.saveFileButton.setMenuRole(QAction.MenuRole.PreferencesRole) )
self.saveFileButton.setIconVisibleInMenu(True) self.save_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.saveFileButton.setToolTip(saveFileBt + " <b>Ctrl+S</b>") self.save_file_action.setIconVisibleInMenu(True)
self.saveFileButton.setText(saveFileBt) self.save_file_action.setToolTip(f"<b>{saveFileBt}</b> (Ctrl+S)")
self.save_file_action.setText(saveFileBt)
# Action for Save File As # Action for Save File As
saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…") saveAsFileBt = QCoreApplication.translate("PythonConsole", "Save As…")
self.saveAsFileButton = QAction(self) self.save_as_file_action = QAction(self)
self.saveAsFileButton.setCheckable(False) self.save_as_file_action.setIcon(
self.saveAsFileButton.setEnabled(True)
self.saveAsFileButton.setIcon(
QgsApplication.getThemeIcon("mActionFileSaveAs.svg") QgsApplication.getThemeIcon("mActionFileSaveAs.svg")
) )
self.saveAsFileButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.save_as_file_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.saveAsFileButton.setIconVisibleInMenu(True) self.save_as_file_action.setIconVisibleInMenu(True)
self.saveAsFileButton.setToolTip(saveAsFileBt + " <b>Ctrl+Shift+S</b>") self.save_as_file_action.setToolTip(f"<b>{saveAsFileBt}</b> (Ctrl+Shift+S)")
self.saveAsFileButton.setText(saveAsFileBt) self.save_as_file_action.setText(saveAsFileBt)
# Action Cut # Action Cut
cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut") cutEditorBt = QCoreApplication.translate("PythonConsole", "Cut")
self.cutEditorButton = QAction(self) self.cut_action = QAction(self)
self.cutEditorButton.setCheckable(False) self.cut_action.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg"))
self.cutEditorButton.setEnabled(True) self.cut_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.cutEditorButton.setIcon(QgsApplication.getThemeIcon("mActionEditCut.svg")) self.cut_action.setIconVisibleInMenu(True)
self.cutEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.cut_action.setToolTip(f"<b>{cutEditorBt}</b> (Ctrl+X)")
self.cutEditorButton.setIconVisibleInMenu(True) self.cut_action.setText(cutEditorBt)
self.cutEditorButton.setToolTip(cutEditorBt + " <b>Ctrl+X</b>")
self.cutEditorButton.setText(cutEditorBt)
# Action Copy # Action Copy
copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy") copyEditorBt = QCoreApplication.translate("PythonConsole", "Copy")
self.copyEditorButton = QAction(self) self.copy_action = QAction(self)
self.copyEditorButton.setCheckable(False) self.copy_action.setIcon(QgsApplication.getThemeIcon("mActionEditCopy.svg"))
self.copyEditorButton.setEnabled(True) self.copy_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.copyEditorButton.setIcon( self.copy_action.setIconVisibleInMenu(True)
QgsApplication.getThemeIcon("mActionEditCopy.svg") self.copy_action.setToolTip(f"<b>{copyEditorBt}</b> (Ctrl+C)")
) self.copy_action.setText(copyEditorBt)
self.copyEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.copyEditorButton.setIconVisibleInMenu(True)
self.copyEditorButton.setToolTip(copyEditorBt + " <b>Ctrl+C</b>")
self.copyEditorButton.setText(copyEditorBt)
# Action Paste # Action Paste
pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste") pasteEditorBt = QCoreApplication.translate("PythonConsole", "Paste")
self.pasteEditorButton = QAction(self) self.paste_action = QAction(self)
self.pasteEditorButton.setCheckable(False) self.paste_action.setIcon(QgsApplication.getThemeIcon("mActionEditPaste.svg"))
self.pasteEditorButton.setEnabled(True) self.paste_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.pasteEditorButton.setIcon( self.paste_action.setIconVisibleInMenu(True)
QgsApplication.getThemeIcon("mActionEditPaste.svg") self.paste_action.setToolTip(f"<b>{pasteEditorBt}</b> (Ctrl+V)")
) self.paste_action.setText(pasteEditorBt)
self.pasteEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.pasteEditorButton.setIconVisibleInMenu(True)
self.pasteEditorButton.setToolTip(pasteEditorBt + " <b>Ctrl+V</b>")
self.pasteEditorButton.setText(pasteEditorBt)
# Action Run Script (subprocess) # Action Run Script (subprocess)
runScriptEditorBt = QCoreApplication.translate("PythonConsole", "Run Script") self.run_script_action = QAction(self)
self.runScriptEditorButton = QAction(self) self.run_script_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
self.runScriptEditorButton.setCheckable(False) self.run_script_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runScriptEditorButton.setEnabled(True) self.run_script_action.setIconVisibleInMenu(True)
self.runScriptEditorButton.setIcon( QgsGui.shortcutsManager().initializeCommonAction(
QgsApplication.getThemeIcon("mActionStart.svg") self.run_script_action, QgsShortcutsManager.CommonAction.CodeRunScript
) )
self.runScriptEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runScriptEditorButton.setIconVisibleInMenu(True)
self.runScriptEditorButton.setToolTip(
runScriptEditorBt + " <b>Ctrl+Shift+E</b>"
)
self.runScriptEditorButton.setText(runScriptEditorBt)
# Action Run Selected # Action Run Selected
runSelectedEditorBt = QCoreApplication.translate( self.run_selection_action = QAction(self)
"PythonConsole", "Run Selected" self.run_selection_action.setIcon(
)
self.runSelectedEditorButton = QAction(self)
self.runSelectedEditorButton.setCheckable(False)
self.runSelectedEditorButton.setEnabled(True)
self.runSelectedEditorButton.setIcon(
QgsApplication.getThemeIcon("mActionRunSelected.svg") QgsApplication.getThemeIcon("mActionRunSelected.svg")
) )
self.runSelectedEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.run_selection_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runSelectedEditorButton.setIconVisibleInMenu(True) self.run_selection_action.setIconVisibleInMenu(True)
self.runSelectedEditorButton.setToolTip(runSelectedEditorBt + " <b>Ctrl+E</b>") QgsGui.shortcutsManager().initializeCommonAction(
self.runSelectedEditorButton.setShortcut("Ctrl+E") self.run_selection_action, QgsShortcutsManager.CommonAction.CodeRunSelection
self.runSelectedEditorButton.setText(runSelectedEditorBt) )
# Action Toggle comment # Action Toggle comment
toggleText = QCoreApplication.translate("PythonConsole", "Toggle Comment") self.toggle_comment_action = QAction(self)
self.toggleCommentEditorButton = QAction(self) self.toggle_comment_action.setIcon(
self.toggleCommentEditorButton.setCheckable(False)
self.toggleCommentEditorButton.setEnabled(True)
self.toggleCommentEditorButton.setIcon(
QgsApplication.getThemeIcon( QgsApplication.getThemeIcon(
"console/iconCommentEditorConsole.svg", "console/iconCommentEditorConsole.svg",
self.palette().color(QPalette.ColorRole.WindowText), self.palette().color(QPalette.ColorRole.WindowText),
), ),
) )
self.toggleCommentEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.toggle_comment_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.toggleCommentEditorButton.setIconVisibleInMenu(True) self.toggle_comment_action.setIconVisibleInMenu(True)
self.toggleCommentEditorButton.setToolTip(toggleText + " <b>Ctrl+:</b>") QgsGui.shortcutsManager().initializeCommonAction(
self.toggleCommentEditorButton.setText(toggleText) self.toggle_comment_action,
QgsShortcutsManager.CommonAction.CodeToggleComment,
)
# Action Format code # Action Format code
reformatCodeText = QCoreApplication.translate("PythonConsole", "Reformat Code") self.reformat_code_action = QAction(self)
self.reformatCodeEditorButton = QAction(self) self.reformat_code_action.setIcon(
self.reformatCodeEditorButton.setCheckable(False)
self.reformatCodeEditorButton.setEnabled(True)
self.reformatCodeEditorButton.setIcon(
QgsApplication.getThemeIcon("console/iconFormatCode.svg") QgsApplication.getThemeIcon("console/iconFormatCode.svg")
) )
self.reformatCodeEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.reformat_code_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.reformatCodeEditorButton.setIconVisibleInMenu(True) self.reformat_code_action.setIconVisibleInMenu(True)
self.reformatCodeEditorButton.setToolTip( QgsGui.shortcutsManager().initializeCommonAction(
reformatCodeText + " <b>Ctrl+Alt+F</b>" self.reformat_code_action, QgsShortcutsManager.CommonAction.CodeReformat
) )
self.reformatCodeEditorButton.setShortcut("Ctrl+Alt+F")
self.reformatCodeEditorButton.setText(reformatCodeText)
# Action for Object browser # Action for Object browser
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…") objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
self.objectListButton = QAction(self) self.object_inspector_action = QAction(self)
self.objectListButton.setCheckable(True) self.object_inspector_action.setCheckable(True)
self.objectListButton.setEnabled( self.object_inspector_action.setEnabled(
QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool) QgsSettings().value("pythonConsole/enableObjectInsp", False, type=bool)
) )
self.objectListButton.setIcon( self.object_inspector_action.setIcon(
QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg") QgsApplication.getThemeIcon("console/iconClassBrowserConsole.svg")
) )
self.objectListButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.object_inspector_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.objectListButton.setIconVisibleInMenu(True) self.object_inspector_action.setIconVisibleInMenu(True)
self.objectListButton.setToolTip(objList) self.object_inspector_action.setToolTip(objList)
self.objectListButton.setText(objList) self.object_inspector_action.setText(objList)
# Action for Find text # Action for Find text
findText = QCoreApplication.translate("PythonConsole", "Find Text") findText = QCoreApplication.translate("PythonConsole", "Find Text")
@ -395,7 +367,7 @@ class PythonConsoleWidget(QWidget):
) )
self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole) self.find_text_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.find_text_action.setIconVisibleInMenu(True) self.find_text_action.setIconVisibleInMenu(True)
self.find_text_action.setToolTip(findText + " <b>Ctrl+F</b>") self.find_text_action.setToolTip(f"<b>{findText}</b> (Ctrl+F)")
self.find_text_action.setText(findText) self.find_text_action.setText(findText)
self.tabEditorWidget.search_bar_toggled.connect( self.tabEditorWidget.search_bar_toggled.connect(
@ -407,75 +379,65 @@ class PythonConsoleWidget(QWidget):
# Action Show Editor # Action Show Editor
showEditor = QCoreApplication.translate("PythonConsole", "Show Editor") showEditor = QCoreApplication.translate("PythonConsole", "Show Editor")
self.showEditorButton = QAction(self) self.show_editor_action = QAction(self)
self.showEditorButton.setEnabled(True) self.show_editor_action.setCheckable(True)
self.showEditorButton.setCheckable(True) self.show_editor_action.setIcon(
self.showEditorButton.setIcon(
QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg") QgsApplication.getThemeIcon("console/iconShowEditorConsole.svg")
) )
self.showEditorButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.show_editor_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.showEditorButton.setIconVisibleInMenu(True) self.show_editor_action.setIconVisibleInMenu(True)
self.showEditorButton.setToolTip(showEditor) self.show_editor_action.setToolTip(showEditor)
self.showEditorButton.setText(showEditor) self.show_editor_action.setText(showEditor)
# Action for Clear button # Action for Clear button
clearBt = QCoreApplication.translate("PythonConsole", "Clear Console") clearBt = QCoreApplication.translate("PythonConsole", "Clear Console")
self.clearButton = QAction(self) self.clear_action = QAction(self)
self.clearButton.setCheckable(False) self.clear_action.setIcon(
self.clearButton.setEnabled(True)
self.clearButton.setIcon(
QgsApplication.getThemeIcon("console/iconClearConsole.svg") QgsApplication.getThemeIcon("console/iconClearConsole.svg")
) )
self.clearButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.clear_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.clearButton.setIconVisibleInMenu(True) self.clear_action.setIconVisibleInMenu(True)
self.clearButton.setToolTip(clearBt) self.clear_action.setToolTip(clearBt)
self.clearButton.setText(clearBt) self.clear_action.setText(clearBt)
# Action for settings # Action for settings
optionsBt = QCoreApplication.translate("PythonConsole", "Options…") optionsBt = QCoreApplication.translate("PythonConsole", "Options…")
self.optionsButton = QAction(self) self.options_action = QAction(self)
self.optionsButton.setCheckable(False) self.options_action.setIcon(
self.optionsButton.setEnabled(True)
self.optionsButton.setIcon(
QgsApplication.getThemeIcon("console/iconSettingsConsole.svg") QgsApplication.getThemeIcon("console/iconSettingsConsole.svg")
) )
self.optionsButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.options_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.optionsButton.setIconVisibleInMenu(True) self.options_action.setIconVisibleInMenu(True)
self.optionsButton.setToolTip(optionsBt) self.options_action.setToolTip(optionsBt)
self.optionsButton.setText(optionsBt) self.options_action.setText(optionsBt)
# Action for Run script # Action for Run script
runBt = QCoreApplication.translate("PythonConsole", "Run Command") runBt = QCoreApplication.translate("PythonConsole", "Run Command")
self.runButton = QAction(self) self.run_action = QAction(self)
self.runButton.setCheckable(False) self.run_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
self.runButton.setEnabled(True) self.run_action.setMenuRole(QAction.MenuRole.PreferencesRole)
self.runButton.setIcon(QgsApplication.getThemeIcon("mActionStart.svg")) self.run_action.setIconVisibleInMenu(True)
self.runButton.setMenuRole(QAction.MenuRole.PreferencesRole) self.run_action.setToolTip(runBt)
self.runButton.setIconVisibleInMenu(True) self.run_action.setText(runBt)
self.runButton.setToolTip(runBt)
self.runButton.setText(runBt)
# Help button # Help button
self.helpConsoleAction = QAction(self) self.help_console_action = QAction(self)
self.helpConsoleAction.setEnabled(True) self.help_console_action.setText(
self.helpConsoleAction.setText(
QCoreApplication.translate("PythonConsole", "Python Console Help") QCoreApplication.translate("PythonConsole", "Python Console Help")
) )
self.helpAPIAction = QAction(self) self.help_api_action = QAction(self)
self.helpAPIAction.setEnabled(True) self.help_api_action.setText(
self.helpAPIAction.setText(
QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation") QCoreApplication.translate("PythonConsole", "PyQGIS API Documentation")
) )
self.helpCookbookAction = QAction(self) self.help_cookbook_action = QAction(self)
self.helpCookbookAction.setEnabled(True) self.help_cookbook_action.setText(
self.helpCookbookAction.setText(
QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook") QCoreApplication.translate("PythonConsole", "PyQGIS Cookbook")
) )
self.helpMenu = QMenu(self) self.helpMenu = QMenu(self)
self.helpMenu.addAction(self.helpConsoleAction) self.helpMenu.addAction(self.help_console_action)
self.helpMenu.addAction(self.helpAPIAction) self.helpMenu.addAction(self.help_api_action)
self.helpMenu.addAction(self.helpCookbookAction) self.helpMenu.addAction(self.help_cookbook_action)
helpBt = QCoreApplication.translate("PythonConsole", "Help…") helpBt = QCoreApplication.translate("PythonConsole", "Help…")
self.helpButton = QToolButton(self) self.helpButton = QToolButton(self)
@ -495,12 +457,12 @@ class PythonConsoleWidget(QWidget):
self.toolBar.setIconSize(icon_size) self.toolBar.setIconSize(icon_size)
self.toolBar.setMovable(False) self.toolBar.setMovable(False)
self.toolBar.setFloatable(False) self.toolBar.setFloatable(False)
self.toolBar.addAction(self.clearButton) self.toolBar.addAction(self.clear_action)
self.toolBar.addAction(self.runButton) self.toolBar.addAction(self.run_action)
self.toolBar.addSeparator() self.toolBar.addSeparator()
self.toolBar.addAction(self.showEditorButton) self.toolBar.addAction(self.show_editor_action)
self.toolBar.addSeparator() self.toolBar.addSeparator()
self.toolBar.addAction(self.optionsButton) self.toolBar.addAction(self.options_action)
self.toolBar.addWidget(self.helpButton) self.toolBar.addWidget(self.helpButton)
self.toolBar.addSeparator() self.toolBar.addSeparator()
self.toolBar.addWidget(parent.dockToggleButton()) self.toolBar.addWidget(parent.dockToggleButton())
@ -513,25 +475,25 @@ class PythonConsoleWidget(QWidget):
self.toolBarEditor.setIconSize(icon_size) self.toolBarEditor.setIconSize(icon_size)
self.toolBarEditor.setMovable(False) self.toolBarEditor.setMovable(False)
self.toolBarEditor.setFloatable(False) self.toolBarEditor.setFloatable(False)
self.toolBarEditor.addAction(self.openFileButton) self.toolBarEditor.addAction(self.open_file_action)
self.toolBarEditor.addAction(self.openInEditorButton) self.toolBarEditor.addAction(self.open_in_editor_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.saveFileButton) self.toolBarEditor.addAction(self.save_file_action)
self.toolBarEditor.addAction(self.saveAsFileButton) self.toolBarEditor.addAction(self.save_as_file_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.runScriptEditorButton) self.toolBarEditor.addAction(self.run_script_action)
self.toolBarEditor.addAction(self.runSelectedEditorButton) self.toolBarEditor.addAction(self.run_selection_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.cutEditorButton) self.toolBarEditor.addAction(self.cut_action)
self.toolBarEditor.addAction(self.copyEditorButton) self.toolBarEditor.addAction(self.copy_action)
self.toolBarEditor.addAction(self.pasteEditorButton) self.toolBarEditor.addAction(self.paste_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.find_text_action) self.toolBarEditor.addAction(self.find_text_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.toggleCommentEditorButton) self.toolBarEditor.addAction(self.toggle_comment_action)
self.toolBarEditor.addAction(self.reformatCodeEditorButton) self.toolBarEditor.addAction(self.reformat_code_action)
self.toolBarEditor.addSeparator() self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.objectListButton) self.toolBarEditor.addAction(self.object_inspector_action)
self.widgetButton = QWidget() self.widgetButton = QWidget()
sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred) sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
@ -585,25 +547,25 @@ class PythonConsoleWidget(QWidget):
# ------------ Signal ------------------------------- # ------------ Signal -------------------------------
self.objectListButton.toggled.connect(self.toggleObjectListWidget) self.object_inspector_action.toggled.connect(self.toggleObjectListWidget)
self.toggleCommentEditorButton.triggered.connect(self.toggleComment) self.toggle_comment_action.triggered.connect(self.toggleComment)
self.reformatCodeEditorButton.triggered.connect(self.reformatCode) self.reformat_code_action.triggered.connect(self.reformatCode)
self.runScriptEditorButton.triggered.connect(self.runScriptEditor) self.run_script_action.triggered.connect(self.runScriptEditor)
self.runSelectedEditorButton.triggered.connect(self.runSelectedEditor) self.run_selection_action.triggered.connect(self.runSelectedEditor)
self.cutEditorButton.triggered.connect(self.cutEditor) self.cut_action.triggered.connect(self.cutEditor)
self.copyEditorButton.triggered.connect(self.copyEditor) self.copy_action.triggered.connect(self.copyEditor)
self.pasteEditorButton.triggered.connect(self.pasteEditor) self.paste_action.triggered.connect(self.pasteEditor)
self.showEditorButton.toggled.connect(self.toggleEditor) self.show_editor_action.toggled.connect(self.toggleEditor)
self.clearButton.triggered.connect(self.shell_output.clearConsole) self.clear_action.triggered.connect(self.shell_output.clearConsole)
self.optionsButton.triggered.connect(self.openSettings) self.options_action.triggered.connect(self.openSettings)
self.runButton.triggered.connect(self.shell.entered) self.run_action.triggered.connect(self.shell.entered)
self.openFileButton.triggered.connect(self.openScriptFile) self.open_file_action.triggered.connect(self.openScriptFile)
self.openInEditorButton.triggered.connect(self.openScriptFileExtEditor) self.open_in_editor_action.triggered.connect(self.openScriptFileExtEditor)
self.saveFileButton.triggered.connect(self.saveScriptFile) self.save_file_action.triggered.connect(self.saveScriptFile)
self.saveAsFileButton.triggered.connect(self.saveAsScriptFile) self.save_as_file_action.triggered.connect(self.saveAsScriptFile)
self.helpConsoleAction.triggered.connect(self.openHelpConsole) self.help_console_action.triggered.connect(self.openHelpConsole)
self.helpAPIAction.triggered.connect(self.openHelpAPI) self.help_api_action.triggered.connect(self.openHelpAPI)
self.helpCookbookAction.triggered.connect(self.openHelpCookbook) self.help_cookbook_action.triggered.connect(self.openHelpCookbook)
self.listClassMethod.itemClicked.connect(self.onClickGoToLine) self.listClassMethod.itemClicked.connect(self.onClickGoToLine)
if iface is not None: if iface is not None:

View File

@ -33,7 +33,13 @@ from operator import itemgetter
from pathlib import Path from pathlib import Path
from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings from qgis.core import Qgis, QgsApplication, QgsBlockingNetworkRequest, QgsSettings
from qgis.gui import QgsCodeEditorPython, QgsCodeEditorWidget, QgsMessageBar from qgis.gui import (
QgsCodeEditorPython,
QgsCodeEditorWidget,
QgsGui,
QgsMessageBar,
QgsShortcutsManager,
)
from qgis.PyQt.Qsci import QsciScintilla from qgis.PyQt.Qsci import QsciScintilla
from qgis.PyQt.QtCore import ( from qgis.PyQt.QtCore import (
@ -159,15 +165,6 @@ class Editor(QgsCodeEditorPython):
syntaxCheckAction.setShortcut("Ctrl+4") syntaxCheckAction.setShortcut("Ctrl+4")
menu.addAction(syntaxCheckAction) menu.addAction(syntaxCheckAction)
runSelected = QAction(
QgsApplication.getThemeIcon("mActionRunSelected.svg"), # spellok
QCoreApplication.translate("PythonConsole", "Run Selected"),
menu,
)
runSelected.triggered.connect(self.runSelectedCode) # spellok
runSelected.setShortcut("Ctrl+E") # spellok
menu.addAction(runSelected) # spellok
word = self.selectedText() or self.wordAtPoint(e.pos()) word = self.selectedText() or self.wordAtPoint(e.pos())
if word: if word:
context_help_action = QAction( context_help_action = QAction(
@ -185,13 +182,24 @@ class Editor(QgsCodeEditorPython):
context_help_action.setShortcut(QKeySequence.StandardKey.HelpContents) context_help_action.setShortcut(QKeySequence.StandardKey.HelpContents)
menu.addAction(context_help_action) menu.addAction(context_help_action)
start_action = QAction( run_selection_action = QAction(menu)
QgsApplication.getThemeIcon("mActionStart.svg"), run_selection_action.setIcon(
QCoreApplication.translate("PythonConsole", "Run Script"), QgsApplication.getThemeIcon("mActionRunSelected.svg"),
menu,
) )
run_selection_action.triggered.connect(self.runSelectedCode)
QgsGui.shortcutsManager().initializeCommonAction(
run_selection_action,
QgsShortcutsManager.CommonAction.CodeRunSelection,
)
menu.addAction(run_selection_action)
start_action = QAction(self)
start_action.setIcon(QgsApplication.getThemeIcon("mActionStart.svg"))
start_action.triggered.connect(self.runScriptCode) start_action.triggered.connect(self.runScriptCode)
start_action.setShortcut("Ctrl+Shift+E") QgsGui.shortcutsManager().initializeCommonAction(
start_action,
QgsShortcutsManager.CommonAction.CodeRunScript,
)
menu.addAction(start_action) menu.addAction(start_action)
menu.addSeparator() menu.addSeparator()
@ -258,18 +266,31 @@ class Editor(QgsCodeEditorPython):
menu.addAction(selectAllAction) menu.addAction(selectAllAction)
menu.addSeparator() menu.addSeparator()
toggle_comment_action = QAction( toggle_comment_action = QAction(menu)
toggle_comment_action.setIcon(
QgsApplication.getThemeIcon( QgsApplication.getThemeIcon(
"console/iconCommentEditorConsole.svg", "console/iconCommentEditorConsole.svg",
self.palette().color(QPalette.ColorRole.WindowText), self.palette().color(QPalette.ColorRole.WindowText),
), )
QCoreApplication.translate("PythonConsole", "Toggle Comment"),
menu,
) )
toggle_comment_action.triggered.connect(self.toggleComment) toggle_comment_action.triggered.connect(self.toggleComment)
toggle_comment_action.setShortcut("Ctrl+:") QgsGui.shortcutsManager().initializeCommonAction(
toggle_comment_action,
QgsShortcutsManager.CommonAction.CodeToggleComment,
)
menu.addAction(toggle_comment_action) menu.addAction(toggle_comment_action)
reformat_code_action = QAction(menu)
reformat_code_action.setIcon(
QgsApplication.getThemeIcon("console/iconFormatCode.svg")
)
reformat_code_action.triggered.connect(self.reformatCode)
QgsGui.shortcutsManager().initializeCommonAction(
reformat_code_action,
QgsShortcutsManager.CommonAction.CodeReformat,
)
menu.addAction(reformat_code_action)
menu.addSeparator() menu.addSeparator()
gist_menu = QMenu(self) gist_menu = QMenu(self)
gist_menu.setTitle( gist_menu.setTitle(
@ -299,14 +320,14 @@ class Editor(QgsCodeEditorPython):
syntaxCheckAction.setEnabled(False) syntaxCheckAction.setEnabled(False)
pasteAction.setEnabled(False) pasteAction.setEnabled(False)
cutAction.setEnabled(False) cutAction.setEnabled(False)
runSelected.setEnabled(False) # spellok run_selection_action.setEnabled(False)
copyAction.setEnabled(False) copyAction.setEnabled(False)
selectAllAction.setEnabled(False) selectAllAction.setEnabled(False)
undoAction.setEnabled(False) undoAction.setEnabled(False)
redoAction.setEnabled(False) redoAction.setEnabled(False)
showCodeInspection.setEnabled(False) showCodeInspection.setEnabled(False)
if self.hasSelectedText(): if self.hasSelectedText():
runSelected.setEnabled(True) # spellok run_selection_action.setEnabled(True)
copyAction.setEnabled(True) copyAction.setEnabled(True)
cutAction.setEnabled(True) cutAction.setEnabled(True)
if not self.text() == "": if not self.text() == "":
@ -326,17 +347,17 @@ class Editor(QgsCodeEditorPython):
listObj = self.console_widget.listClassMethod listObj = self.console_widget.listClassMethod
if listObj.isVisible(): if listObj.isVisible():
listObj.hide() listObj.hide()
self.console_widget.objectListButton.setChecked(False) self.console_widget.object_inspector_action.setChecked(False)
else: else:
listObj.show() listObj.show()
self.console_widget.objectListButton.setChecked(True) self.console_widget.object_inspector_action.setChecked(True)
def shareOnGist(self, is_public): def shareOnGist(self, is_public):
self.code_editor_widget.shareOnGist(is_public) self.code_editor_widget.shareOnGist(is_public)
def hideEditor(self): def hideEditor(self):
self.console_widget.splitterObj.hide() self.console_widget.splitterObj.hide()
self.console_widget.showEditorButton.setChecked(False) self.console_widget.show_editor_action.setChecked(False)
def createTempFile(self): def createTempFile(self):
name = tempfile.NamedTemporaryFile(delete=False).name name = tempfile.NamedTemporaryFile(delete=False).name
@ -475,7 +496,7 @@ class Editor(QgsCodeEditorPython):
) )
self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath()) self.tab_widget.setTabToolTip(index, self.code_editor_widget.filePath())
self.setModified(False) self.setModified(False)
self.console_widget.saveFileButton.setEnabled(False) self.console_widget.save_file_action.setEnabled(False)
self.console_widget.updateTabListScript( self.console_widget.updateTabListScript(
self.code_editor_widget.filePath(), action="append" self.code_editor_widget.filePath(), action="append"
) )
@ -720,7 +741,7 @@ class EditorTabWidget(QTabWidget):
# New Editor button # New Editor button
self.newTabButton = QToolButton() self.newTabButton = QToolButton()
txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor") txtToolTipNewTab = QCoreApplication.translate("PythonConsole", "New Editor")
self.newTabButton.setToolTip(txtToolTipNewTab + " <b>Ctrl+T</b>") self.newTabButton.setToolTip(f"<b>{txtToolTipNewTab}</b> (Ctrl+T)")
self.newTabButton.setAutoRaise(True) self.newTabButton.setAutoRaise(True)
self.newTabButton.setIcon( self.newTabButton.setIcon(
QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg") QgsApplication.getThemeIcon("console/iconNewTabEditorConsole.svg")
@ -810,7 +831,7 @@ class EditorTabWidget(QTabWidget):
def enableSaveIfModified(self, tab): def enableSaveIfModified(self, tab):
tabWidget = self.widget(tab) tabWidget = self.widget(tab)
if tabWidget: if tabWidget:
self.console_widget.saveFileButton.setEnabled(tabWidget.isModified()) self.console_widget.save_file_action.setEnabled(tabWidget.isModified())
def enableToolBarEditor(self, enable): def enableToolBarEditor(self, enable):
if self.topFrame.isVisible(): if self.topFrame.isVisible():
@ -864,7 +885,7 @@ class EditorTabWidget(QTabWidget):
index = self.indexOf(tab) index = self.indexOf(tab)
s = self.tabText(index) s = self.tabText(index)
self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s)) self.setTabTitle(index, f"*{s}" if modified else re.sub(r"^(\*)", "", s))
self.console_widget.saveFileButton.setEnabled(modified) self.console_widget.save_file_action.setEnabled(modified)
def setTabTitle(self, tab, title): def setTabTitle(self, tab, title):
self.setTabText(tab, title) self.setTabText(tab, title)
@ -1071,10 +1092,9 @@ class EditorTabWidget(QTabWidget):
objInspectorEnabled = QgsSettings().value( objInspectorEnabled = QgsSettings().value(
"pythonConsole/enableObjectInsp", False, type=bool "pythonConsole/enableObjectInsp", False, type=bool
) )
listObj = self.console_widget.objectListButton
if self.console_widget.listClassMethod.isVisible(): if self.console_widget.listClassMethod.isVisible():
listObj.setChecked(objInspectorEnabled) self.console_widget.object_inspector_action.setChecked(objInspectorEnabled)
listObj.setEnabled(objInspectorEnabled) self.console_widget.object_inspector_action.setEnabled(objInspectorEnabled)
if objInspectorEnabled: if objInspectorEnabled:
cW = self.currentWidget() cW = self.currentWidget()
if cW and not self.console_widget.listClassMethod.isVisible(): if cW and not self.console_widget.listClassMethod.isVisible():

View File

@ -348,7 +348,7 @@ class ShellOutputScintilla(QgsCodeEditorPython):
Ed = self.console_widget.splitterObj Ed = self.console_widget.splitterObj
if not Ed.isVisible(): if not Ed.isVisible():
Ed.show() Ed.show()
self.console_widget.showEditorButton.setChecked(True) self.console_widget.show_editor_action.setChecked(True)
self.shell_editor.setFocus() self.shell_editor.setFocus()
def copy(self): def copy(self):

View File

@ -0,0 +1,18 @@
# The following has been generated automatically from src/gui/qgsshortcutsmanager.h
# monkey patching scoped based enum
QgsShortcutsManager.CommonAction.CodeToggleComment.__doc__ = "Toggle code comments"
QgsShortcutsManager.CommonAction.CodeReformat.__doc__ = "Reformat code"
QgsShortcutsManager.CommonAction.CodeRunScript.__doc__ = "Run script"
QgsShortcutsManager.CommonAction.CodeRunSelection.__doc__ = "Run selection from script"
QgsShortcutsManager.CommonAction.__doc__ = """Contains common actions which are used across a variety of classes.
.. versionadded:: 4.0
* ``CodeToggleComment``: Toggle code comments
* ``CodeReformat``: Reformat code
* ``CodeRunScript``: Run script
* ``CodeRunSelection``: Run selection from script
"""
# --
QgsShortcutsManager.CommonAction.baseClass = QgsShortcutsManager

View File

@ -23,6 +23,14 @@ rather accessed through :py:func:`QgsGui.shortcutsManager()`.
#include "qgsshortcutsmanager.h" #include "qgsshortcutsmanager.h"
%End %End
public: public:
enum class CommonAction
{
CodeToggleComment,
CodeReformat,
CodeRunScript,
CodeRunSelection,
};
QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" ); QgsShortcutsManager( QObject *parent /TransferThis/ = 0, const QString &settingsRoot = "/shortcuts/" );
%Docstring %Docstring
Constructor for QgsShortcutsManager. Constructor for QgsShortcutsManager.
@ -36,6 +44,8 @@ Constructor for QgsShortcutsManager.
QGIS actions. QGIS actions.
%End %End
~QgsShortcutsManager();
void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() ); void registerAllChildren( QObject *object, bool recursive = false, const QString &section = QString() );
%Docstring %Docstring
Automatically registers all QActions and QShortcuts which are children Automatically registers all QActions and QShortcuts which are children
@ -105,6 +115,16 @@ in GUI.
.. seealso:: :py:func:`unregisterAction` .. seealso:: :py:func:`unregisterAction`
.. seealso:: :py:func:`registerAllChildActions` .. seealso:: :py:func:`registerAllChildActions`
%End
void initializeCommonAction( QAction *action, CommonAction commonAction );
%Docstring
Initializes an ``action`` as a common action.
This automatically configures the ``action`` to use the properties for
the common action, such as setting the action's tooltip and shortcut.
.. versionadded:: 4.0
%End %End
bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() ); bool registerShortcut( QShortcut *shortcut, const QString &defaultSequence = QString(), const QString &section = QString() );
@ -290,6 +310,14 @@ if no shortcut is associated.
.. seealso:: :py:func:`objectForSequence` .. seealso:: :py:func:`objectForSequence`
.. seealso:: :py:func:`actionForSequence` .. seealso:: :py:func:`actionForSequence`
%End
QKeySequence sequenceForCommonAction( CommonAction action ) const;
%Docstring
Returns the key sequence which is associated with a common ``action``,
or an empty sequence if no shortcut is assigned to that action.
.. versionadded:: 4.0
%End %End
QAction *actionByName( const QString &name ) const; QAction *actionByName( const QString &name ) const;

View File

@ -6977,28 +6977,30 @@ QgsShapeburstFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwid
QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426 QgsShapeburstFillSymbolLayerWidget.setSymbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L426
QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427 QgsShapeburstFillSymbolLayerWidget.symbolLayer: src/gui/symbology/qgssymbollayerwidget.h#L427
QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407 QgsShapeburstFillSymbolLayerWidget: src/gui/symbology/qgssymbollayerwidget.h#L407
QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L236 QgsShortcutsManager.actionByName: src/gui/qgsshortcutsmanager.h#L267
QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L221 QgsShortcutsManager.actionForSequence: src/gui/qgsshortcutsmanager.h#L246
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L163 QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L188
QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L171 QgsShortcutsManager.defaultKeySequence: src/gui/qgsshortcutsmanager.h#L196
QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L155 QgsShortcutsManager.initializeCommonAction: src/gui/qgsshortcutsmanager.h#L119
QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L213 QgsShortcutsManager.objectDefaultKeySequence: src/gui/qgsshortcutsmanager.h#L180
QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L262 QgsShortcutsManager.objectForSequence: src/gui/qgsshortcutsmanager.h#L238
QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L254 QgsShortcutsManager.objectForSettingKey: src/gui/qgsshortcutsmanager.h#L293
QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L94 QgsShortcutsManager.objectSettingKey: src/gui/qgsshortcutsmanager.h#L285
QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L70 QgsShortcutsManager.registerAction: src/gui/qgsshortcutsmanager.h#L109
QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L81 QgsShortcutsManager.registerAllChildActions: src/gui/qgsshortcutsmanager.h#L85
QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L59 QgsShortcutsManager.registerAllChildShortcuts: src/gui/qgsshortcutsmanager.h#L96
QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L106 QgsShortcutsManager.registerAllChildren: src/gui/qgsshortcutsmanager.h#L74
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L180 QgsShortcutsManager.registerShortcut: src/gui/qgsshortcutsmanager.h#L131
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L196 QgsShortcutsManager.sequenceForCommonAction: src/gui/qgsshortcutsmanager.h#L260
QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L204 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L205
QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L188 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L221
QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L246 QgsShortcutsManager.setKeySequence: src/gui/qgsshortcutsmanager.h#L229
QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L243 QgsShortcutsManager.setObjectKeySequence: src/gui/qgsshortcutsmanager.h#L213
QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L229 QgsShortcutsManager.settingsPath: src/gui/qgsshortcutsmanager.h#L277
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L116 QgsShortcutsManager.shortcutByName: src/gui/qgsshortcutsmanager.h#L274
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L126 QgsShortcutsManager.shortcutForSequence: src/gui/qgsshortcutsmanager.h#L254
QgsShortcutsManager.unregisterAction: src/gui/qgsshortcutsmanager.h#L141
QgsShortcutsManager.unregisterShortcut: src/gui/qgsshortcutsmanager.h#L151
QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36 QgsShortcutsManager: src/gui/qgsshortcutsmanager.h#L36
QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264 QgsSimpleFillSymbolLayerWidget.create: src/gui/symbology/qgssymbollayerwidget.h#L264
QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271 QgsSimpleFillSymbolLayerWidget.setColor: src/gui/symbology/qgssymbollayerwidget.h#L271

View File

@ -30,7 +30,7 @@ from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QPalette from qgis.PyQt.QtGui import QPalette
from qgis.PyQt.QtWidgets import QMessageBox, QFileDialog, QVBoxLayout from qgis.PyQt.QtWidgets import QMessageBox, QFileDialog, QVBoxLayout
from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget from qgis.gui import QgsGui, QgsErrorDialog, QgsCodeEditorWidget, QgsShortcutsManager
from qgis.core import ( from qgis.core import (
QgsApplication, QgsApplication,
QgsFileUtils, QgsFileUtils,
@ -122,6 +122,9 @@ class ScriptEditorDialog(BASE, WIDGET):
self.palette().color(QPalette.ColorRole.WindowText), self.palette().color(QPalette.ColorRole.WindowText),
) )
) )
QgsGui.shortcutsManager().initializeCommonAction(
self.actionToggleComment, QgsShortcutsManager.CommonAction.CodeToggleComment
)
# Connect signals and slots # Connect signals and slots
self.actionOpenScript.triggered.connect(self.openScript) self.actionOpenScript.triggered.connect(self.openScript)

View File

@ -0,0 +1,790 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
gml:id="aFeatureCollection"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ transect_fixed_single_both.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml/3.2">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007699.67743807 1334639.79376903</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.0">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007699.39626005 1334640.42219045</gml:lowerCorner><gml:upperCorner>-1007698.51286553 1334640.89082049</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.0"><gml:posList>-1007699.39626005 1334640.89082049 -1007698.51286553 1334640.42219045</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>1</ogr:fid>
<ogr:TR_FID>0</ogr:TR_FID>
<ogr:TR_ID>0</ogr:TR_ID>
<ogr:TR_SEGMENT>1</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.1">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007699.48998606 1334640.24551155</gml:lowerCorner><gml:upperCorner>-1007698.60659153 1334640.71414158</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.1"><gml:posList>-1007699.48998606 1334640.71414158 -1007698.60659153 1334640.24551155</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>1</ogr:fid>
<ogr:TR_FID>0</ogr:TR_FID>
<ogr:TR_ID>1</ogr:TR_ID>
<ogr:TR_SEGMENT>2</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.2">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007699.58371207 1334640.06883264</gml:lowerCorner><gml:upperCorner>-1007698.70031754 1334640.53746267</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.2"><gml:posList>-1007699.58371207 1334640.53746267 -1007698.70031754 1334640.06883264</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>1</ogr:fid>
<ogr:TR_FID>0</ogr:TR_FID>
<ogr:TR_ID>2</ogr:TR_ID>
<ogr:TR_SEGMENT>3</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.3">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007699.67743807 1334639.89215374</gml:lowerCorner><gml:upperCorner>-1007698.79404354 1334640.36078377</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.3"><gml:posList>-1007699.67743807 1334640.36078377 -1007698.79404354 1334639.89215374</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>1</ogr:fid>
<ogr:TR_FID>0</ogr:TR_FID>
<ogr:TR_ID>3</ogr:TR_ID>
<ogr:TR_SEGMENT>4</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.4">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.87625616 1334641.0871433</gml:lowerCorner><gml:upperCorner>-1007697.8764214 1334641.10532212</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.4"><gml:posList>-1007698.87625616 1334641.10532212 -1007697.8764214 1334641.0871433</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>4</ogr:TR_ID>
<ogr:TR_SEGMENT>1</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.5">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.87989192 1334640.88717635</gml:lowerCorner><gml:upperCorner>-1007697.88005717 1334640.90535517</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.5"><gml:posList>-1007698.87989192 1334640.90535517 -1007697.88005717 1334640.88717635</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>5</ogr:TR_ID>
<ogr:TR_SEGMENT>2</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.6">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.88352768 1334640.6872094</gml:lowerCorner><gml:upperCorner>-1007697.88369293 1334640.70538822</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.6"><gml:posList>-1007698.88352768 1334640.70538822 -1007697.88369293 1334640.6872094</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>6</ogr:TR_ID>
<ogr:TR_SEGMENT>3</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.7">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.57444492 1334640.06062766</gml:lowerCorner><gml:upperCorner>-1007698.14727692 1334640.96479993</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.7"><gml:posList>-1007698.57444492 1334640.06062766 -1007698.14727692 1334640.96479993</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>7</ogr:TR_ID>
<ogr:TR_SEGMENT>4</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.8">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.39361047 1334639.97519406</gml:lowerCorner><gml:upperCorner>-1007697.96644246 1334640.87936633</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.8"><gml:posList>-1007698.39361047 1334639.97519406 -1007697.96644246 1334640.87936633</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>8</ogr:TR_ID>
<ogr:TR_SEGMENT>5</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.9">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.21277601 1334639.88976046</gml:lowerCorner><gml:upperCorner>-1007697.78560801 1334640.79393273</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.9"><gml:posList>-1007698.21277601 1334639.88976046 -1007697.78560801 1334640.79393273</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>2</ogr:fid>
<ogr:TR_FID>1</ogr:TR_FID>
<ogr:TR_ID>9</ogr:TR_ID>
<ogr:TR_SEGMENT>6</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.10">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.43018498 1334641.18324138</gml:lowerCorner><gml:upperCorner>-1007696.5063014 1334641.56591506</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.10"><gml:posList>-1007697.43018498 1334641.56591506 -1007696.5063014 1334641.18324138</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>10</ogr:TR_ID>
<ogr:TR_SEGMENT>1</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.11">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.50671971 1334640.99846467</gml:lowerCorner><gml:upperCorner>-1007696.58283614 1334641.38113835</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.11"><gml:posList>-1007697.50671971 1334641.38113835 -1007696.58283614 1334640.99846467</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>11</ogr:TR_ID>
<ogr:TR_SEGMENT>2</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.12">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.58325445 1334640.81368795</gml:lowerCorner><gml:upperCorner>-1007696.65937088 1334641.19636163</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.12"><gml:posList>-1007697.58325445 1334641.19636163 -1007696.65937088 1334640.81368795</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>12</ogr:TR_ID>
<ogr:TR_SEGMENT>3</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.13">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.65978918 1334640.62891124</gml:lowerCorner><gml:upperCorner>-1007696.73590561 1334641.01158492</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.13"><gml:posList>-1007697.65978918 1334641.01158492 -1007696.73590561 1334640.62891124</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>13</ogr:TR_ID>
<ogr:TR_SEGMENT>4</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.14">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.73672317 1334640.44900368</gml:lowerCorner><gml:upperCorner>-1007696.80824647 1334640.82039432</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.14"><gml:posList>-1007697.73672317 1334640.82039432 -1007696.80824647 1334640.44900368</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>14</ogr:TR_ID>
<ogr:TR_SEGMENT>5</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.15">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.81121219 1334640.26596432</gml:lowerCorner><gml:upperCorner>-1007696.88031755 1334640.63125212</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.15"><gml:posList>-1007697.81121219 1334640.63125212 -1007696.88031755 1334640.26596432</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>15</ogr:TR_ID>
<ogr:TR_SEGMENT>6</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.16">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.88853468 1334640.09745328</gml:lowerCorner><gml:upperCorner>-1007696.94386101 1334640.42546496</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.16"><gml:posList>-1007697.88853468 1334640.42546496 -1007696.94386101 1334640.09745328</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>16</ogr:TR_ID>
<ogr:TR_SEGMENT>7</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.17">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.97399382 1334639.99112039</gml:lowerCorner><gml:upperCorner>-1007696.987237 1334640.15332697</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.17"><gml:posList>-1007697.97399382 1334640.15332697 -1007696.987237 1334639.99112039</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>17</ogr:TR_ID>
<ogr:TR_SEGMENT>8</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.18">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.00643513 1334639.79376903</gml:lowerCorner><gml:upperCorner>-1007697.01967831 1334639.9559756</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.18"><gml:posList>-1007698.00643513 1334639.9559756 -1007697.01967831 1334639.79376903</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>3</ogr:fid>
<ogr:TR_FID>2</ogr:TR_FID>
<ogr:TR_ID>18</ogr:TR_ID>
<ogr:TR_SEGMENT>9</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.19">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334641.37803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334641.37803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.19"><gml:posList>-1007697.38013693 1334641.37803237 -1007698.38013693 1334641.37803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>19</ogr:TR_ID>
<ogr:TR_SEGMENT>1</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.20">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334641.57803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334641.57803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.20"><gml:posList>-1007697.38013693 1334641.57803237 -1007698.38013693 1334641.57803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>20</ogr:TR_ID>
<ogr:TR_SEGMENT>2</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.21">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334641.77803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334641.77803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.21"><gml:posList>-1007697.38013693 1334641.77803237 -1007698.38013693 1334641.77803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>21</ogr:TR_ID>
<ogr:TR_SEGMENT>3</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.22">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334641.97803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334641.97803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.22"><gml:posList>-1007697.38013693 1334641.97803237 -1007698.38013693 1334641.97803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>22</ogr:TR_ID>
<ogr:TR_SEGMENT>4</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.23">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334642.17803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334642.17803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.23"><gml:posList>-1007697.38013693 1334642.17803237 -1007698.38013693 1334642.17803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>23</ogr:TR_ID>
<ogr:TR_SEGMENT>5</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.24">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334642.37803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334642.37803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.24"><gml:posList>-1007697.38013693 1334642.37803237 -1007698.38013693 1334642.37803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>24</ogr:TR_ID>
<ogr:TR_SEGMENT>6</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.25">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334642.57803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334642.57803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.25"><gml:posList>-1007697.38013693 1334642.57803237 -1007698.38013693 1334642.57803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>25</ogr:TR_ID>
<ogr:TR_SEGMENT>7</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.26">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334642.77803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334642.77803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.26"><gml:posList>-1007697.38013693 1334642.77803237 -1007698.38013693 1334642.77803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>26</ogr:TR_ID>
<ogr:TR_SEGMENT>8</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.27">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334642.97803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334642.97803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.27"><gml:posList>-1007697.38013693 1334642.97803237 -1007698.38013693 1334642.97803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>27</ogr:TR_ID>
<ogr:TR_SEGMENT>9</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.28">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334643.17803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334643.17803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.28"><gml:posList>-1007697.38013693 1334643.17803237 -1007698.38013693 1334643.17803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>28</ogr:TR_ID>
<ogr:TR_SEGMENT>10</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.29">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38013693 1334643.37803237</gml:lowerCorner><gml:upperCorner>-1007697.38013693 1334643.37803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.29"><gml:posList>-1007697.38013693 1334643.37803237 -1007698.38013693 1334643.37803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>29</ogr:TR_ID>
<ogr:TR_SEGMENT>11</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.30">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.68013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007697.68013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.30"><gml:posList>-1007697.68013693 1334642.87803237 -1007697.68013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>30</ogr:TR_ID>
<ogr:TR_SEGMENT>12</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.31">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.48013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007697.48013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.31"><gml:posList>-1007697.48013693 1334642.87803237 -1007697.48013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>31</ogr:TR_ID>
<ogr:TR_SEGMENT>13</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.32">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.28013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007697.28013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.32"><gml:posList>-1007697.28013693 1334642.87803237 -1007697.28013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>32</ogr:TR_ID>
<ogr:TR_SEGMENT>14</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.33">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.08013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007697.08013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.33"><gml:posList>-1007697.08013693 1334642.87803237 -1007697.08013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>33</ogr:TR_ID>
<ogr:TR_SEGMENT>15</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.34">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.88013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007696.88013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.34"><gml:posList>-1007696.88013693 1334642.87803237 -1007696.88013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>34</ogr:TR_ID>
<ogr:TR_SEGMENT>16</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.35">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.68013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007696.68013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.35"><gml:posList>-1007696.68013693 1334642.87803237 -1007696.68013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>35</ogr:TR_ID>
<ogr:TR_SEGMENT>17</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.36">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.48013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007696.48013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.36"><gml:posList>-1007696.48013693 1334642.87803237 -1007696.48013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>36</ogr:TR_ID>
<ogr:TR_SEGMENT>18</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.37">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.28013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007696.28013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.37"><gml:posList>-1007696.28013693 1334642.87803237 -1007696.28013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>37</ogr:TR_ID>
<ogr:TR_SEGMENT>19</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.38">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.08013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007696.08013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.38"><gml:posList>-1007696.08013693 1334642.87803237 -1007696.08013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>38</ogr:TR_ID>
<ogr:TR_SEGMENT>20</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.39">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007695.88013693 1334642.87803237</gml:lowerCorner><gml:upperCorner>-1007695.88013693 1334643.87803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.39"><gml:posList>-1007695.88013693 1334642.87803237 -1007695.88013693 1334643.87803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>39</ogr:TR_ID>
<ogr:TR_SEGMENT>21</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.40">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334643.17803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334643.17803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.40"><gml:posList>-1007696.38013693 1334643.17803237 -1007695.38013693 1334643.17803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>40</ogr:TR_ID>
<ogr:TR_SEGMENT>22</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.41">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334642.97803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334642.97803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.41"><gml:posList>-1007696.38013693 1334642.97803237 -1007695.38013693 1334642.97803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>41</ogr:TR_ID>
<ogr:TR_SEGMENT>23</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.42">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334642.77803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334642.77803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.42"><gml:posList>-1007696.38013693 1334642.77803237 -1007695.38013693 1334642.77803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>42</ogr:TR_ID>
<ogr:TR_SEGMENT>24</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.43">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334642.57803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334642.57803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.43"><gml:posList>-1007696.38013693 1334642.57803237 -1007695.38013693 1334642.57803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>43</ogr:TR_ID>
<ogr:TR_SEGMENT>25</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.44">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334642.37803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334642.37803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.44"><gml:posList>-1007696.38013693 1334642.37803237 -1007695.38013693 1334642.37803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>44</ogr:TR_ID>
<ogr:TR_SEGMENT>26</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.45">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334642.17803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334642.17803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.45"><gml:posList>-1007696.38013693 1334642.17803237 -1007695.38013693 1334642.17803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>45</ogr:TR_ID>
<ogr:TR_SEGMENT>27</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.46">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334641.97803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334641.97803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.46"><gml:posList>-1007696.38013693 1334641.97803237 -1007695.38013693 1334641.97803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>46</ogr:TR_ID>
<ogr:TR_SEGMENT>28</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.47">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334641.77803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334641.77803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.47"><gml:posList>-1007696.38013693 1334641.77803237 -1007695.38013693 1334641.77803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>47</ogr:TR_ID>
<ogr:TR_SEGMENT>29</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.48">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334641.57803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334641.57803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.48"><gml:posList>-1007696.38013693 1334641.57803237 -1007695.38013693 1334641.57803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>48</ogr:TR_ID>
<ogr:TR_SEGMENT>30</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.49">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.38013693 1334641.37803237</gml:lowerCorner><gml:upperCorner>-1007695.38013693 1334641.37803237</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.49"><gml:posList>-1007696.38013693 1334641.37803237 -1007695.38013693 1334641.37803237</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>49</ogr:TR_ID>
<ogr:TR_SEGMENT>31</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.50">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.08172314 1334640.8774</gml:lowerCorner><gml:upperCorner>-1007696.0785487 1334641.87739496</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.50"><gml:posList>-1007696.08172314 1334641.87739496 -1007696.0785487 1334640.8774</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>50</ogr:TR_ID>
<ogr:TR_SEGMENT>32</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.51">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.28172214 1334640.87676511</gml:lowerCorner><gml:upperCorner>-1007696.27854769 1334641.87676007</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.51"><gml:posList>-1007696.28172214 1334641.87676007 -1007696.27854769 1334640.87676511</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>51</ogr:TR_ID>
<ogr:TR_SEGMENT>33</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.52">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.48172113 1334640.87613022</gml:lowerCorner><gml:upperCorner>-1007696.47854668 1334641.87612518</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.52"><gml:posList>-1007696.48172113 1334641.87612518 -1007696.47854668 1334640.87613022</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>52</ogr:TR_ID>
<ogr:TR_SEGMENT>34</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.53">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.68172012 1334640.87549533</gml:lowerCorner><gml:upperCorner>-1007696.67854568 1334641.8754903</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.53"><gml:posList>-1007696.68172012 1334641.8754903 -1007696.67854568 1334640.87549533</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>53</ogr:TR_ID>
<ogr:TR_SEGMENT>35</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.54">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007696.88171911 1334640.87486045</gml:lowerCorner><gml:upperCorner>-1007696.87854467 1334641.87485541</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.54"><gml:posList>-1007696.88171911 1334641.87485541 -1007696.87854467 1334640.87486045</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>54</ogr:TR_ID>
<ogr:TR_SEGMENT>36</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.55">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.08202457 1334640.87500562</gml:lowerCorner><gml:upperCorner>-1007697.07823672 1334641.87499845</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.55"><gml:posList>-1007697.07823672 1334641.87499845 -1007697.08202457 1334640.87500562</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>55</ogr:TR_ID>
<ogr:TR_SEGMENT>37</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.56">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.28202314 1334640.8757632</gml:lowerCorner><gml:upperCorner>-1007697.27823528 1334641.87575602</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.56"><gml:posList>-1007697.27823528 1334641.87575602 -1007697.28202314 1334640.8757632</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>56</ogr:TR_ID>
<ogr:TR_SEGMENT>38</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.57">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.4820217 1334640.87652077</gml:lowerCorner><gml:upperCorner>-1007697.47823385 1334641.87651359</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.57"><gml:posList>-1007697.47823385 1334641.87651359 -1007697.4820217 1334640.87652077</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>57</ogr:TR_ID>
<ogr:TR_SEGMENT>39</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.58">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007697.68202027 1334640.87727834</gml:lowerCorner><gml:upperCorner>-1007697.67823241 1334641.87727117</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.58"><gml:posList>-1007697.67823241 1334641.87727117 -1007697.68202027 1334640.87727834</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>58</ogr:TR_ID>
<ogr:TR_SEGMENT>40</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
<ogr:featureMember>
<ogr:transect_fixed_single_both gml:id="transect_fixed_single_both.59">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::2154"><gml:lowerCorner>-1007698.38012491 1334641.37803232</gml:lowerCorner><gml:upperCorner>-1007697.38012491 1334641.37803232</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:LineString srsName="urn:ogc:def:crs:EPSG::2154" gml:id="transect_fixed_single_both.geom.59"><gml:posList>-1007697.38012491 1334641.37803232 -1007698.38012491 1334641.37803232</gml:posList></gml:LineString></ogr:geometryProperty>
<ogr:fid>4</ogr:fid>
<ogr:TR_FID>3</ogr:TR_FID>
<ogr:TR_ID>59</ogr:TR_ID>
<ogr:TR_SEGMENT>41</ogr:TR_SEGMENT>
<ogr:TR_ANGLE>90.00</ogr:TR_ANGLE>
<ogr:TR_LENGTH>1.000000</ogr:TR_LENGTH>
<ogr:TR_ORIENT>2</ogr:TR_ORIENT>
</ogr:transect_fixed_single_both>
</ogr:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
targetNamespace="http://ogr.maptools.org/"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:gmlsf="http://www.opengis.net/gmlsf/2.0"
elementFormDefault="qualified"
version="1.0">
<xs:annotation>
<xs:appinfo source="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd">
<gmlsf:ComplianceLevel>0</gmlsf:ComplianceLevel>
</xs:appinfo>
</xs:annotation>
<xs:import namespace="http://www.opengis.net/gml/3.2" schemaLocation="http://schemas.opengis.net/gml/3.2.1/gml.xsd"/>
<xs:import namespace="http://www.opengis.net/gmlsf/2.0" schemaLocation="http://schemas.opengis.net/gmlsfProfile/2.0/gmlsfLevels.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="featureMember">
<xs:complexType>
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureMemberType">
<xs:sequence>
<xs:element ref="gml:AbstractFeature"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="transect_fixed_single_both" type="ogr:transect_fixed_single_both_Type" substitutionGroup="gml:AbstractFeature"/>
<xs:complexType name="transect_fixed_single_both_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:CurvePropertyType" nillable="true" minOccurs="0" maxOccurs="1"/> <!-- restricted to LineString --><!-- srsName="urn:ogc:def:crs:EPSG::2154" -->
<xs:element name="fid" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_FID" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_ID" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_SEGMENT" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_ANGLE" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="6"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_LENGTH" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="6"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="TR_ORIENT" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -2466,6 +2466,21 @@ tests:
name: expected/transect_multi_both_2_30.gml name: expected/transect_multi_both_2_30.gml
type: vector type: vector
- algorithm: native:transectfixeddistance
name: Test (native:transectfixeddistance)
params:
ANGLE: 90.0
INPUT:
name: custom/transect_single.gml
type: vector
INTERVAL: 0.20
LENGTH: 1.0
SIDE: 2
results:
OUTPUT:
name: expected/transect_fixed_single_both.gml
type: vector
- algorithm: qgis:distancematrix - algorithm: qgis:distancematrix
name: Linear (N*k x 3) distance matrix name: Linear (N*k x 3) distance matrix
params: params:

View File

@ -8,6 +8,8 @@ set(QGIS_3D_SRCS
qgsaabb.cpp qgsaabb.cpp
qgsabstract3dengine.cpp qgsabstract3dengine.cpp
qgsabstractvectorlayer3drenderer.cpp qgsabstractvectorlayer3drenderer.cpp
qgsannotationlayer3drenderer.cpp
qgsannotationlayerchunkloader_p.cpp
qgs3danimationsettings.cpp qgs3danimationsettings.cpp
qgs3dexportobject.cpp qgs3dexportobject.cpp
qgs3dmapexportsettings.cpp qgs3dmapexportsettings.cpp
@ -154,6 +156,8 @@ set(QGIS_3D_HDRS
qgsaabb.h qgsaabb.h
qgsabstract3dengine.h qgsabstract3dengine.h
qgsabstractvectorlayer3drenderer.h qgsabstractvectorlayer3drenderer.h
qgsannotationlayer3drenderer.h
qgsannotationlayerchunkloader_p.h
qgscameracontroller.h qgscameracontroller.h
qgscamerapose.h qgscamerapose.h
qgsgeotransform.h qgsgeotransform.h

View File

@ -27,6 +27,7 @@
#include "qgsmeshlayer3drenderer.h" #include "qgsmeshlayer3drenderer.h"
#include "qgspointcloudlayer3drenderer.h" #include "qgspointcloudlayer3drenderer.h"
#include "qgstiledscenelayer3drenderer.h" #include "qgstiledscenelayer3drenderer.h"
#include "qgsannotationlayer3drenderer.h"
#include "qgs3dsymbolregistry.h" #include "qgs3dsymbolregistry.h"
#include "qgspoint3dsymbol.h" #include "qgspoint3dsymbol.h"
#include "qgsline3dsymbol.h" #include "qgsline3dsymbol.h"
@ -68,6 +69,7 @@ void Qgs3D::initialize()
QgsApplication::renderer3DRegistry()->addRenderer( new QgsMeshLayer3DRendererMetadata ); QgsApplication::renderer3DRegistry()->addRenderer( new QgsMeshLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsPointCloudLayer3DRendererMetadata ); QgsApplication::renderer3DRegistry()->addRenderer( new QgsPointCloudLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsTiledSceneLayer3DRendererMetadata ); QgsApplication::renderer3DRegistry()->addRenderer( new QgsTiledSceneLayer3DRendererMetadata );
QgsApplication::renderer3DRegistry()->addRenderer( new QgsAnnotationLayer3DRendererMetadata );
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "point" ), QObject::tr( "Point" ), &QgsPoint3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForPoint3DSymbol ) ); QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "point" ), QObject::tr( "Point" ), &QgsPoint3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForPoint3DSymbol ) );
QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "line" ), QObject::tr( "Line" ), &QgsLine3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForLine3DSymbol ) ); QgsApplication::symbol3DRegistry()->addSymbolType( new Qgs3DSymbolMetadata( QStringLiteral( "line" ), QObject::tr( "Line" ), &QgsLine3DSymbol::create, nullptr, Qgs3DSymbolImpl::handlerForLine3DSymbol ) );

View File

@ -45,6 +45,7 @@
#include "qgsapplication.h" #include "qgsapplication.h"
#include "qgsaabb.h" #include "qgsaabb.h"
#include "qgsabstract3dengine.h" #include "qgsabstract3dengine.h"
#include "qgsannotationlayer.h"
#include "qgs3dmapsettings.h" #include "qgs3dmapsettings.h"
#include "qgs3dutils.h" #include "qgs3dutils.h"
#include "qgsabstract3drenderer.h" #include "qgsabstract3drenderer.h"
@ -66,6 +67,7 @@
#include "qgsterraingenerator.h" #include "qgsterraingenerator.h"
#include "qgstiledscenelayer.h" #include "qgstiledscenelayer.h"
#include "qgstiledscenelayer3drenderer.h" #include "qgstiledscenelayer3drenderer.h"
#include "qgsannotationlayer3drenderer.h"
#include "qgsdirectionallightsettings.h" #include "qgsdirectionallightsettings.h"
#include "qgsvectorlayer.h" #include "qgsvectorlayer.h"
#include "qgsvectorlayer3drenderer.h" #include "qgsvectorlayer3drenderer.h"
@ -746,6 +748,11 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer ); QgsTiledSceneLayer3DRenderer *tiledSceneRenderer = static_cast<QgsTiledSceneLayer3DRenderer *>( renderer );
tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) ); tiledSceneRenderer->setLayer( static_cast<QgsTiledSceneLayer *>( layer ) );
} }
else if ( layer->type() == Qgis::LayerType::Annotation && renderer->type() == QLatin1String( "annotation" ) )
{
auto annotationLayerRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( renderer );
annotationLayerRenderer->setLayer( qobject_cast<QgsAnnotationLayer *>( layer ) );
}
Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap ); Qt3DCore::QEntity *newEntity = renderer->createEntity( &mMap );
if ( newEntity ) if ( newEntity )

View File

@ -1161,7 +1161,7 @@ QgsCameraPose Qgs3DUtils::lineSegmentToCameraPose( const QgsVector3D &startPoint
std::unique_ptr<Qt3DRender::QCamera> Qgs3DUtils::copyCamera( Qt3DRender::QCamera *cam ) std::unique_ptr<Qt3DRender::QCamera> Qgs3DUtils::copyCamera( Qt3DRender::QCamera *cam )
{ {
std::unique_ptr<Qt3DRender::QCamera> copy = std::make_unique<Qt3DRender::QCamera>(); auto copy = std::make_unique<Qt3DRender::QCamera>();
copy->setPosition( cam->position() ); copy->setPosition( cam->position() );
copy->setViewCenter( cam->viewCenter() ); copy->setViewCenter( cam->viewCenter() );
copy->setUpVector( cam->upVector() ); copy->setUpVector( cam->upVector() );

View File

@ -0,0 +1,155 @@
/***************************************************************************
qgsannotationlayer3drenderer.cpp
--------------------------------------
Date : January 2020
Copyright : (C) 2020 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayer3drenderer.h"
#include "qgsannotationlayer.h"
#include "qgsannotationlayerchunkloader_p.h"
#include "qgis.h"
//
// QgsAnnotationLayer3DRendererMetadata
//
QgsAnnotationLayer3DRendererMetadata::QgsAnnotationLayer3DRendererMetadata()
: Qgs3DRendererAbstractMetadata( QStringLiteral( "annotation" ) )
{
}
QgsAbstract3DRenderer *QgsAnnotationLayer3DRendererMetadata::createRenderer( QDomElement &elem, const QgsReadWriteContext &context )
{
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
r->readXml( elem, context );
return r.release();
}
//
// QgsAnnotationLayer3DRenderer
//
QgsAnnotationLayer3DRenderer::QgsAnnotationLayer3DRenderer() = default;
void QgsAnnotationLayer3DRenderer::setLayer( QgsAnnotationLayer *layer )
{
mLayerRef = QgsMapLayerRef( layer );
}
QgsAnnotationLayer *QgsAnnotationLayer3DRenderer::layer() const
{
return qobject_cast<QgsAnnotationLayer *>( mLayerRef.layer );
}
void QgsAnnotationLayer3DRenderer::resolveReferences( const QgsProject &project )
{
mLayerRef.resolve( &project );
}
bool QgsAnnotationLayer3DRenderer::showCalloutLines() const
{
return mShowCalloutLines;
}
void QgsAnnotationLayer3DRenderer::setShowCalloutLines( bool show )
{
mShowCalloutLines = show;
}
void QgsAnnotationLayer3DRenderer::setCalloutLineColor( const QColor &color )
{
mCalloutLineColor = color;
}
QColor QgsAnnotationLayer3DRenderer::calloutLineColor() const
{
return mCalloutLineColor;
}
void QgsAnnotationLayer3DRenderer::setCalloutLineWidth( double width )
{
mCalloutLineWidth = width;
}
double QgsAnnotationLayer3DRenderer::calloutLineWidth() const
{
return mCalloutLineWidth;
}
QString QgsAnnotationLayer3DRenderer::type() const
{
return "annotation";
}
QgsAnnotationLayer3DRenderer *QgsAnnotationLayer3DRenderer::clone() const
{
auto r = std::make_unique< QgsAnnotationLayer3DRenderer >();
r->mLayerRef = mLayerRef;
r->mAltClamping = mAltClamping;
r->mZOffset = mZOffset;
r->mShowCalloutLines = mShowCalloutLines;
r->mCalloutLineColor = mCalloutLineColor;
r->mCalloutLineWidth = mCalloutLineWidth;
return r.release();
}
Qt3DCore::QEntity *QgsAnnotationLayer3DRenderer::createEntity( Qgs3DMapSettings *map ) const
{
QgsAnnotationLayer *l = layer();
if ( !l )
return nullptr;
// For some cases we start with a maximal z range because we can't know this upfront, as it potentially involves terrain heights.
// This range will be refined after populating the nodes to the actual z range of the generated chunks nodes.
// Assuming the vertical height is in meter, then it's extremely unlikely that a real vertical
// height will exceed this amount!
constexpr double MINIMUM_ANNOTATION_Z_ESTIMATE = -100000;
constexpr double MAXIMUM_ANNOTATION_Z_ESTIMATE = 100000;
double minimumZ = MINIMUM_ANNOTATION_Z_ESTIMATE;
double maximumZ = MAXIMUM_ANNOTATION_Z_ESTIMATE;
switch ( mAltClamping )
{
case Qgis::AltitudeClamping::Absolute:
// special case where we DO know the exact z range upfront!
minimumZ = mZOffset;
maximumZ = mZOffset;
break;
case Qgis::AltitudeClamping::Relative:
case Qgis::AltitudeClamping::Terrain:
break;
}
return new QgsAnnotationLayerChunkedEntity( map, l, mAltClamping, mZOffset, mShowCalloutLines, mCalloutLineColor, mCalloutLineWidth, minimumZ, maximumZ );
}
void QgsAnnotationLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext & ) const
{
QDomDocument doc = elem.ownerDocument();
elem.setAttribute( QStringLiteral( "layer" ), mLayerRef.layerId );
elem.setAttribute( QStringLiteral( "clamping" ), qgsEnumValueToKey( mAltClamping ) );
elem.setAttribute( QStringLiteral( "offset" ), mZOffset );
if ( mShowCalloutLines )
elem.setAttribute( QStringLiteral( "callouts" ), QStringLiteral( "1" ) );
}
void QgsAnnotationLayer3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext & )
{
mLayerRef = QgsMapLayerRef( elem.attribute( QStringLiteral( "layer" ) ) );
mAltClamping = qgsEnumKeyToValue( elem.attribute( QStringLiteral( "clamping" ) ), Qgis::AltitudeClamping::Relative );
mZOffset = elem.attribute( QStringLiteral( "offset" ), QString::number( DEFAULT_Z_OFFSET ) ).toDouble();
mShowCalloutLines = elem.attribute( QStringLiteral( "callouts" ), QStringLiteral( "0" ) ).toInt();
}

View File

@ -0,0 +1,185 @@
/***************************************************************************
qgsannotationlayer3drenderer.h
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYER3DRENDERER_H
#define QGSANNOTATIONLAYER3DRENDERER_H
#include "qgis_3d.h"
#include "qgis_sip.h"
#include "qgs3drendererregistry.h"
#include "qgsabstract3drenderer.h"
#include "qgsmaplayerref.h"
class QgsAnnotationLayer;
#ifdef SIP_RUN
// this is needed for the "convert to subclass" code below to compile
% ModuleHeaderCode
#include "qgsannotationlayer3drenderer.h"
% End
#endif
/**
* \ingroup core
* \brief Metadata for annotation layer 3D renderer to allow creation of its instances from XML.
*
* \warning This is not considered stable API, and may change in future QGIS releases. It is
* exposed to the Python bindings as a tech preview only.
*
* \since QGIS 4.0
*/
class _3D_EXPORT QgsAnnotationLayer3DRendererMetadata : public Qgs3DRendererAbstractMetadata
{
public:
QgsAnnotationLayer3DRendererMetadata();
//! Creates an instance of a 3D renderer based on a DOM element with renderer configuration
QgsAbstract3DRenderer *createRenderer( QDomElement &elem, const QgsReadWriteContext &context ) override SIP_FACTORY;
};
/**
* \ingroup qgis_3d
* \brief 3D renderers for annotation layers.
*
* \since QGIS 4.0
*/
class _3D_EXPORT QgsAnnotationLayer3DRenderer : public QgsAbstract3DRenderer
{
#ifdef SIP_RUN
SIP_CONVERT_TO_SUBCLASS_CODE
if ( dynamic_cast<QgsAnnotationLayer3DRenderer *>( sipCpp ) != nullptr )
sipType = sipType_QgsAnnotationLayer3DRenderer;
else
sipType = nullptr;
SIP_END
#endif
public:
QgsAnnotationLayer3DRenderer();
/**
* Sets the annotation layer associated with the renderer.
*
* \see layer()
*/
void setLayer( QgsAnnotationLayer *layer );
/**
* Returns the annotation layer associated with the renderer.
*
* \see setLayer()
*/
QgsAnnotationLayer *layer() const;
QString type() const override;
QgsAnnotationLayer3DRenderer *clone() const override SIP_FACTORY;
Qt3DCore::QEntity *createEntity( Qgs3DMapSettings *map ) const override SIP_SKIP;
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
void resolveReferences( const QgsProject &project ) override;
/**
* Returns the altitude clamping method, which determines the vertical position of annotations.
*
* \see setAltitudeClamping()
*/
Qgis::AltitudeClamping altitudeClamping() const { return mAltClamping; }
/**
* Sets the altitude \a clamping method, which determines the vertical position of annotations.
*
* \see altitudeClamping()
*/
void setAltitudeClamping( Qgis::AltitudeClamping clamping ) { mAltClamping = clamping; }
/**
* Returns the z offset, which is a fixed offset amount which should be added to z values for the annotations.
*
* \see setZOffset()
*/
double zOffset() const { return mZOffset; }
/**
* Sets the z \a offset, which is a fixed offset amount which will be added to z values for the annotations.
*
* \see zOffset()
*/
void setZOffset( double offset ) { mZOffset = offset; }
/**
* Returns TRUE if callout lines are shown, vertically joining the annotations to the terrain.
*
* \see setShowCalloutLines()
*/
bool showCalloutLines() const;
/**
* Sets whether callout lines are shown, vertically joining the annotations to the terrain.
*
* \see showCalloutLines()
*/
void setShowCalloutLines( bool show );
// TODO -- consider exposing via QgsSimpleLineMaterialSettings, for now, for testing only
/**
* Sets the callout line \a color.
*
* \see calloutLineColor()
* \note Not available in Python bindings
*/
SIP_SKIP void setCalloutLineColor( const QColor &color );
/**
* Returns the callout line color.
*
* \see setCalloutLineColor()
* \note Not available in Python bindings
*/
SIP_SKIP QColor calloutLineColor() const;
/**
* Sets the callout line \a width.
*
* \see calloutLineWidth()
* \note Not available in Python bindings
*/
SIP_SKIP void setCalloutLineWidth( double width );
/**
* Returns the callout line width.
*
* \see setCalloutLineWidth()
* \note Not available in Python bindings
*/
SIP_SKIP double calloutLineWidth() const;
private:
#ifdef SIP_RUN
QgsAnnotationLayer3DRenderer( const QgsAnnotationLayer3DRenderer & );
#endif
static constexpr double DEFAULT_Z_OFFSET = 50;
QgsMapLayerRef mLayerRef;
Qgis::AltitudeClamping mAltClamping = Qgis::AltitudeClamping::Relative;
double mZOffset = DEFAULT_Z_OFFSET;
bool mShowCalloutLines = true;
QColor mCalloutLineColor { 0, 0, 0 };
double mCalloutLineWidth = 2;
};
#endif // QGSANNOTATIONLAYER3DRENDERER_H

View File

@ -0,0 +1,396 @@
/***************************************************************************
qgsannotationlayerchunkloader_p.cpp
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayerchunkloader_p.h"
#include "moc_qgsannotationlayerchunkloader_p.cpp"
#include "qgs3dutils.h"
#include "qgsannotationitem.h"
#include "qgstessellatedpolygongeometry.h"
#include "qgschunknode.h"
#include "qgseventtracing.h"
#include "qgslogger.h"
#include "qgsannotationlayer.h"
#include "qgsabstract3dsymbol.h"
#include "qgsabstractterrainsettings.h"
#include "qgsannotationmarkeritem.h"
#include "qgsbillboardgeometry.h"
#include "qgspoint3dbillboardmaterial.h"
#include "qgsgeotransform.h"
#include "qgsexpressioncontextutils.h"
#include "qgstextureatlasgenerator.h"
#include "qgslinevertexdata_p.h"
#include "qgslinematerial_p.h"
#include <QtConcurrent>
#include <Qt3DCore/QTransform>
///@cond PRIVATE
QgsAnnotationLayerChunkLoader::QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node )
: QgsChunkLoader( node )
, mFactory( factory )
, mRenderContext( factory->mRenderContext )
{
}
struct Billboard
{
QVector3D position;
int textureId;
};
void QgsAnnotationLayerChunkLoader::start()
{
QgsChunkNode *node = chunk();
if ( node->level() < mFactory->mLeafLevel )
{
QTimer::singleShot( 0, this, &QgsAnnotationLayerChunkLoader::finished );
return;
}
QgsAnnotationLayer *layer = mFactory->mLayer;
mLayerName = mFactory->mLayer->name();
// only a subset of data to be queried
const QgsRectangle rect = node->box3D().toRectangle();
// origin for coordinates of the chunk - it is kind of arbitrary, but it should be
// picked so that the coordinates are relatively small to avoid numerical precision issues
mChunkOrigin = QgsVector3D( rect.center().x(), rect.center().y(), 0 );
QgsExpressionContext exprContext;
exprContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
mRenderContext.setExpressionContext( exprContext );
QgsCoordinateTransform layerToMapTransform( layer->crs(), mRenderContext.crs(), mRenderContext.transformContext() );
QgsRectangle layerExtent;
try
{
layerExtent = layerToMapTransform.transformBoundingBox( rect, Qgis::TransformDirection::Reverse );
}
catch ( QgsCsException &e )
{
QgsDebugError( QStringLiteral( "Error transforming annotation layer extent to 3d map extent: %1" ).arg( e.what() ) );
return;
}
const double zOffset = mFactory->mZOffset;
const Qgis::AltitudeClamping altitudeClamping = mFactory->mClamping;
bool showCallouts = mFactory->mShowCallouts;
// see logic from QgsAnnotationLayerRenderer
const QStringList itemsList = layer->queryIndex( layerExtent );
QSet< QString > itemIds( itemsList.begin(), itemsList.end() );
// we also have NO choice but to clone ALL non-indexed items (i.e. those with a scale-dependent bounding box)
// since these won't be in the layer's spatial index, and it's too expensive to determine their actual bounding box
// upfront (we are blocking the main thread right now!)
// TODO -- come up with some brilliant way to avoid this and also index scale-dependent items ;)
itemIds.unite( layer->mNonIndexedItems );
mItemsToRender.reserve( itemIds.size() );
std::transform( itemIds.begin(), itemIds.end(), std::back_inserter( mItemsToRender ), [layer]( const QString &id ) -> std::unique_ptr< QgsAnnotationItem > {
return std::unique_ptr< QgsAnnotationItem >( layer->item( id )->clone() );
} );
//
// this will be run in a background thread
//
mFutureWatcher = new QFutureWatcher<void>( this );
connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
const QFuture<void> future = QtConcurrent::run( [this, rect, layerToMapTransform, zOffset, altitudeClamping, showCallouts] {
const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "Annotation layer chunk load" ) );
std::vector< Billboard > billboards;
billboards.reserve( mItemsToRender.size() );
QVector< QImage > textures;
textures.reserve( mItemsToRender.size() );
for ( const std::unique_ptr< QgsAnnotationItem > &item : std::as_const( mItemsToRender ) )
{
if ( mCanceled )
break;
QgsAnnotationItem *annotation = item.get();
if ( !annotation->enabled() )
continue;
if ( QgsAnnotationMarkerItem *marker = dynamic_cast< QgsAnnotationMarkerItem * >( annotation ) )
{
if ( marker->symbol() )
{
QgsPointXY p = marker->geometry();
try
{
const QgsPointXY mapPoint = layerToMapTransform.transform( p );
if ( !rect.contains( mapPoint ) )
continue;
double z = 0;
const float terrainZ = ( altitudeClamping == Qgis::AltitudeClamping::Absolute && !showCallouts ) ? 0 : mRenderContext.terrainRenderingEnabled() && mRenderContext.terrainGenerator() ? static_cast<float>( mRenderContext.terrainGenerator()->heightAt( mapPoint.x(), mapPoint.y(), mRenderContext ) * mRenderContext.terrainSettings()->verticalScale() )
: 0.f;
switch ( altitudeClamping )
{
case Qgis::AltitudeClamping::Absolute:
z = zOffset;
break;
case Qgis::AltitudeClamping::Terrain:
z = terrainZ;
break;
case Qgis::AltitudeClamping::Relative:
z = terrainZ + zOffset;
break;
}
Billboard billboard;
billboard.position = ( QgsVector3D( mapPoint.x(), mapPoint.y(), z ) - mChunkOrigin ).toVector3D();
billboard.textureId = textures.size();
textures.append( QgsPoint3DBillboardMaterial::renderSymbolToImage( marker->symbol(), mRenderContext ) );
billboards.emplace_back( std::move( billboard ) );
if ( showCallouts )
{
mCalloutLines << QgsLineString( { mapPoint.x(), mapPoint.x() }, { mapPoint.y(), mapPoint.y() }, { terrainZ, z } );
}
mZMax = std::max( mZMax, showCallouts ? std::max( 0.0, z ) : z );
mZMin = std::min( mZMin, showCallouts ? std::min( 0.0, z ) : z );
}
catch ( QgsCsException &e )
{
QgsDebugError( e.what() );
}
}
}
}
// free memory
mItemsToRender.clear();
const QgsTextureAtlas atlas = QgsTextureAtlasGenerator::createFromImages( textures, 2048 );
if ( atlas.isValid() )
{
mBillboardAtlas = atlas.renderAtlasTexture();
mBillboardPositions.reserve( static_cast< int >( billboards.size() ) );
for ( Billboard &billboard : billboards )
{
const QRect textureRect = atlas.rect( billboard.textureId );
QgsBillboardGeometry::BillboardAtlasData geometry;
geometry.position = billboard.position;
geometry.textureAtlasOffset = QVector2D( static_cast< float >( textureRect.left() ) / static_cast< float>( mBillboardAtlas.width() ), 1 - ( static_cast< float >( textureRect.bottom() ) / static_cast< float>( mBillboardAtlas.height() ) ) );
geometry.textureAtlasSize = QVector2D( static_cast< float >( textureRect.width() ) / static_cast< float>( mBillboardAtlas.width() ), static_cast< float>( textureRect.height() ) / static_cast< float>( mBillboardAtlas.height() ) );
mBillboardPositions.append( geometry );
}
}
else
{
QgsDebugError( QStringLiteral( "Error encountered building texture atlas" ) );
mBillboardAtlas = QImage();
}
} );
// emit finished() as soon as the handler is populated with features
mFutureWatcher->setFuture( future );
}
QgsAnnotationLayerChunkLoader::~QgsAnnotationLayerChunkLoader()
{
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
{
disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
mFutureWatcher->waitForFinished();
}
}
void QgsAnnotationLayerChunkLoader::cancel()
{
mCanceled = true;
}
Qt3DCore::QEntity *QgsAnnotationLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
{
if ( mNode->level() < mFactory->mLeafLevel )
{
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent ); // dummy entity
entity->setObjectName( mLayerName + "_CONTAINER_" + mNode->tileId().text() );
return entity;
}
if ( mBillboardPositions.empty() )
{
// an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
// we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
mNode->setExactBox3D( QgsBox3D() );
mNode->updateParentBoundingBoxesRecursively();
return nullptr;
}
Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
QgsBillboardGeometry *billboardGeometry = new QgsBillboardGeometry();
billboardGeometry->setBillboardData( mBillboardPositions );
Qt3DRender::QGeometryRenderer *billboardGeometryRenderer = new Qt3DRender::QGeometryRenderer;
billboardGeometryRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::Points );
billboardGeometryRenderer->setGeometry( billboardGeometry );
billboardGeometryRenderer->setVertexCount( billboardGeometry->count() );
QgsPoint3DBillboardMaterial *billboardMaterial = new QgsPoint3DBillboardMaterial( QgsPoint3DBillboardMaterial::Mode::AtlasTexture );
billboardMaterial->setTexture2DFromImage( mBillboardAtlas );
QgsGeoTransform *billboardTransform = new QgsGeoTransform;
billboardTransform->setGeoTranslation( mChunkOrigin );
Qt3DCore::QEntity *billboardEntity = new Qt3DCore::QEntity;
billboardEntity->addComponent( billboardMaterial );
billboardEntity->addComponent( billboardTransform );
billboardEntity->addComponent( billboardGeometryRenderer );
billboardEntity->setParent( entity );
if ( mFactory->mShowCallouts )
{
QgsLineVertexData lineData;
lineData.withAdjacency = true;
lineData.geocentricCoordinates = false; // mMapSettings->sceneMode() == Qgis::SceneMode::Globe;
lineData.init( Qgis::AltitudeClamping::Absolute, Qgis::AltitudeBinding::Vertex, 0, mRenderContext, mChunkOrigin );
for ( const QgsLineString &line : mCalloutLines )
{
lineData.addLineString( line, 0, false );
}
QgsLineMaterial *mat = new QgsLineMaterial;
mat->setLineColor( mFactory->mCalloutLineColor );
mat->setLineWidth( mFactory->mCalloutLineWidth );
Qt3DCore::QEntity *calloutEntity = new Qt3DCore::QEntity;
calloutEntity->setObjectName( parent->objectName() + "_CALLOUTS" );
// geometry renderer
Qt3DRender::QGeometryRenderer *calloutRenderer = new Qt3DRender::QGeometryRenderer;
calloutRenderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStripAdjacency );
calloutRenderer->setGeometry( lineData.createGeometry( calloutEntity ) );
calloutRenderer->setVertexCount( lineData.indexes.count() );
calloutRenderer->setPrimitiveRestartEnabled( true );
calloutRenderer->setRestartIndexValue( 0 );
// make entity
calloutEntity->addComponent( calloutRenderer );
calloutEntity->addComponent( mat );
calloutEntity->setParent( billboardEntity );
}
// fix the vertical range of the node from the estimated vertical range to the true range
if ( mZMin != std::numeric_limits<float>::max() && mZMax != std::numeric_limits<float>::lowest() )
{
QgsBox3D box = mNode->box3D();
box.setZMinimum( mZMin );
box.setZMaximum( mZMax );
mNode->setExactBox3D( box );
mNode->updateParentBoundingBoxesRecursively();
}
return entity;
}
///////////////
QgsAnnotationLayerChunkLoaderFactory::QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
: mRenderContext( context )
, mLayer( layer )
, mLeafLevel( leafLevel )
, mClamping( clamping )
, mZOffset( zOffset )
, mShowCallouts( showCallouts )
, mCalloutLineColor( calloutLineColor )
, mCalloutLineWidth( calloutLineWidth )
{
if ( context.crs().type() == Qgis::CrsType::Geocentric )
{
// TODO: add support for handling of annotation layers
// (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
QgsDebugError( QStringLiteral( "Annotation layers in globe scenes are not supported yet!" ) );
setupQuadtree( QgsBox3D( -1e7, -1e7, -1e7, 1e7, 1e7, 1e7 ), -1, leafLevel );
return;
}
QgsBox3D rootBox3D( context.extent(), zMin, zMax );
// add small padding to avoid clipping of point features located at the edge of the bounding box
rootBox3D.grow( 1.0 );
setupQuadtree( rootBox3D, -1, leafLevel ); // negative root error means that the node does not contain anything
}
QgsChunkLoader *QgsAnnotationLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
{
return new QgsAnnotationLayerChunkLoader( this, node );
}
///////////////
QgsAnnotationLayerChunkedEntity::QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax )
: QgsChunkedEntity( map,
-1, // max. allowed screen error (negative tau means that we need to go until leaves are reached)
new QgsAnnotationLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), layer, 3, clamping, zOffset, showCallouts, calloutLineColor, calloutLineWidth, zMin, zMax ), true )
{
mTransform = new Qt3DCore::QTransform;
if ( applyTerrainOffset() )
{
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, static_cast<float>( map->terrainSettings()->elevationOffset() ) ) );
}
this->addComponent( mTransform );
connect( map, &Qgs3DMapSettings::terrainSettingsChanged, this, &QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged );
}
QgsAnnotationLayerChunkedEntity::~QgsAnnotationLayerChunkedEntity()
{
// cancel / wait for jobs
cancelActiveJobs();
}
// if the AltitudeClamping is `Absolute`, do not apply the offset
bool QgsAnnotationLayerChunkedEntity::applyTerrainOffset() const
{
if ( auto loaderFactory = static_cast<QgsAnnotationLayerChunkLoaderFactory *>( mChunkLoaderFactory ) )
{
return loaderFactory->mClamping != Qgis::AltitudeClamping::Absolute;
}
return true;
}
void QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged()
{
QgsDebugMsgLevel( QStringLiteral( "QgsAnnotationLayerChunkedEntity::onTerrainElevationOffsetChanged" ), 2 );
float newOffset = static_cast<float>( qobject_cast<Qgs3DMapSettings *>( sender() )->terrainSettings()->elevationOffset() );
if ( !applyTerrainOffset() )
{
newOffset = 0.0;
}
mTransform->setTranslation( QVector3D( 0.0f, 0.0f, newOffset ) );
}
/// @endcond

View File

@ -0,0 +1,146 @@
/***************************************************************************
qgsannotationlayerchunkloader_p.h
--------------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYERCHUNKLOADER_P_H
#define QGSANNOTATIONLAYERCHUNKLOADER_P_H
///@cond PRIVATE
//
// W A R N I N G
// -------------
//
// This file is not part of the QGIS API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
#include "qgschunkloader.h"
#include "qgschunkedentity.h"
#include "qgs3drendercontext.h"
#include "qgsbillboardgeometry.h"
#include <QImage>
#define SIP_NO_FILE
class QgsAnnotationLayer;
class QgsAnnotationItem;
namespace Qt3DCore
{
class QTransform;
}
#include <QFutureWatcher>
/**
* \ingroup qgis_3d
* \brief This loader factory is responsible for creation of loaders of QgsAnnotationLayerChunkedEntity.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkLoaderFactory : public QgsQuadtreeChunkLoaderFactory
{
Q_OBJECT
public:
//! Constructs the factory
QgsAnnotationLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsAnnotationLayer *layer, int leafLevel, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
Qgs3DRenderContext mRenderContext;
QgsAnnotationLayer *mLayer = nullptr;
int mLeafLevel = 0;
Qgis::AltitudeClamping mClamping = Qgis::AltitudeClamping::Relative;
double mZOffset = 0;
bool mShowCallouts = false;
QColor mCalloutLineColor;
double mCalloutLineWidth = 2;
};
/**
* \ingroup qgis_3d
* \brief This loader class is responsible for async loading of data for QgsAnnotationLayerChunkedEntity
* and creation of final 3D entity from the data previously prepared in a worker thread.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkLoader : public QgsChunkLoader
{
Q_OBJECT
public:
//! Constructs the loader
QgsAnnotationLayerChunkLoader( const QgsAnnotationLayerChunkLoaderFactory *factory, QgsChunkNode *node );
~QgsAnnotationLayerChunkLoader() override;
void start() override;
virtual void cancel() override;
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) override;
private:
const QgsAnnotationLayerChunkLoaderFactory *mFactory = nullptr;
Qgs3DRenderContext mRenderContext;
bool mCanceled = false;
QFutureWatcher<void> *mFutureWatcher = nullptr;
QString mLayerName;
QgsVector3D mChunkOrigin;
std::vector< std::unique_ptr< QgsAnnotationItem > > mItemsToRender;
QVector< QgsBillboardGeometry::BillboardAtlasData > mBillboardPositions;
QVector< QgsLineString > mCalloutLines;
QImage mBillboardAtlas;
double mZMin = std::numeric_limits< double >::max();
double mZMax = std::numeric_limits< double >::lowest();
};
/**
* \ingroup qgis_3d
* \brief 3D entity used for rendering of annotation layers.
*
* Internally it uses QgsAnnotationLayerChunkLoaderFactory and
* QgsAnnotationLayerChunkLoader to do the actual work
* of loading and creating 3D sub-entities for the layer.
*
* \since QGIS 4.0
*/
class QgsAnnotationLayerChunkedEntity : public QgsChunkedEntity
{
Q_OBJECT
public:
//! Constructs the entity.
explicit QgsAnnotationLayerChunkedEntity( Qgs3DMapSettings *map, QgsAnnotationLayer *layer, Qgis::AltitudeClamping clamping, double zOffset, bool showCallouts, const QColor &calloutLineColor, double calloutLineWidth, double zMin, double zMax );
~QgsAnnotationLayerChunkedEntity();
private slots:
void onTerrainElevationOffsetChanged();
private:
Qt3DCore::QTransform *mTransform = nullptr;
bool applyTerrainOffset() const;
friend class TestQgsChunkedEntity;
};
/// @endcond
#endif // QGSANNOTATIONLAYERCHUNKLOADER_P_H

View File

@ -289,6 +289,8 @@ set(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmtaperedbuffer.cpp processing/qgsalgorithmtaperedbuffer.cpp
processing/qgsalgorithmtinmeshcreation.cpp processing/qgsalgorithmtinmeshcreation.cpp
processing/qgsalgorithmtransect.cpp processing/qgsalgorithmtransect.cpp
processing/qgsalgorithmtransectbase.cpp
processing/qgsalgorithmtransectfixeddistance.cpp
processing/qgsalgorithmtransform.cpp processing/qgsalgorithmtransform.cpp
processing/qgsalgorithmtranslate.cpp processing/qgsalgorithmtranslate.cpp
processing/qgsalgorithmtruncatetable.cpp processing/qgsalgorithmtruncatetable.cpp

View File

@ -296,7 +296,7 @@ QgsPointCloudLayer *QgsPdalAlgorithmBase::parameterAsPointCloudLayer( const QVar
return nullptr; return nullptr;
// if COPC provider, return as it is // if COPC provider, return as it is
if ( layer->dataProvider()->name() == QStringLiteral( "copc" ) ) if ( layer->dataProvider()->name() == QLatin1String( "copc" ) )
{ {
return layer; return layer;
} }

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckAngleAlgorithm::initAlgorithm( const QVariantMap &configura
QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "ERRORS" ), QObject::tr( "Small angle errors" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -97,7 +97,7 @@ void QgsGeometryCheckAreaAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Small polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -98,7 +98,7 @@ void QgsGeometryCheckContainedAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "Contained features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Contained features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckDangleAlgorithm::initAlgorithm( const QVariantMap &configur
QStringLiteral( "OUTPUT" ), QObject::tr( "Dangle-end features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Dangle-end features" ), Qgis::ProcessingSourceType::VectorLine, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -87,7 +87,7 @@ void QgsGeometryCheckDegeneratePolygonAlgorithm::initAlgorithm( const QVariantMa
QStringLiteral( "OUTPUT" ), QObject::tr( "Degenerate polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Degenerate polygons features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 ); auto tolerance = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );
tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, " tolerance->setHelp( QObject::tr( "The \"Tolerance\" advanced parameter defines the numerical precision of geometric operations, "
"given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) ); "given as an integer n, meaning that any difference smaller than 10⁻ⁿ (in map units) is considered zero." ) );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckDuplicateAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicate geometries" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicate geometries" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -89,7 +89,7 @@ void QgsGeometryCheckDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicated vertices features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Duplicated vertices features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -92,7 +92,7 @@ void QgsGeometryCheckFollowBoundariesAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon ) QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorPolygon )
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -108,7 +108,7 @@ void QgsGeometryCheckGapAlgorithm::initAlgorithm( const QVariantMap &configurati
QStringLiteral( "OUTPUT" ), QObject::tr( "Gap features" ), Qgis::ProcessingSourceType::VectorPolygon QStringLiteral( "OUTPUT" ), QObject::tr( "Gap features" ), Qgis::ProcessingSourceType::VectorPolygon
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckHoleAlgorithm::initAlgorithm( const QVariantMap &configurat
QStringLiteral( "OUTPUT" ), QObject::tr( "Polygons with holes" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Polygons with holes" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckLineIntersectionAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "OUTPUT" ), QObject::tr( "Intersecting feature" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Intersecting feature" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -94,7 +94,7 @@ void QgsGeometryCheckLineLayerIntersectionAlgorithm::initAlgorithm( const QVaria
QStringLiteral( "OUTPUT" ), QObject::tr( "Line intersecting other layer features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Line intersecting other layer features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -95,7 +95,7 @@ void QgsGeometryCheckMissingVertexAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "OUTPUT" ), QObject::tr( "Missing vertices features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Missing vertices features" ), Qgis::ProcessingSourceType::VectorPolygon, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -93,7 +93,7 @@ void QgsGeometryCheckMultipartAlgorithm::initAlgorithm( const QVariantMap &confi
QStringLiteral( "OUTPUT" ), QObject::tr( "One-part geometry features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "One-part geometry features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -92,7 +92,7 @@ void QgsGeometryCheckOverlapAlgorithm::initAlgorithm( const QVariantMap &configu
QStringLiteral( "MIN_OVERLAP_AREA" ), QObject::tr( "Minimum overlap area" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0 QStringLiteral( "MIN_OVERLAP_AREA" ), QObject::tr( "Minimum overlap area" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckPointCoveredByLineAlgorithm::initAlgorithm( const QVariantM
QStringLiteral( "ERRORS" ), QObject::tr( "Points not covered by a line" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "ERRORS" ), QObject::tr( "Points not covered by a line" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -88,7 +88,7 @@ void QgsGeometryCheckPointInPolygonAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "ERRORS" ), QObject::tr( "Points outside polygons errors" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "ERRORS" ), QObject::tr( "Points outside polygons errors" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -93,7 +93,7 @@ void QgsGeometryCheckSegmentLengthAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "MIN_SEGMENT_LENGTH" ), QObject::tr( "Minimum segment length" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0 QStringLiteral( "MIN_SEGMENT_LENGTH" ), QObject::tr( "Minimum segment length" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -89,7 +89,7 @@ void QgsGeometryCheckSelfContactAlgorithm::initAlgorithm( const QVariantMap &con
QStringLiteral( "OUTPUT" ), QObject::tr( "Self contact features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Self contact features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -91,7 +91,7 @@ void QgsGeometryCheckSelfIntersectionAlgorithm::initAlgorithm( const QVariantMap
QStringLiteral( "OUTPUT" ), QObject::tr( "Self-intersecting features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false QStringLiteral( "OUTPUT" ), QObject::tr( "Self-intersecting features" ), Qgis::ProcessingSourceType::VectorAnyGeometry, QVariant(), true, false
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -100,7 +100,7 @@ void QgsGeometryCheckSliverPolygonAlgorithm::initAlgorithm( const QVariantMap &c
QStringLiteral( "MAX_AREA" ), QObject::tr( "Maximum area (map units squared)" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0 QStringLiteral( "MAX_AREA" ), QObject::tr( "Maximum area (map units squared)" ), Qgis::ProcessingNumberParameterType::Double, 0, false, 0.0
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -196,11 +196,11 @@ QVariantMap QgsClimbAlgorithm::processAlgorithm( const QVariantMap &parameters,
if ( !noGeometry.empty() ) if ( !noGeometry.empty() )
{ {
feedback->pushInfo( QObject::tr( "The following features do not have geometry: %1" ).arg( noGeometry.join( QStringLiteral( ", " ) ) ) ); feedback->pushInfo( QObject::tr( "The following features do not have geometry: %1" ).arg( noGeometry.join( QLatin1String( ", " ) ) ) );
} }
if ( !noZValue.empty() ) if ( !noZValue.empty() )
{ {
feedback->pushInfo( QObject::tr( "The following points do not have Z value: %1" ).arg( noZValue.join( QStringLiteral( ", " ) ) ) ); feedback->pushInfo( QObject::tr( "The following points do not have Z value: %1" ).arg( noZValue.join( QLatin1String( ", " ) ) ) );
} }
QVariantMap results; QVariantMap results;

View File

@ -107,7 +107,7 @@ void QgsFixGeometryAngleAlgorithm::initAlgorithm( const QVariantMap &configurati
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing small angles" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing small angles" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -120,7 +120,7 @@ void QgsFixGeometryAreaAlgorithm::initAlgorithm( const QVariantMap &configuratio
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from merging small polygons" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from merging small polygons" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -104,7 +104,7 @@ void QgsFixGeometryDuplicateNodesAlgorithm::initAlgorithm( const QVariantMap &co
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing duplicate vertices" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing duplicate vertices" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -108,7 +108,7 @@ void QgsFixGeometryGapAlgorithm::initAlgorithm( const QVariantMap &configuration
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing gaps" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing gaps" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -104,7 +104,7 @@ void QgsFixGeometryHoleAlgorithm::initAlgorithm( const QVariantMap &configuratio
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing holes" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing holes" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -105,7 +105,7 @@ void QgsFixGeometryMissingVertexAlgorithm::initAlgorithm( const QVariantMap &con
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing border vertices" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing border vertices" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -91,7 +91,7 @@ void QgsFixGeometryMultipartAlgorithm::initAlgorithm( const QVariantMap &configu
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing multiparts" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing multiparts" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -99,7 +99,7 @@ void QgsFixGeometryOverlapAlgorithm::initAlgorithm( const QVariantMap &configura
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing overlaps" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing overlaps" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -129,7 +129,7 @@ void QgsFixGeometrySelfIntersectionAlgorithm::initAlgorithm( const QVariantMap &
QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing self-intersections" ), Qgis::ProcessingSourceType::VectorPoint QStringLiteral( "REPORT" ), QObject::tr( "Report layer from fixing self-intersections" ), Qgis::ProcessingSourceType::VectorPoint
) ); ) );
std::unique_ptr<QgsProcessingParameterNumber> tolerance = std::make_unique<QgsProcessingParameterNumber>( auto tolerance = std::make_unique<QgsProcessingParameterNumber>(
QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13 QStringLiteral( "TOLERANCE" ), QObject::tr( "Tolerance" ), Qgis::ProcessingNumberParameterType::Integer, 8, false, 1, 13
); );
tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced ); tolerance->setFlags( tolerance->flags() | Qgis::ProcessingParameterFlag::Advanced );

View File

@ -90,7 +90,7 @@ Qgis::ProcessingFeatureSourceFlags QgsRemovePartsByAreaAlgorithm::sourceFlags()
void QgsRemovePartsByAreaAlgorithm::initParameters( const QVariantMap & ) void QgsRemovePartsByAreaAlgorithm::initParameters( const QVariantMap & )
{ {
std::unique_ptr< QgsProcessingParameterArea > minArea = std::make_unique< QgsProcessingParameterArea >( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 ); auto minArea = std::make_unique< QgsProcessingParameterArea >( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
minArea->setIsDynamic( true ); minArea->setIsDynamic( true );
minArea->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), QgsPropertyDefinition::DoublePositive ) ); minArea->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_AREA" ), QObject::tr( "Remove parts with area less than" ), QgsPropertyDefinition::DoublePositive ) );
minArea->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) ); minArea->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );

View File

@ -90,7 +90,7 @@ Qgis::ProcessingFeatureSourceFlags QgsRemovePartsByLengthAlgorithm::sourceFlags(
void QgsRemovePartsByLengthAlgorithm::initParameters( const QVariantMap & ) void QgsRemovePartsByLengthAlgorithm::initParameters( const QVariantMap & )
{ {
std::unique_ptr< QgsProcessingParameterDistance > minLength = std::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with lengths less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 ); auto minLength = std::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with lengths less than" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
minLength->setIsDynamic( true ); minLength->setIsDynamic( true );
minLength->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with length less than" ), QgsPropertyDefinition::DoublePositive ) ); minLength->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "MIN_LENGTH" ), QObject::tr( "Remove parts with length less than" ), QgsPropertyDefinition::DoublePositive ) );
minLength->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) ); minLength->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );

View File

@ -31,40 +31,6 @@ QString QgsTransectAlgorithm::displayName() const
return QObject::tr( "Transect" ); return QObject::tr( "Transect" );
} }
QStringList QgsTransectAlgorithm::tags() const
{
return QObject::tr( "transect,station,lines,extend," ).split( ',' );
}
QString QgsTransectAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}
QString QgsTransectAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}
void QgsTransectAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
auto length = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), 5.0, QStringLiteral( "INPUT" ), false, 0 );
length->setIsDynamic( true );
length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( length.release() );
auto angle = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
angle->setIsDynamic( true );
angle->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
angle->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( angle.release() );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "SIDE" ), QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
}
QString QgsTransectAlgorithm::shortHelpString() const QString QgsTransectAlgorithm::shortHelpString() const
{ {
return QObject::tr( "This algorithm creates transects on vertices for (multi)linestrings.\n" ) return QObject::tr( "This algorithm creates transects on vertices for (multi)linestrings.\n" )
@ -79,156 +45,37 @@ QString QgsTransectAlgorithm::shortHelpString() const
+ QObject::tr( "- TR_ORIENT: Side of the transect (only on the left or right of the line, or both side)\n" ); + QObject::tr( "- TR_ORIENT: Side of the transect (only on the left or right of the line, or both side)\n" );
} }
QString QgsTransectAlgorithm::shortDescription() const
{
return QObject::tr( "Creates transects on vertices for (multi)linestrings." );
}
Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectAlgorithm::documentationFlags() const
{
return Qgis::ProcessingAlgorithmDocumentationFlag::RegeneratesPrimaryKey;
}
QgsTransectAlgorithm *QgsTransectAlgorithm::createInstance() const QgsTransectAlgorithm *QgsTransectAlgorithm::createInstance() const
{ {
return new QgsTransectAlgorithm(); return new QgsTransectAlgorithm();
} }
QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) void QgsTransectAlgorithm::addAlgorithmParams()
{ {
const Side orientation = static_cast<QgsTransectAlgorithm::Side>( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) ); // No additional parameters for the basic transect algorithm (vertex-based only)
const double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
const bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
QgsProperty angleProperty;
if ( dynamicAngle )
angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value<QgsProperty>();
double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
const bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
QgsProperty lengthProperty;
if ( dynamicLength )
lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value<QgsProperty>();
if ( orientation == QgsTransectAlgorithm::Both )
length /= 2.0;
std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
QgsFields newFields;
newFields.append( QgsField( QStringLiteral( "TR_FID" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_ID" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QMetaType::Type::Double, QString(), 5, 2 ) );
newFields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QMetaType::Type::Double, QString(), 20, 6 ) );
newFields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QMetaType::Type::Int, QString(), 1 ) );
QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
Qgis::WkbType outputWkb = Qgis::WkbType::LineString;
if ( QgsWkbTypes::hasZ( source->wkbType() ) )
outputWkb = QgsWkbTypes::addZ( outputWkb );
if ( QgsWkbTypes::hasM( source->wkbType() ) )
outputWkb = QgsWkbTypes::addM( outputWkb );
QString dest;
std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsFeatureIterator features = source->getFeatures();
int current = -1;
int number = 0;
const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
QgsFeature feat;
while ( features.nextFeature( feat ) )
{
current++;
if ( feedback->isCanceled() )
{
break;
}
feedback->setProgress( current * step );
if ( !feat.hasGeometry() )
continue;
QgsGeometry inputGeometry = feat.geometry();
if ( dynamicLength || dynamicAngle )
{
expressionContext.setFeature( feat );
}
double evaluatedLength = length;
if ( dynamicLength )
evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length );
double evaluatedAngle = angle;
if ( dynamicAngle )
evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle );
inputGeometry.convertToMultiType();
const QgsMultiLineString *multiLine = static_cast<const QgsMultiLineString *>( inputGeometry.constGet() );
for ( int id = 0; id < multiLine->numGeometries(); ++id )
{
const QgsLineString *line = multiLine->lineStringN( id );
QgsAbstractGeometry::vertex_iterator it = line->vertices_begin();
while ( it != line->vertices_end() )
{
const QgsVertexId vertexId = it.vertexId();
const int i = vertexId.vertex;
QgsFeature outFeat;
QgsAttributes attrs = feat.attributes();
attrs << current << number << i + 1 << evaluatedAngle << ( ( orientation == QgsTransectAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength ) << orientation;
outFeat.setAttributes( attrs );
const double angleAtVertex = line->vertexAngle( vertexId );
outFeat.setGeometry( calcTransect( *it, angleAtVertex, evaluatedLength, orientation, evaluatedAngle ) );
if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
number++;
it++;
}
}
}
sink->finalize();
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
} }
bool QgsTransectAlgorithm::prepareAlgorithmTransectParameters( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
QgsGeometry QgsTransectAlgorithm::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithm::Side orientation, const double angle )
{ {
QgsPoint pLeft; // left point of the line // No additional preparation needed for basic transect algorithm
QgsPoint pRight; // right point of the line return true;
QgsPolyline line;
if ( ( orientation == QgsTransectAlgorithm::Right ) || ( orientation == QgsTransectAlgorithm::Both ) )
{
pLeft = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
if ( orientation != QgsTransectAlgorithm::Both )
pRight = point;
}
if ( ( orientation == QgsTransectAlgorithm::Left ) || ( orientation == QgsTransectAlgorithm::Both ) )
{
pRight = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
if ( orientation != QgsTransectAlgorithm::Both )
pLeft = point;
}
line.append( pLeft );
line.append( pRight );
return QgsGeometry::fromPolyline( line );
} }
///@endcond std::vector<QgsPoint> QgsTransectAlgorithm::generateSamplingPoints( const QgsLineString &line, const QVariantMap &, QgsProcessingContext & )
{
std::vector<QgsPoint> samplingPoints;
// Vertex-based sampling only (like original master algorithm)
for ( auto it = line.vertices_begin(); it != line.vertices_end(); ++it )
samplingPoints.push_back( *it );
return samplingPoints;
}
double QgsTransectAlgorithm::calculateAzimuth( const QgsLineString &line, const QgsPoint &, int pointIndex )
{
// For vertex-based sampling, use vertex angle directly (like original master algorithm)
return line.vertexAngle( QgsVertexId( 0, 0, pointIndex ) );
}
///@endcond

View File

@ -21,50 +21,27 @@
#define SIP_NO_FILE #define SIP_NO_FILE
#include "qgis_sip.h" #include "qgis_sip.h"
#include "qgsprocessingalgorithm.h" #include "qgsalgorithmtransectbase.h"
///@cond PRIVATE ///@cond PRIVATE
/** /**
* Native transect algorithm. * Native transect algorithm.
*/ */
class QgsTransectAlgorithm : public QgsProcessingAlgorithm class QgsTransectAlgorithm : public QgsTransectAlgorithmBase
{ {
public: public:
/**
* Draw the transect on which side of the line
*/
enum Side
{
Left,
Right,
Both
};
QgsTransectAlgorithm() = default; QgsTransectAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override; QString name() const override;
QString displayName() const override; QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override; QString shortHelpString() const override;
QString shortDescription() const override;
Qgis::ProcessingAlgorithmDocumentationFlags documentationFlags() const override;
QgsTransectAlgorithm *createInstance() const override SIP_FACTORY; QgsTransectAlgorithm *createInstance() const override SIP_FACTORY;
protected: protected:
QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; void addAlgorithmParams() override;
bool prepareAlgorithmTransectParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private: std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap &parameters, QgsProcessingContext &context ) override;
/** double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) override;
* Returns the transect of the point \a point with \a length, \a orientation and \a angle.
* \param point The vertex
* \param angleAtVertex Angle at the vertex
* \param length Length of the transect Distance to extend line from input feature
* \param orientation Orientation of the transect
* \param angle Angle of the transect relative to the segment [\a p1 - \a p2] (degrees clockwise)
*/
QgsGeometry calcTransect( const QgsPoint &point, double angleAtVertex, double length, Side orientation, double angle );
}; };
///@endcond PRIVATE ///@endcond PRIVATE

View File

@ -0,0 +1,221 @@
/***************************************************************************
qgsalgorithmtransectbase.cpp
----------------------------
begin : September 2025
copyright : (C) 2025 by Loïc Bartoletti
email : loic dot bartoletti at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsalgorithmtransectbase.h"
#include "qgsmultilinestring.h"
#include "qgslinestring.h"
///@cond PRIVATE
QString QgsTransectAlgorithmBase::group() const
{
return QObject::tr( "Vector geometry" );
}
QString QgsTransectAlgorithmBase::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}
QStringList QgsTransectAlgorithmBase::tags() const
{
return QObject::tr( "transect,station,lines,extend" ).split( ',' );
}
QString QgsTransectAlgorithmBase::shortDescription() const
{
return QObject::tr( "Creates transects for (multi)linestrings." );
}
Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectAlgorithmBase::documentationFlags() const
{
return Qgis::ProcessingAlgorithmDocumentationFlag::RegeneratesPrimaryKey;
}
void QgsTransectAlgorithmBase::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
auto length = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), 5.0, QStringLiteral( "INPUT" ), false, 0 );
length->setIsDynamic( true );
length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( length.release() );
auto angle = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
angle->setIsDynamic( true );
angle->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
angle->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( angle.release() );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "SIDE" ), QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false ) );
// Allow subclasses to add their specific parameters
addAlgorithmParams();
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
}
QVariantMap QgsTransectAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
mOrientation = static_cast<QgsTransectAlgorithmBase::Side>( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) );
mAngle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
mDynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
if ( mDynamicAngle )
mAngleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value<QgsProperty>();
mLength = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
mDynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
if ( mDynamicLength )
mLengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value<QgsProperty>();
if ( mOrientation == QgsTransectAlgorithmBase::Both )
mLength /= 2.0;
// Let subclass prepare their specific parameters
if ( !prepareAlgorithmTransectParameters( parameters, context, feedback ) )
return QVariantMap();
std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
QgsFields newFields;
newFields.append( QgsField( QStringLiteral( "TR_FID" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_ID" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QMetaType::Type::Int, QString(), 20 ) );
newFields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QMetaType::Type::Double, QString(), 5, 2 ) );
newFields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QMetaType::Type::Double, QString(), 20, 6 ) );
newFields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QMetaType::Type::Int, QString(), 1 ) );
QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
Qgis::WkbType outputWkb = Qgis::WkbType::LineString;
if ( QgsWkbTypes::hasZ( source->wkbType() ) )
outputWkb = QgsWkbTypes::addZ( outputWkb );
if ( QgsWkbTypes::hasM( source->wkbType() ) )
outputWkb = QgsWkbTypes::addM( outputWkb );
QString dest;
std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsFeatureIterator features = source->getFeatures();
int current = -1;
int number = 0;
const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
QgsFeature feat;
while ( features.nextFeature( feat ) )
{
current++;
if ( feedback->isCanceled() )
{
break;
}
feedback->setProgress( current * step );
if ( !feat.hasGeometry() )
continue;
QgsGeometry inputGeometry = feat.geometry();
if ( mDynamicLength || mDynamicAngle )
{
expressionContext.setFeature( feat );
}
double evaluatedLength = mLength;
if ( mDynamicLength )
evaluatedLength = mLengthProperty.valueAsDouble( context.expressionContext(), mLength );
double evaluatedAngle = mAngle;
if ( mDynamicAngle )
evaluatedAngle = mAngleProperty.valueAsDouble( context.expressionContext(), mAngle );
inputGeometry.convertToMultiType();
const QgsMultiLineString *multiLine = static_cast<const QgsMultiLineString *>( inputGeometry.constGet() );
for ( int part = 0; part < multiLine->numGeometries(); ++part )
{
const QgsLineString *lineString = multiLine->lineStringN( part );
if ( !lineString )
continue;
QgsLineString line = *lineString;
// Let subclass generate sampling points using their specific strategy
std::vector<QgsPoint> samplingPoints = generateSamplingPoints( line, parameters, context );
for ( int i = 0; i < static_cast<int>( samplingPoints.size() ); ++i )
{
const QgsPoint &pt = samplingPoints[i];
// Let subclass calculate azimuth using their specific method
double azimuth = calculateAzimuth( line, pt, i );
QgsFeature outFeat;
QgsAttributes attrs = feat.attributes();
attrs << current << number << i + 1 << evaluatedAngle
<< ( ( mOrientation == QgsTransectAlgorithmBase::Both ) ? evaluatedLength * 2 : evaluatedLength )
<< static_cast<int>( mOrientation );
outFeat.setAttributes( attrs );
outFeat.setGeometry( calcTransect( pt, azimuth, evaluatedLength, mOrientation, evaluatedAngle ) );
if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
number++;
}
}
}
sink->finalize();
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}
QgsGeometry QgsTransectAlgorithmBase::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithmBase::Side orientation, const double angle )
{
QgsPoint pLeft; // left point of the line
QgsPoint pRight; // right point of the line
QgsPolyline line;
if ( ( orientation == QgsTransectAlgorithmBase::Right ) || ( orientation == QgsTransectAlgorithmBase::Both ) )
{
pLeft = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
if ( orientation != QgsTransectAlgorithmBase::Both )
pRight = point;
}
if ( ( orientation == QgsTransectAlgorithmBase::Left ) || ( orientation == QgsTransectAlgorithmBase::Both ) )
{
pRight = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
if ( orientation != QgsTransectAlgorithmBase::Both )
pLeft = point;
}
line.append( pLeft );
line.append( pRight );
return QgsGeometry::fromPolyline( line );
}
///@endcond

View File

@ -0,0 +1,97 @@
/***************************************************************************
qgsalgorithmtransectbase.h
-------------------------
begin : September 2025
copyright : (C) 2025 by Loïc Bartoletti
email : loic dot bartoletti at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSALGORITHMTRANSECTBASE_H
#define QGSALGORITHMTRANSECTBASE_H
#define SIP_NO_FILE
#include "qgis_sip.h"
#include "qgsprocessingalgorithm.h"
///@cond PRIVATE
/**
* Base class for transect algorithms.
*/
class QgsTransectAlgorithmBase : public QgsProcessingAlgorithm
{
public:
/**
* Draw the transect on which side of the line
*/
enum Side
{
Left,
Right,
Both
};
QString group() const final;
QString groupId() const final;
QStringList tags() const override;
QString shortDescription() const override;
Qgis::ProcessingAlgorithmDocumentationFlags documentationFlags() const final;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) final;
protected:
/**
* Adds specific subclass algorithm parameters. The common parameters (INPUT, LENGTH, ANGLE, SIDE, OUTPUT)
* are automatically added by the base class.
*/
virtual void addAlgorithmParams() = 0;
/**
* Prepares the transect algorithm subclass for execution.
*/
virtual bool prepareAlgorithmTransectParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0;
/**
* Processes a line geometry using the specific sampling strategy implemented in subclasses.
*/
QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) final;
/**
* Pure virtual method that generates sampling points along a line geometry.
* Subclasses implement their specific sampling strategy here.
*/
virtual std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap &parameters, QgsProcessingContext &context ) = 0;
/**
* Calculate the azimuth at a given point for transect orientation.
* Subclasses can override this if they need different azimuth calculation.
*/
virtual double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) = 0;
/**
* Returns the transect geometry at the specified point.
*/
static QgsGeometry calcTransect( const QgsPoint &point, double angleAtVertex, double length, Side orientation, double angle );
// Shared member variables accessible to subclasses
Side mOrientation = Both;
double mAngle = 90.0;
double mLength = 5.0;
bool mDynamicAngle = false;
bool mDynamicLength = false;
QgsProperty mAngleProperty;
QgsProperty mLengthProperty;
};
///@endcond PRIVATE
#endif // QGSALGORITHMTRANSECTBASE_H

View File

@ -0,0 +1,99 @@
/***************************************************************************
qgsalgorithmtransectfixeddistance.cpp
-------------------------------------
begin : September 2025
copyright : (C) 2025 by Loïc Bartoletti
email : loic dot bartoletti at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsalgorithmtransectfixeddistance.h"
#include "qgsmultilinestring.h"
#include "qgslinestring.h"
///@cond PRIVATE
QString QgsTransectFixedDistanceAlgorithm::name() const
{
return QStringLiteral( "transectfixeddistance" );
}
QString QgsTransectFixedDistanceAlgorithm::displayName() const
{
return QObject::tr( "Transect (fixed distance)" );
}
QStringList QgsTransectFixedDistanceAlgorithm::tags() const
{
return QObject::tr( "transect,station,lines,extend,fixed,interval,distance" ).split( ',' );
}
QString QgsTransectFixedDistanceAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm creates transects at fixed distance intervals along (multi)linestrings.\n" )
+ QObject::tr( "A transect is a line oriented from an angle (by default perpendicular) to the input polylines at regular intervals." )
+ QStringLiteral( "\n\n" )
+ QObject::tr( "Field(s) from feature(s) are returned in the transect with these new fields:\n" )
+ QObject::tr( "- TR_FID: ID of the original feature\n" )
+ QObject::tr( "- TR_ID: ID of the transect. Each transect have an unique ID\n" )
+ QObject::tr( "- TR_SEGMENT: ID of the segment of the linestring\n" )
+ QObject::tr( "- TR_ANGLE: Angle in degrees from the original line at the vertex\n" )
+ QObject::tr( "- TR_LENGTH: Total length of the transect returned\n" )
+ QObject::tr( "- TR_ORIENT: Side of the transect (only on the left or right of the line, or both side)\n" );
}
QString QgsTransectFixedDistanceAlgorithm::shortDescription() const
{
return QObject::tr( "Creates transects at fixed distance intervals along (multi)linestrings." );
}
QgsTransectFixedDistanceAlgorithm *QgsTransectFixedDistanceAlgorithm::createInstance() const
{
return new QgsTransectFixedDistanceAlgorithm();
}
void QgsTransectFixedDistanceAlgorithm::addAlgorithmParams()
{
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "INTERVAL" ), QObject::tr( "Fixed sampling interval" ), Qgis::ProcessingNumberParameterType::Double, 10.0, false, 0 ) );
}
bool QgsTransectFixedDistanceAlgorithm::prepareAlgorithmTransectParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mInterval = parameterAsDouble( parameters, QStringLiteral( "INTERVAL" ), context );
return true;
}
std::vector<QgsPoint> QgsTransectFixedDistanceAlgorithm::generateSamplingPoints( const QgsLineString &line, const QVariantMap &, QgsProcessingContext & )
{
std::vector<QgsPoint> samplingPoints;
// Sample points at fixed intervals
double totalLength = line.length();
for ( double d = 0; d <= totalLength; d += mInterval )
{
QgsPoint *pt = line.interpolatePoint( d );
samplingPoints.push_back( *pt );
}
return samplingPoints;
}
double QgsTransectFixedDistanceAlgorithm::calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int )
{
// For fixed distance sampling, find closest segment
QgsPoint segPt;
QgsVertexId vid;
line.closestSegment( point, segPt, vid, nullptr, Qgis::DEFAULT_SEGMENT_EPSILON );
QgsVertexId prev( vid.part, vid.ring, vid.vertex - 1 );
return line.vertexAt( prev ).azimuth( line.vertexAt( vid ) ) * M_PI / 180.0;
}
///@endcond

View File

@ -0,0 +1,54 @@
/***************************************************************************
qgsalgorithmtransectfixeddistance.h
------------------------------------
begin : September 2025
copyright : (C) 2025 by Loïc Bartoletti
email : loic dot bartoletti at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSALGORITHMTRANSECTFIXEDDISTANCE_H
#define QGSALGORITHMTRANSECTFIXEDDISTANCE_H
#define SIP_NO_FILE
#include "qgis_sip.h"
#include "qgsalgorithmtransectbase.h"
///@cond PRIVATE
/**
* Native transect (fixed distance) algorithm.
*/
class QgsTransectFixedDistanceAlgorithm : public QgsTransectAlgorithmBase
{
public:
QgsTransectFixedDistanceAlgorithm() = default;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString shortHelpString() const override;
QString shortDescription() const override;
QgsTransectFixedDistanceAlgorithm *createInstance() const override SIP_FACTORY;
protected:
void addAlgorithmParams() override;
bool prepareAlgorithmTransectParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap &parameters, QgsProcessingContext &context ) override;
double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) override;
private:
double mInterval = 10.0;
};
///@endcond PRIVATE
#endif // QGSALGORITHMTRANSECTFIXEDDISTANCE_H

View File

@ -271,6 +271,7 @@
#include "qgsalgorithmtaperedbuffer.h" #include "qgsalgorithmtaperedbuffer.h"
#include "qgsalgorithmtinmeshcreation.h" #include "qgsalgorithmtinmeshcreation.h"
#include "qgsalgorithmtransect.h" #include "qgsalgorithmtransect.h"
#include "qgsalgorithmtransectfixeddistance.h"
#include "qgsalgorithmtransform.h" #include "qgsalgorithmtransform.h"
#include "qgsalgorithmtranslate.h" #include "qgsalgorithmtranslate.h"
#include "qgsalgorithmtruncatetable.h" #include "qgsalgorithmtruncatetable.h"
@ -636,6 +637,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsTaperedBufferAlgorithm() ); addAlgorithm( new QgsTaperedBufferAlgorithm() );
addAlgorithm( new QgsTinMeshCreationAlgorithm() ); addAlgorithm( new QgsTinMeshCreationAlgorithm() );
addAlgorithm( new QgsTransectAlgorithm() ); addAlgorithm( new QgsTransectAlgorithm() );
addAlgorithm( new QgsTransectFixedDistanceAlgorithm() );
addAlgorithm( new QgsTransferAnnotationsFromMainAlgorithm() ); addAlgorithm( new QgsTransferAnnotationsFromMainAlgorithm() );
addAlgorithm( new QgsTransformAlgorithm() ); addAlgorithm( new QgsTransformAlgorithm() );
addAlgorithm( new QgsTranslateAlgorithm() ); addAlgorithm( new QgsTranslateAlgorithm() );

View File

@ -0,0 +1,199 @@
/***************************************************************************
qgsannotationlayer3drendererwidget.cpp
------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsannotationlayer3drendererwidget.h"
#include "moc_qgsannotationlayer3drendererwidget.cpp"
#include "qgsapplication.h"
#include "qgsannotationlayer.h"
#include "qgsannotationlayer3drenderer.h"
#include <QBoxLayout>
#include <QCheckBox>
QgsAnnotationLayer3DRendererWidget::QgsAnnotationLayer3DRendererWidget( QgsAnnotationLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
: QgsMapLayerConfigWidget( layer, canvas, parent )
{
setPanelTitle( tr( "3D View" ) );
setObjectName( QStringLiteral( "mOptsPage_3DView" ) );
setupUi( this );
mComboRendererType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "rendererNullSymbol.svg" ) ), tr( "No Symbols" ), QVariant::fromValue( RendererType::None ) );
mComboRendererType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "rendererSingleSymbol.svg" ) ), tr( "3D Billboards" ), QVariant::fromValue( RendererType::Billboards ) );
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::None ) ) );
mStackedWidget->setCurrentWidget( mPageNoRenderer );
connect( mComboRendererType, qOverload< int >( &QComboBox::currentIndexChanged ), this, &QgsAnnotationLayer3DRendererWidget::rendererTypeChanged );
mComboClamping->addItem( tr( "Relative to Terrain" ), QVariant::fromValue( Qgis::AltitudeClamping::Relative ) );
mComboClamping->addItem( tr( "Absolute" ), QVariant::fromValue( Qgis::AltitudeClamping::Absolute ) );
connect( mComboClamping, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsAnnotationLayer3DRendererWidget::clampingChanged );
mComboClamping->setCurrentIndex( mComboClamping->findData( QVariant::fromValue( Qgis::AltitudeClamping::Relative ) ) );
clampingChanged();
connect( mOffsetZSpinBox, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [this]( double ) {
if ( !mBlockChanges )
emit widgetChanged();
} );
connect( mCheckShowCallouts, &QCheckBox::toggled, this, [this] {
if ( !mBlockChanges )
emit widgetChanged();
} );
syncToLayer( layer );
}
void QgsAnnotationLayer3DRendererWidget::setRenderer( const QgsAnnotationLayer3DRenderer *renderer )
{
mBlockChanges++;
mRenderer.reset( renderer ? renderer->clone() : nullptr );
if ( renderer )
{
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::Billboards ) ) );
mComboClamping->setCurrentIndex( mComboClamping->findData( QVariant::fromValue( renderer->altitudeClamping() ) ) );
mOffsetZSpinBox->setValue( renderer->zOffset() );
mCheckShowCallouts->setChecked( renderer->showCalloutLines() );
}
else
{
mComboRendererType->setCurrentIndex( mComboRendererType->findData( QVariant::fromValue( RendererType::None ) ) );
}
mBlockChanges--;
}
std::unique_ptr< QgsAnnotationLayer3DRenderer > QgsAnnotationLayer3DRendererWidget::renderer()
{
const RendererType type = mComboRendererType->currentData().value< RendererType >();
switch ( type )
{
case RendererType::None:
return nullptr;
break;
case RendererType::Billboards:
{
auto renderer = std::make_unique< QgsAnnotationLayer3DRenderer >();
renderer->setAltitudeClamping( mComboClamping->currentData().value< Qgis::AltitudeClamping >() );
renderer->setZOffset( mOffsetZSpinBox->value() );
renderer->setShowCalloutLines( mCheckShowCallouts->isChecked() );
return renderer;
}
}
BUILTIN_UNREACHABLE
}
void QgsAnnotationLayer3DRendererWidget::apply()
{
std::unique_ptr< QgsAnnotationLayer3DRenderer > r = renderer();
mLayer->setRenderer3D( r.release() );
}
void QgsAnnotationLayer3DRendererWidget::rendererTypeChanged()
{
const RendererType type = mComboRendererType->currentData().value< RendererType >();
switch ( type )
{
case RendererType::None:
mStackedWidget->setCurrentWidget( mPageNoRenderer );
break;
case RendererType::Billboards:
mStackedWidget->setCurrentWidget( mPageBillboards );
break;
}
if ( !mBlockChanges )
emit widgetChanged();
}
void QgsAnnotationLayer3DRendererWidget::clampingChanged()
{
switch ( mComboClamping->currentData().value< Qgis::AltitudeClamping >() )
{
case Qgis::AltitudeClamping::Absolute:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg( tr( "All billboards will be placed at the same elevation." ), tr( "The terrain height will be ignored." ) )
);
break;
case Qgis::AltitudeClamping::Relative:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p>" ).arg( tr( "Billboard elevation is relative to terrain height." ) )
);
break;
case Qgis::AltitudeClamping::Terrain:
mLabelClampingExplanation->setText(
QStringLiteral( "<p><b>%1</b></p><p>%2</p>" ).arg( tr( "Billboard elevation will be taken directly from the terrain height." ), tr( "Billboards will be placed directly on the terrain." ) )
);
break;
}
if ( !mBlockChanges )
emit widgetChanged();
}
void QgsAnnotationLayer3DRendererWidget::syncToLayer( QgsMapLayer *layer )
{
mLayer = layer;
QgsAbstract3DRenderer *r = layer->renderer3D();
if ( r && r->type() == QLatin1String( "annotation" ) )
{
QgsAnnotationLayer3DRenderer *annotationRenderer = qgis::down_cast<QgsAnnotationLayer3DRenderer *>( r );
setRenderer( annotationRenderer );
}
else
{
setRenderer( nullptr );
}
}
//
// QgsAnnotationLayer3DRendererWidgetFactory
//
QgsAnnotationLayer3DRendererWidgetFactory::QgsAnnotationLayer3DRendererWidgetFactory( QObject *parent )
: QObject( parent )
{
setIcon( QIcon( ":/images/themes/default/3d.svg" ) );
setTitle( tr( "3D View" ) );
}
QgsMapLayerConfigWidget *QgsAnnotationLayer3DRendererWidgetFactory::createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const
{
Q_UNUSED( dockWidget )
QgsAnnotationLayer *annotationLayer = qobject_cast<QgsAnnotationLayer *>( layer );
if ( !annotationLayer )
return nullptr;
return new QgsAnnotationLayer3DRendererWidget( annotationLayer, canvas, parent );
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportLayerPropertiesDialog() const
{
return true;
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportsStyleDock() const
{
return true;
}
bool QgsAnnotationLayer3DRendererWidgetFactory::supportsLayer( QgsMapLayer *layer ) const
{
return layer->type() == Qgis::LayerType::Annotation;
}
QString QgsAnnotationLayer3DRendererWidgetFactory::layerPropertiesPagePositionHint() const
{
return QStringLiteral( "mOptsPage_Rendering" );
}

View File

@ -0,0 +1,75 @@
/***************************************************************************
qgsannotationlayer3drendererwidget.h
------------------------------
Date : September 2025
Copyright : (C) 2025 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSANNOTATIONLAYER3DRENDERERWIDGET_H
#define QGSANNOTATIONLAYER3DRENDERERWIDGET_H
#include <memory>
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerconfigwidgetfactory.h"
#include "ui_qgsannotationlayer3drendererwidget.h"
class QgsAnnotationLayer;
class QgsAnnotationLayer3DRenderer;
class QgsMapCanvas;
//! Widget for configuration of 3D renderer of an annotation layer
class QgsAnnotationLayer3DRendererWidget : public QgsMapLayerConfigWidget, private Ui::QgsAnnotationLayer3dRendererWidgetBase
{
Q_OBJECT
public:
enum class RendererType
{
None,
Billboards,
};
Q_ENUM( RendererType )
explicit QgsAnnotationLayer3DRendererWidget( QgsAnnotationLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr );
void syncToLayer( QgsMapLayer *layer ) final;
//! no transfer of ownership
void setRenderer( const QgsAnnotationLayer3DRenderer *renderer );
std::unique_ptr< QgsAnnotationLayer3DRenderer > renderer();
public slots:
void apply() override;
private slots:
void rendererTypeChanged();
void clampingChanged();
private:
int mBlockChanges = 0;
std::unique_ptr<QgsAnnotationLayer3DRenderer> mRenderer;
};
class QgsAnnotationLayer3DRendererWidgetFactory : public QObject, public QgsMapLayerConfigWidgetFactory
{
Q_OBJECT
public:
explicit QgsAnnotationLayer3DRendererWidgetFactory( QObject *parent = nullptr );
QgsMapLayerConfigWidget *createWidget( QgsMapLayer *layer, QgsMapCanvas *canvas, bool dockWidget, QWidget *parent ) const override;
bool supportLayerPropertiesDialog() const override;
bool supportsStyleDock() const override;
bool supportsLayer( QgsMapLayer *layer ) const override;
QString layerPropertiesPagePositionHint() const override;
};
#endif // QGSANNOTATIONLAYER3DRENDERERWIDGET_H

View File

@ -353,6 +353,7 @@ if (WITH_3D)
3d/qgs3dmodelsourcelineedit.cpp 3d/qgs3dmodelsourcelineedit.cpp
3d/qgs3dnavigationwidget.cpp 3d/qgs3dnavigationwidget.cpp
3d/qgs3doptions.cpp 3d/qgs3doptions.cpp
3d/qgsannotationlayer3drendererwidget.cpp
3d/qgsgoochmaterialwidget.cpp 3d/qgsgoochmaterialwidget.cpp
3d/qgslightswidget.cpp 3d/qgslightswidget.cpp
3d/qgsline3dsymbolwidget.cpp 3d/qgsline3dsymbolwidget.cpp

View File

@ -327,11 +327,14 @@ void QgsAppLayerHandling::postProcessAddedLayers( const QList<QgsMapLayer *> &la
} }
break; break;
} }
case Qgis::LayerType::Annotation:
postProcessAddedLayer( layer );
break;
case Qgis::LayerType::Raster: case Qgis::LayerType::Raster:
case Qgis::LayerType::Plugin: case Qgis::LayerType::Plugin:
case Qgis::LayerType::Mesh: case Qgis::LayerType::Mesh:
case Qgis::LayerType::VectorTile: case Qgis::LayerType::VectorTile:
case Qgis::LayerType::Annotation:
case Qgis::LayerType::PointCloud: case Qgis::LayerType::PointCloud:
case Qgis::LayerType::Group: case Qgis::LayerType::Group:
case Qgis::LayerType::TiledScene: case Qgis::LayerType::TiledScene:

View File

@ -156,6 +156,7 @@
#include "qgsmeshlayer3drendererwidget.h" #include "qgsmeshlayer3drendererwidget.h"
#include "qgspointcloudlayer3drendererwidget.h" #include "qgspointcloudlayer3drendererwidget.h"
#include "qgstiledscenelayer3drendererwidget.h" #include "qgstiledscenelayer3drendererwidget.h"
#include "qgsannotationlayer3drendererwidget.h"
#include "qgs3dapputils.h" #include "qgs3dapputils.h"
#include "qgs3doptions.h" #include "qgs3doptions.h"
#include "qgsmapviewsmanager.h" #include "qgsmapviewsmanager.h"
@ -1329,12 +1330,6 @@ QgisApp::QgisApp( QSplashScreen *splash, AppOptions options, const QString &root
registerMapLayerPropertiesFactory( new QgsVectorLayerDigitizingPropertiesFactory( this ) ); registerMapLayerPropertiesFactory( new QgsVectorLayerDigitizingPropertiesFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudRendererWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsPointCloudRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneRendererWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsTiledSceneRendererWidgetFactory( this ) );
#ifdef HAVE_3D
registerMapLayerPropertiesFactory( new QgsVectorLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsMeshLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneLayer3DRendererWidgetFactory( this ) );
#endif
registerMapLayerPropertiesFactory( new QgsPointCloudElevationPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsPointCloudElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsRasterElevationPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsRasterElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsVectorElevationPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsVectorElevationPropertiesWidgetFactory( this ) );
@ -1342,6 +1337,13 @@ QgisApp::QgisApp( QSplashScreen *splash, AppOptions options, const QString &root
registerMapLayerPropertiesFactory( new QgsMeshElevationPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsMeshElevationPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationItemPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsAnnotationItemPropertiesWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsLayerTreeGroupPropertiesWidgetFactory( this ) ); registerMapLayerPropertiesFactory( new QgsLayerTreeGroupPropertiesWidgetFactory( this ) );
#ifdef HAVE_3D
registerMapLayerPropertiesFactory( new QgsVectorLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsMeshLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsPointCloudLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsTiledSceneLayer3DRendererWidgetFactory( this ) );
registerMapLayerPropertiesFactory( new QgsAnnotationLayer3DRendererWidgetFactory( this ) );
#endif
mMapStyleWidget = new QgsLayerStylingWidget( mMapCanvas, mInfoBar, mMapLayerPanelFactories ); mMapStyleWidget = new QgsLayerStylingWidget( mMapCanvas, mInfoBar, mMapLayerPanelFactories );
mMapStylingDock->setWidget( mMapStyleWidget ); mMapStylingDock->setWidget( mMapStyleWidget );
@ -3247,7 +3249,7 @@ void QgisApp::showStyleManager()
void QgisApp::initPythonConsoleOptions() void QgisApp::initPythonConsoleOptions()
{ {
QgsPythonRunner::run( QStringLiteral( "import console" ) ); QgsPythonRunner::run( QStringLiteral( "import console" ) );
QgsPythonRunner::run( QStringLiteral( "console.init_options_widget()" ) ); QgsPythonRunner::run( QStringLiteral( "console.init_console()" ) );
} }
void QgisApp::showPythonDialog() void QgisApp::showPythonDialog()
@ -7953,6 +7955,7 @@ void QgisApp::createAnnotationLayer()
// layer should be created at top of layer tree // layer should be created at top of layer tree
QgsProject::instance()->addMapLayer( layer, false ); QgsProject::instance()->addMapLayer( layer, false );
QgsProject::instance()->layerTreeRoot()->insertLayer( 0, layer ); QgsProject::instance()->layerTreeRoot()->insertLayer( 0, layer );
QgsAppLayerHandling::postProcessAddedLayers( { layer } );
} }
void QgisApp::setCadDockVisible( bool visible ) void QgisApp::setCadDockVisible( bool visible )

View File

@ -157,7 +157,7 @@ void QgsAuthPlanetaryComputerEdit::updateServerType( int indx )
lblTenantId->setVisible( isPro ); lblTenantId->setVisible( isPro );
leTenantId->setVisible( isPro ); leTenantId->setVisible( isPro );
const QString openHelp = tr( "Use this server type for %1 - the data are publicly accessible and do not require an account." ).arg( QStringLiteral( "<a href=\"https://planetarycomputer.microsoft.com/\">https://planetarycomputer.microsoft.com/</a>" ) ); const QString openHelp = tr( "Use this server type for %1 - the data are publicly accessible and do not require an account." ).arg( QLatin1String( "<a href=\"https://planetarycomputer.microsoft.com/\">https://planetarycomputer.microsoft.com/</a>" ) );
const QString proHelp = tr( const QString proHelp = tr(
"Use this server type for <a href=\"https://learn.microsoft.com/en-us/azure/planetary-computer/get-started-planetary-computer\">Planetary Computer Pro</a> instances.<br/>" "Use this server type for <a href=\"https://learn.microsoft.com/en-us/azure/planetary-computer/get-started-planetary-computer\">Planetary Computer Pro</a> instances.<br/>"
"The Directory (tenant) and Application (client) IDs can be found in your organization's Microsoft Entra ID main and application page respectively.<br/>" "The Directory (tenant) and Application (client) IDs can be found in your organization's Microsoft Entra ID main and application page respectively.<br/>"

View File

@ -241,6 +241,7 @@ class CORE_EXPORT QgsAnnotationLayer : public QgsMapLayer
QgsMapLayerRef mLinkedLayer; QgsMapLayerRef mLinkedLayer;
friend class QgsAnnotationLayerRenderer; friend class QgsAnnotationLayerRenderer;
friend class QgsAnnotationLayerChunkLoader;
}; };

View File

@ -4627,7 +4627,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
// insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon // insert \a result geometry (obtain by the chamfer/fillet operation) back into original \a inputPoly polygon
auto updatePolygon = []( const QgsPolygon * inputPoly, QgsAbstractGeometry * result, int modifiedRing ) -> std::unique_ptr<QgsPolygon> auto updatePolygon = []( const QgsPolygon * inputPoly, QgsAbstractGeometry * result, int modifiedRing ) -> std::unique_ptr<QgsPolygon>
{ {
std::unique_ptr<QgsPolygon> newPoly = std::make_unique<QgsPolygon>(); auto newPoly = std::make_unique<QgsPolygon>();
for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex ) for ( int ringIndex = 0; ringIndex < inputPoly->numInteriorRings() + 1; ++ringIndex )
{ {
if ( ringIndex == modifiedRing ) if ( ringIndex == modifiedRing )
@ -4656,7 +4656,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
{ {
if ( modifiedPart >= 0 ) if ( modifiedPart >= 0 )
{ {
std::unique_ptr<QgsMultiLineString> newMultiLine = std::make_unique<QgsMultiLineString>(); auto newMultiLine = std::make_unique<QgsMultiLineString>();
int partIndex = 0; int partIndex = 0;
for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte ) for ( QgsMultiLineString::part_iterator partIte = inputMultiLine->parts_begin(); partIte != inputMultiLine->parts_end(); ++partIte )
{ {
@ -4686,7 +4686,7 @@ QgsGeometry QgsGeometry::doChamferFillet( ChamferFilletOperationType op, int ver
// geomType == Qgis::GeometryType::Polygon // geomType == Qgis::GeometryType::Polygon
if ( modifiedPart >= 0 ) if ( modifiedPart >= 0 )
{ {
std::unique_ptr<QgsMultiPolygon> newMultiPoly = std::make_unique<QgsMultiPolygon>(); auto newMultiPoly = std::make_unique<QgsMultiPolygon>();
int partIndex = 0; int partIndex = 0;
for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte ) for ( QgsAbstractGeometry::part_iterator partIte = inputMultiPoly->parts_begin(); partIte != inputMultiPoly->parts_end(); ++partIte )
{ {

View File

@ -972,7 +972,7 @@ bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement
QDomElement sourceElement = sourcesElement.firstChildElement(); QDomElement sourceElement = sourcesElement.firstChildElement();
while ( !sourceElement.isNull() ) while ( !sourceElement.isNull() )
{ {
if ( sourceElement.tagName() == QStringLiteral( "profileCustomSource" ) ) if ( sourceElement.tagName() == QLatin1String( "profileCustomSource" ) )
{ {
const QString sourceId = sourceElement.attribute( QStringLiteral( "id" ) ); const QString sourceId = sourceElement.attribute( QStringLiteral( "id" ) );
if ( QgsAbstractProfileSource *profileSource = QgsApplication::profileSourceRegistry()->findSourceById( sourceId ) ) if ( QgsAbstractProfileSource *profileSource = QgsApplication::profileSourceRegistry()->findSourceById( sourceId ) )
@ -980,7 +980,7 @@ bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement
mSources.append( profileSource ); mSources.append( profileSource );
} }
} }
else if ( sourceElement.tagName() == QStringLiteral( "profileLayerSource" ) ) else if ( sourceElement.tagName() == QLatin1String( "profileLayerSource" ) )
{ {
QgsMapLayerRef ref; QgsMapLayerRef ref;
ref.readXml( sourceElement, context ); ref.readXml( sourceElement, context );

View File

@ -403,7 +403,7 @@ inline std::unique_ptr<Chain> Problem::chain( int seed )
{ {
// A lot of conflict : make them inactive and store chain // A lot of conflict : make them inactive and store chain
std::unique_ptr< Chain > newChain = std::make_unique< Chain >(); auto newChain = std::make_unique< Chain >();
newChain->degree = currentChain.size() + 1 + conflicts.size(); newChain->degree = currentChain.size() + 1 + conflicts.size();
newChain->feat.resize( newChain->degree ); newChain->feat.resize( newChain->degree );
newChain->label.resize( newChain->degree ); newChain->label.resize( newChain->degree );

View File

@ -44,7 +44,7 @@ bool QgsVectorLayerXyPlotDataGatherer::run()
gatheredSeries.reserve( mSeriesDetails.size() ); gatheredSeries.reserve( mSeriesDetails.size() );
for ( int i = 0; i < mSeriesDetails.size(); i++ ) for ( int i = 0; i < mSeriesDetails.size(); i++ )
{ {
std::unique_ptr<QgsXyPlotSeries> series = std::make_unique<QgsXyPlotSeries>(); auto series = std::make_unique<QgsXyPlotSeries>();
gatheredSeries.emplace_back( std::move( series ) ); gatheredSeries.emplace_back( std::move( series ) );
gatheredSeriesCategoriesSum << QMap<QString, double>(); gatheredSeriesCategoriesSum << QMap<QString, double>();
} }

View File

@ -663,11 +663,11 @@ void QgsProject::registerTranslatableObjects( QgsTranslationContext *translation
translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName ); translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) ) if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
{ {
translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() ); translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
} }
if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueMap" ) ) if ( field.editorWidgetSetup().type() == QLatin1String( "ValueMap" ) )
{ {
if ( field.editorWidgetSetup().config().value( QStringLiteral( "map" ) ).canConvert<QList<QVariant>>() ) if ( field.editorWidgetSetup().config().value( QStringLiteral( "map" ) ).canConvert<QList<QVariant>>() )
{ {
@ -4003,11 +4003,11 @@ bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &pro
if ( e.isNull() ) if ( e.isNull() )
{ {
e = propertiesElem.firstChildElement( QStringLiteral( "properties" ) ); e = propertiesElem.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Paths" ) ) while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QLatin1String( "Paths" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) ); e = e.nextSiblingElement( QStringLiteral( "properties" ) );
e = e.firstChildElement( QStringLiteral( "properties" ) ); e = e.firstChildElement( QStringLiteral( "properties" ) );
while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QStringLiteral( "Absolute" ) ) while ( !e.isNull() && e.attribute( QStringLiteral( "name" ) ) != QLatin1String( "Absolute" ) )
e = e.nextSiblingElement( QStringLiteral( "properties" ) ); e = e.nextSiblingElement( QStringLiteral( "properties" ) );
} }
else else

View File

@ -365,7 +365,7 @@ bool QgsProjectPropertyKey::readXml( const QDomNode &keyNode )
const QDomNode subkey = subkeys.item( i ); const QDomNode subkey = subkeys.item( i );
QString name; QString name;
if ( subkey.nodeName() == QStringLiteral( "properties" ) && if ( subkey.nodeName() == QLatin1String( "properties" ) &&
subkey.hasAttributes() && // if we have attributes subkey.hasAttributes() && // if we have attributes
subkey.isElement() && // and we're an element subkey.isElement() && // and we're an element
subkey.toElement().hasAttribute( QStringLiteral( "name" ) ) ) // and we have a "name" attribute subkey.toElement().hasAttribute( QStringLiteral( "name" ) ) ) // and we have a "name" attribute

View File

@ -129,7 +129,7 @@ QString QgsCommandLineUtils::allVersions( )
versionString += QStringLiteral( "SFCGAL version %1\n" ).arg( sfcgalVersionCompiled ); versionString += QStringLiteral( "SFCGAL version %1\n" ).arg( sfcgalVersionCompiled );
} }
#else #else
versionString += QStringLiteral( "No support for SFCGAL\n" ); versionString += QLatin1String( "No support for SFCGAL\n" );
#endif #endif
// SQLite version // SQLite version

View File

@ -572,7 +572,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
{ {
case QMetaType::Type::QString: case QMetaType::Type::QString:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
if ( encoding ) if ( encoding )
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) ); value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
@ -641,7 +641,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::QStringList: case QMetaType::Type::QStringList:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
QStringList list; QStringList list;
char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex ); char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
@ -668,7 +668,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
{ {
case QMetaType::Type::QString: case QMetaType::Type::QString:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
QStringList list; QStringList list;
char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex ); char **lst = OGR_F_GetFieldAsStringList( ogrFet, attIndex );
@ -691,7 +691,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::Int: case QMetaType::Type::Int:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
QVariantList list; QVariantList list;
int count = 0; int count = 0;
@ -711,7 +711,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::Double: case QMetaType::Type::Double:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
QVariantList list; QVariantList list;
int count = 0; int count = 0;
@ -731,7 +731,7 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
case QMetaType::Type::LongLong: case QMetaType::Type::LongLong:
{ {
if ( field.typeName() != QStringLiteral( "JSON" ) || ! getJsonValue() ) if ( field.typeName() != QLatin1String( "JSON" ) || ! getJsonValue() )
{ {
QVariantList list; QVariantList list;
int count = 0; int count = 0;

View File

@ -3563,7 +3563,7 @@ bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
QMimeType mimeType; QMimeType mimeType;
QString base64data; QString base64data;
if ( mPath.startsWith( QStringLiteral( "base64:" ) ) ) if ( mPath.startsWith( QLatin1String( "base64:" ) ) )
{ {
base64data = mPath.mid( 7 ); base64data = mPath.mid( 7 );
} }
@ -3601,7 +3601,7 @@ bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement
extGraphElem.appendChild( onlineResElem ); extGraphElem.appendChild( onlineResElem );
// determine mime type // determine mime type
if ( mPath.startsWith( QStringLiteral( "http://" ) ) || mPath.startsWith( QStringLiteral( "https://" ) ) ) if ( mPath.startsWith( QLatin1String( "http://" ) ) || mPath.startsWith( QLatin1String( "https://" ) ) )
{ {
// Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
// but not generic ones, so let's hardcode to png. // but not generic ones, so let's hardcode to png.

View File

@ -2708,11 +2708,11 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes
const QDomElement optionsElem = cfgElem.childNodes().at( 0 ).toElement(); const QDomElement optionsElem = cfgElem.childNodes().at( 0 ).toElement();
QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap(); QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
// translate widget configuration strings // translate widget configuration strings
if ( widgetType == QStringLiteral( "ValueRelation" ) ) if ( widgetType == QLatin1String( "ValueRelation" ) )
{ {
optionsMap[ QStringLiteral( "Value" ) ] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( layerNode.namedItem( QStringLiteral( "id" ) ).toElement().text(), fieldName ), optionsMap[ QStringLiteral( "Value" ) ].toString() ); optionsMap[ QStringLiteral( "Value" ) ] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( layerNode.namedItem( QStringLiteral( "id" ) ).toElement().text(), fieldName ), optionsMap[ QStringLiteral( "Value" ) ].toString() );
} }
if ( widgetType == QStringLiteral( "ValueMap" ) ) if ( widgetType == QLatin1String( "ValueMap" ) )
{ {
if ( optionsMap[ QStringLiteral( "map" ) ].canConvert<QList<QVariant>>() ) if ( optionsMap[ QStringLiteral( "map" ) ].canConvert<QList<QVariant>>() )
{ {

View File

@ -344,7 +344,7 @@ void QgsVectorTileUtils::loadSprites( const QVariantMap &styleDefinition, QgsMap
{ {
return sprite; return sprite;
} }
else if ( sprite.startsWith( QLatin1String( "/" ) ) ) else if ( sprite.startsWith( QLatin1Char( '/' ) ) )
{ {
const QUrl url( styleUrl ); const QUrl url( styleUrl );
return QStringLiteral( "%1://%2%3" ).arg( url.scheme(), url.host(), sprite ); return QStringLiteral( "%1://%2%3" ).arg( url.scheme(), url.host(), sprite );

View File

@ -25,6 +25,7 @@
#include "qgsstringutils.h" #include "qgsstringutils.h"
#include "qgsfontutils.h" #include "qgsfontutils.h"
#include "qgssettingsentryimpl.h" #include "qgssettingsentryimpl.h"
#include "qgsshortcutsmanager.h"
#include <QLabel> #include <QLabel>
#include <QWidget> #include <QWidget>
@ -252,25 +253,30 @@ void QgsCodeEditor::keyPressEvent( QKeyEvent *event )
} }
} }
const bool ctrlModifier = event->modifiers() & Qt::ControlModifier; // check for reformat code sequence
const bool altModifier = event->modifiers() & Qt::AltModifier;
// Ctrl+Alt+F: reformat code
const bool canReformat = languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat; const bool canReformat = languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat;
if ( !isReadOnly() && canReformat && ctrlModifier && altModifier && event->key() == Qt::Key_F ) if ( !isReadOnly() && canReformat )
{ {
event->accept(); const QKeySequence reformatCodeSequence = QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeReformat );
reformatCode(); if ( !reformatCodeSequence.isEmpty() && reformatCodeSequence.matches( event->key() | event->modifiers() ) )
return; {
event->accept();
reformatCode();
return;
}
} }
// Toggle comment when user presses Ctrl+: // Check for toggle comment sequence
const bool canToggle = languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment; const bool canToggle = languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment;
if ( !isReadOnly() && canToggle && ctrlModifier && event->key() == Qt::Key_Colon ) if ( !isReadOnly() && canToggle )
{ {
event->accept(); const QKeySequence toggleCommentCodeSequence = QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeToggleComment );
toggleComment(); if ( !toggleCommentCodeSequence.isEmpty() && toggleCommentCodeSequence.matches( event->key() | event->modifiers() ) )
return; {
event->accept();
toggleComment();
return;
}
} }
QsciScintilla::keyPressEvent( event ); QsciScintilla::keyPressEvent( event );
@ -299,7 +305,7 @@ void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat ) if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
{ {
QAction *reformatAction = new QAction( tr( "Reformat Code" ), menu ); QAction *reformatAction = new QAction( tr( "Reformat Code" ), menu );
reformatAction->setShortcut( QStringLiteral( "Ctrl+Alt+F" ) ); reformatAction->setShortcut( QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeReformat ) );
reformatAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconFormatCode.svg" ) ) ); reformatAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconFormatCode.svg" ) ) );
reformatAction->setEnabled( !isReadOnly() ); reformatAction->setEnabled( !isReadOnly() );
connect( reformatAction, &QAction::triggered, this, &QgsCodeEditor::reformatCode ); connect( reformatAction, &QAction::triggered, this, &QgsCodeEditor::reformatCode );
@ -317,7 +323,7 @@ void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment ) if ( languageCapabilities() & Qgis::ScriptLanguageCapability::ToggleComment )
{ {
QAction *toggleCommentAction = new QAction( tr( "Toggle Comment" ), menu ); QAction *toggleCommentAction = new QAction( tr( "Toggle Comment" ), menu );
toggleCommentAction->setShortcut( QStringLiteral( "Ctrl+:" ) ); toggleCommentAction->setShortcut( QgsGui::shortcutsManager()->sequenceForCommonAction( QgsShortcutsManager::CommonAction::CodeToggleComment ) );
toggleCommentAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconCommentEditorConsole.svg" ), palette().color( QPalette::ColorRole::WindowText ) ) ); toggleCommentAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconCommentEditorConsole.svg" ), palette().color( QPalette::ColorRole::WindowText ) ) );
toggleCommentAction->setEnabled( !isReadOnly() ); toggleCommentAction->setEnabled( !isReadOnly() );
connect( toggleCommentAction, &QAction::triggered, this, &QgsCodeEditor::toggleComment ); connect( toggleCommentAction, &QAction::triggered, this, &QgsCodeEditor::toggleComment );

View File

@ -491,7 +491,7 @@ void QgsElevationProfileLayerTreeView::populateInitialSources( QgsProject *proje
void QgsElevationProfileLayerTreeView::addNodeForRegisteredSource( const QString &sourceId, const QString &sourceName ) void QgsElevationProfileLayerTreeView::addNodeForRegisteredSource( const QString &sourceId, const QString &sourceName )
{ {
std::unique_ptr< QgsLayerTreeCustomNode > customNode = std::make_unique< QgsLayerTreeCustomNode >( sourceId, sourceName.isEmpty() ? sourceId : sourceName ); auto customNode = std::make_unique< QgsLayerTreeCustomNode >( sourceId, sourceName.isEmpty() ? sourceId : sourceName );
customNode->setItemVisibilityChecked( true ); customNode->setItemVisibilityChecked( true );
// Mark the node so that we know which custom nodes correspond to elevation profile sources // Mark the node so that we know which custom nodes correspond to elevation profile sources
customNode->setCustomProperty( QStringLiteral( "source" ), QgsElevationProfileLayerTreeView::CUSTOM_NODE_ELEVATION_PROFILE_SOURCE ); customNode->setCustomProperty( QStringLiteral( "source" ), QgsElevationProfileLayerTreeView::CUSTOM_NODE_ELEVATION_PROFILE_SOURCE );

View File

@ -578,7 +578,7 @@ QgsExpressionContext QgsLayoutElevationProfileWidget::createExpressionContext()
{ {
QgsExpressionContext context = mProfile->createExpressionContext(); QgsExpressionContext context = mProfile->createExpressionContext();
std::unique_ptr<QgsExpressionContextScope> plotScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "plot" ) ); auto plotScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "plot" ) );
plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis" ), QString(), true ) ); plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis" ), QString(), true ) );
plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis_value" ), 0.0, true ) ); plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis_value" ), 0.0, true ) );
context.appendScope( plotScope.release() ); context.appendScope( plotScope.release() );

View File

@ -44,12 +44,12 @@ QgsExpressionContext QgsPlotWidget::createExpressionContext() const
context.appendScope( QgsExpressionContextUtils::globalScope() ); context.appendScope( QgsExpressionContextUtils::globalScope() );
} }
std::unique_ptr<QgsExpressionContextScope> plotScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "plot" ) ); auto plotScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "plot" ) );
plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis" ), QString(), true ) ); plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis" ), QString(), true ) );
plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis_value" ), 0.0, true ) ); plotScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "plot_axis_value" ), 0.0, true ) );
context.appendScope( plotScope.release() ); context.appendScope( plotScope.release() );
std::unique_ptr<QgsExpressionContextScope> chartScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "chart" ) ); auto chartScope = std::make_unique<QgsExpressionContextScope>( QStringLiteral( "chart" ) );
chartScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "chart_category" ), QString(), true ) ); chartScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "chart_category" ), QString(), true ) );
chartScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "chart_value" ), 0.0, true ) ); chartScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "chart_value" ), 0.0, true ) );
context.appendScope( chartScope.release() ); context.appendScope( chartScope.release() );

View File

@ -17,15 +17,44 @@
#include "moc_qgsshortcutsmanager.cpp" #include "moc_qgsshortcutsmanager.cpp"
#include "qgslogger.h" #include "qgslogger.h"
#include "qgssettings.h" #include "qgssettings.h"
#include "qgsapplication.h"
#include <QShortcut> #include <QShortcut>
#include <QRegularExpression> #include <QRegularExpression>
#include <QWidgetAction> #include <QWidgetAction>
#include <QPalette>
QgsShortcutsManager::QgsShortcutsManager( QObject *parent, const QString &settingsRoot ) QgsShortcutsManager::QgsShortcutsManager( QObject *parent, const QString &settingsRoot )
: QObject( parent ) : QObject( parent )
, mSettingsPath( settingsRoot ) , mSettingsPath( settingsRoot )
{ {
// Register common actions
auto registerCommonAction = [this]( CommonAction commonAction, const QIcon &icon, const QString &text, const QString &toolTip, const QString &sequence, const QString &objectName, const QString &section ) {
QAction *action = new QAction( icon, text, this );
action->setToolTip( toolTip );
setObjectName( objectName );
// We do not want these actions to be enabled, they are just there to be able to change
// the shortcuts in the Shortcuts Manager.
action->setEnabled( false );
action->setProperty( "commonAction", static_cast< int >( commonAction ) );
registerAction( action, sequence, section );
mCommonActions.insert( static_cast< int >( commonAction ), action );
};
registerCommonAction( CommonAction::CodeToggleComment, QgsApplication::getThemeIcon( QStringLiteral( "console/iconCommentEditorConsole.svg" ), QgsApplication::palette().color( QPalette::ColorRole::WindowText ) ), tr( "Toggle Comment" ), tr( "Toggle comment" ), QStringLiteral( "Ctrl+/" ), QStringLiteral( "mEditorToggleComment" ), QStringLiteral( "Editor" ) );
registerCommonAction( CommonAction::CodeReformat, QgsApplication::getThemeIcon( QStringLiteral( "console/iconFormatCode.svg" ) ), tr( "Reformat Code" ), tr( "Reformat code" ), QStringLiteral( "Ctrl+Alt+F" ), QStringLiteral( "mEditorReformatCode" ), QStringLiteral( "Editor" ) );
registerCommonAction( CommonAction::CodeRunScript, QgsApplication::getThemeIcon( QStringLiteral( "mActionStart.svg" ) ), tr( "Run Script" ), tr( "Run entire script" ), QStringLiteral( "Ctrl+Shift+E" ), QStringLiteral( "mEditorRunScript" ), QStringLiteral( "Editor" ) );
registerCommonAction( CommonAction::CodeRunSelection, QgsApplication::getThemeIcon( QStringLiteral( "mActionRunSelected.svg" ) ), tr( "Run Selection" ), tr( "Run selected part of script" ), QStringLiteral( "Ctrl+E" ), QStringLiteral( "mEditorRunSelection" ), QStringLiteral( "Editor" ) );
}
QgsShortcutsManager::~QgsShortcutsManager()
{
// delete all common actions BEFORE this object is destroyed -- they have a lambda connection which
// we do NOT want to be triggered during the qt child object cleanup which will occur after this destructor
const QHash< int, QAction * > commonActionsToCleanup = std::move( mCommonActions );
for ( auto it = commonActionsToCleanup.constBegin(); it != commonActionsToCleanup.constEnd(); ++it )
{
delete it.value();
}
} }
void QgsShortcutsManager::registerAllChildren( QObject *object, bool recursive, const QString &section ) void QgsShortcutsManager::registerAllChildren( QObject *object, bool recursive, const QString &section )
@ -93,7 +122,7 @@ bool QgsShortcutsManager::registerAction( QAction *action, const QString &defaul
QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( key ) ); QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( key ) );
#endif #endif
const QString settingKey = mSettingsPath + section + key; const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith( QLatin1Char( '/' ) ) ? section : section + QStringLiteral( "/" ) ) + key;
mActions.insert( action, { defaultSequence, settingKey } ); mActions.insert( action, { defaultSequence, settingKey } );
connect( action, &QObject::destroyed, this, [action, this]() { actionDestroyed( action ); } ); connect( action, &QObject::destroyed, this, [action, this]() { actionDestroyed( action ); } );
@ -105,21 +134,28 @@ bool QgsShortcutsManager::registerAction( QAction *action, const QString &defaul
action->setShortcut( sequence ); action->setShortcut( sequence );
if ( !action->toolTip().isEmpty() ) if ( !action->toolTip().isEmpty() )
{ {
const QStringList parts = action->toolTip().split( '\n' ); action->setToolTip( formatActionToolTip( action->toolTip() ) );
QString formatted = QStringLiteral( "<b>%1</b>" ).arg( parts.at( 0 ) );
if ( parts.count() > 1 )
{
for ( int i = 1; i < parts.count(); ++i )
formatted += QStringLiteral( "<p>%1</p>" ).arg( parts.at( i ) );
}
action->setToolTip( formatted );
updateActionToolTip( action, sequence ); updateActionToolTip( action, sequence );
} }
return true; return true;
} }
void QgsShortcutsManager::initializeCommonAction( QAction *action, CommonAction commonAction )
{
const auto it = mCommonActions.constFind( static_cast< int >( commonAction ) );
if ( it == mCommonActions.constEnd() )
return;
// copy properties from common action
action->setText( it.value()->text() );
action->setToolTip( it.value()->toolTip() );
action->setShortcut( it.value()->shortcut() );
mLinkedCommonActions.insert( action, commonAction );
connect( action, &QObject::destroyed, this, [action, this]() { actionDestroyed( action ); } );
}
bool QgsShortcutsManager::registerShortcut( QShortcut *shortcut, const QString &defaultSequence, const QString &section ) bool QgsShortcutsManager::registerShortcut( QShortcut *shortcut, const QString &defaultSequence, const QString &section )
{ {
#ifdef QGISDEBUG #ifdef QGISDEBUG
@ -130,7 +166,7 @@ bool QgsShortcutsManager::registerShortcut( QShortcut *shortcut, const QString &
QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( shortcut->objectName() ) ); QgsLogger::warning( QStringLiteral( "Duplicate shortcut registered: %1" ).arg( shortcut->objectName() ) );
#endif #endif
const QString settingKey = mSettingsPath + section + shortcut->objectName(); const QString settingKey = mSettingsPath + ( section.isEmpty() || section.endsWith( QLatin1Char( '/' ) ) ? section : section + QStringLiteral( "/" ) ) + shortcut->objectName();
mShortcuts.insert( shortcut, { defaultSequence, settingKey } ); mShortcuts.insert( shortcut, { defaultSequence, settingKey } );
connect( shortcut, &QObject::destroyed, this, [shortcut, this]() { shortcutDestroyed( shortcut ); } ); connect( shortcut, &QObject::destroyed, this, [shortcut, this]() { shortcutDestroyed( shortcut ); } );
@ -237,6 +273,21 @@ bool QgsShortcutsManager::setKeySequence( QAction *action, const QString &sequen
action->setShortcut( sequence ); action->setShortcut( sequence );
this->updateActionToolTip( action, sequence ); this->updateActionToolTip( action, sequence );
if ( action->property( "commonAction" ).isValid() )
{
// if the key sequence for a common action is changed, update all QActions currently linked
// to that common action
const CommonAction commonAction = static_cast< CommonAction >( action->property( "commonAction" ).toInt() );
for ( auto it = mLinkedCommonActions.constBegin(); it != mLinkedCommonActions.constEnd(); ++it )
{
if ( it.value() == commonAction )
{
it.key()->setShortcut( action->shortcut() );
it.key()->setToolTip( action->toolTip() );
}
}
}
const QString settingKey = mActions[action].second; const QString settingKey = mActions[action].second;
// save to settings // save to settings
@ -299,6 +350,15 @@ QShortcut *QgsShortcutsManager::shortcutForSequence( const QKeySequence &sequenc
return nullptr; return nullptr;
} }
QKeySequence QgsShortcutsManager::sequenceForCommonAction( CommonAction action ) const
{
const auto it = mCommonActions.constFind( static_cast< int >( action ) );
if ( it == mCommonActions.constEnd() )
return QKeySequence();
return it.value()->shortcut();
}
QAction *QgsShortcutsManager::actionByName( const QString &name ) const QAction *QgsShortcutsManager::actionByName( const QString &name ) const
{ {
for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it ) for ( ActionsHash::const_iterator it = mActions.constBegin(); it != mActions.constEnd(); ++it )
@ -331,6 +391,7 @@ QShortcut *QgsShortcutsManager::shortcutByName( const QString &name ) const
void QgsShortcutsManager::actionDestroyed( QAction *action ) void QgsShortcutsManager::actionDestroyed( QAction *action )
{ {
mActions.remove( action ); mActions.remove( action );
mLinkedCommonActions.remove( action );
} }
QString QgsShortcutsManager::objectSettingKey( QObject *object ) const QString QgsShortcutsManager::objectSettingKey( QObject *object ) const
@ -366,10 +427,26 @@ void QgsShortcutsManager::shortcutDestroyed( QShortcut *shortcut )
mShortcuts.remove( shortcut ); mShortcuts.remove( shortcut );
} }
QString QgsShortcutsManager::formatActionToolTip( const QString &toolTip )
{
if ( toolTip.isEmpty() )
return QString();
const QStringList parts = toolTip.split( '\n' );
QString formatted = QStringLiteral( "<b>%1</b>" ).arg( parts.at( 0 ) );
if ( parts.count() > 1 )
{
for ( int i = 1; i < parts.count(); ++i )
formatted += QStringLiteral( "<p>%1</p>" ).arg( parts.at( i ) );
}
return formatted;
}
void QgsShortcutsManager::updateActionToolTip( QAction *action, const QString &sequence ) void QgsShortcutsManager::updateActionToolTip( QAction *action, const QString &sequence )
{ {
QString current = action->toolTip(); QString current = action->toolTip();
const thread_local QRegularExpression rx( QStringLiteral( "\\((.*)\\)" ) ); const thread_local QRegularExpression rx( QStringLiteral( "\\s*\\((.*)\\)" ) );
// Look for the last occurrence of text inside parentheses // Look for the last occurrence of text inside parentheses
QRegularExpressionMatch match; QRegularExpressionMatch match;
if ( current.lastIndexOf( rx, -1, &match ) != -1 ) if ( current.lastIndexOf( rx, -1, &match ) != -1 )

View File

@ -38,6 +38,19 @@ class GUI_EXPORT QgsShortcutsManager : public QObject
Q_OBJECT Q_OBJECT
public: public:
/**
* Contains common actions which are used across a variety of classes.
* \since QGIS 4.0
*/
enum class CommonAction
{
CodeToggleComment, //!< Toggle code comments
CodeReformat, //!< Reformat code
CodeRunScript, //!< Run script
CodeRunSelection, //!< Run selection from script
};
Q_ENUM( CommonAction )
/** /**
* Constructor for QgsShortcutsManager. * Constructor for QgsShortcutsManager.
* \param parent parent object * \param parent parent object
@ -47,6 +60,8 @@ class GUI_EXPORT QgsShortcutsManager : public QObject
*/ */
QgsShortcutsManager( QObject *parent SIP_TRANSFERTHIS = nullptr, const QString &settingsRoot = "/shortcuts/" ); QgsShortcutsManager( QObject *parent SIP_TRANSFERTHIS = nullptr, const QString &settingsRoot = "/shortcuts/" );
~QgsShortcutsManager() override;
/** /**
* Automatically registers all QActions and QShortcuts which are children of the * Automatically registers all QActions and QShortcuts which are children of the
* passed object. * passed object.
@ -93,6 +108,16 @@ class GUI_EXPORT QgsShortcutsManager : public QObject
*/ */
bool registerAction( QAction *action, const QString &defaultShortcut = QString(), const QString &section = QString() ); bool registerAction( QAction *action, const QString &defaultShortcut = QString(), const QString &section = QString() );
/**
* Initializes an \a action as a common action.
*
* This automatically configures the \a action to use the properties for the common action, such
* as setting the action's tooltip and shortcut.
*
* \since QGIS 4.0
*/
void initializeCommonAction( QAction *action, CommonAction commonAction );
/** /**
* Registers a QShortcut with the manager so the shortcut can be configured in GUI. * Registers a QShortcut with the manager so the shortcut can be configured in GUI.
* \param shortcut QShortcut to register. The shortcut must have a unique QObject::objectName() for * \param shortcut QShortcut to register. The shortcut must have a unique QObject::objectName() for
@ -228,6 +253,12 @@ class GUI_EXPORT QgsShortcutsManager : public QObject
*/ */
QShortcut *shortcutForSequence( const QKeySequence &sequence ) const; QShortcut *shortcutForSequence( const QKeySequence &sequence ) const;
/**
* Returns the key sequence which is associated with a common \a action, or an empty sequence if no shortcut is assigned to that action.
* \since QGIS 4.0
*/
QKeySequence sequenceForCommonAction( CommonAction action ) const;
/** /**
* Returns an action by its name, or NULLPTR if nothing found. * Returns an action by its name, or NULLPTR if nothing found.
* \param name action name. Must match QAction's text. * \param name action name. Must match QAction's text.
@ -273,6 +304,10 @@ class GUI_EXPORT QgsShortcutsManager : public QObject
ActionsHash mActions; ActionsHash mActions;
ShortcutsHash mShortcuts; ShortcutsHash mShortcuts;
QString mSettingsPath; QString mSettingsPath;
QHash< int, QAction * > mCommonActions;
QHash< QAction *, CommonAction > mLinkedCommonActions;
static QString formatActionToolTip( const QString &toolTip );
/** /**
* Updates the action to include the shortcut keys. Shortcut keys are * Updates the action to include the shortcut keys. Shortcut keys are

View File

@ -1694,7 +1694,7 @@ void QgsAttributesFormLayoutModel::addContainer( QModelIndex &parent, const QStr
QgsAttributesFormItem *parentItem = itemForIndex( parent ); QgsAttributesFormItem *parentItem = itemForIndex( parent );
std::unique_ptr< QgsAttributesFormItem > containerItem = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::Container, name, QString(), parentItem ); auto containerItem = std::make_unique< QgsAttributesFormItem >( QgsAttributesFormData::Container, name, QString(), parentItem );
QgsAttributesFormData::AttributeFormItemData itemData; QgsAttributesFormData::AttributeFormItemData itemData;
itemData.setColumnCount( columnCount ); itemData.setColumnCount( columnCount );
@ -1713,7 +1713,7 @@ void QgsAttributesFormLayoutModel::insertChild( const QModelIndex &parent, int r
return; return;
beginInsertRows( parent, row, row ); beginInsertRows( parent, row, row );
std::unique_ptr< QgsAttributesFormItem > item = std::make_unique< QgsAttributesFormItem >(); auto item = std::make_unique< QgsAttributesFormItem >();
item->setData( QgsAttributesFormModel::ItemIdRole, itemId ); item->setData( QgsAttributesFormModel::ItemIdRole, itemId );
item->setData( QgsAttributesFormModel::ItemTypeRole, itemType ); item->setData( QgsAttributesFormModel::ItemTypeRole, itemType );

View File

@ -1147,11 +1147,11 @@ void QgsAttributesFormProperties::pasteWidgetConfiguration()
QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap(); QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap();
QgsReadWriteContext context; QgsReadWriteContext context;
// translate widget configuration strings // translate widget configuration strings
if ( widgetType == QStringLiteral( "ValueRelation" ) ) if ( widgetType == QLatin1String( "ValueRelation" ) )
{ {
optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() ); optionsMap[QStringLiteral( "Value" )] = context.projectTranslator()->translate( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( mLayer->id(), fieldName ), optionsMap[QStringLiteral( "Value" )].toString() );
} }
if ( widgetType == QStringLiteral( "ValueMap" ) ) if ( widgetType == QLatin1String( "ValueMap" ) )
{ {
if ( optionsMap[QStringLiteral( "map" )].canConvert<QList<QVariant>>() ) if ( optionsMap[QStringLiteral( "map" )].canConvert<QList<QVariant>>() )
{ {

View File

@ -1284,7 +1284,7 @@ bool QgsMssqlProvider::addAttributes( const QList<QgsField> &attributes )
attributeClauses.append( QStringLiteral( "[%1] %2" ).arg( it->name(), type ) ); attributeClauses.append( QStringLiteral( "[%1] %2" ).arg( it->name(), type ) );
} }
statement += attributeClauses.join( QStringLiteral( ", " ) ); statement += attributeClauses.join( QLatin1String( ", " ) );
QSqlQuery query = createQuery(); QSqlQuery query = createQuery();
query.setForwardOnly( true ); query.setForwardOnly( true );

View File

@ -5191,7 +5191,7 @@ QString QgsPostgresProvider::htmlMetadata() const
")" ) ")" )
.arg( QgsPostgresConn::quotedValue( mTableName ), QgsPostgresConn::quotedValue( mSchemaName ) ); .arg( QgsPostgresConn::quotedValue( mTableName ), QgsPostgresConn::quotedValue( mSchemaName ) );
const QString sqlWithPrivilages = QStringLiteral( "privileges AS (" const QString sqlWithPrivileges = QStringLiteral( "privileges AS ("
"SELECT table_info.oid as oid, " "SELECT table_info.oid as oid, "
"COALESCE(NULLIF(CONCAT_WS(', '," "COALESCE(NULLIF(CONCAT_WS(', ',"
"CASE WHEN has_table_privilege(table_info.oid, 'SELECT') THEN 'SELECT' END," "CASE WHEN has_table_privilege(table_info.oid, 'SELECT') THEN 'SELECT' END,"
@ -5221,7 +5221,7 @@ QString QgsPostgresProvider::htmlMetadata() const
"LEFT JOIN privileges ON table_info.oid = privileges.oid " "LEFT JOIN privileges ON table_info.oid = privileges.oid "
"LEFT JOIN table_indexes_info ON table_indexes_info.table_name = table_info.table_name AND table_indexes_info.schema_name = table_info.schema_name " "LEFT JOIN table_indexes_info ON table_indexes_info.table_name = table_info.table_name AND table_indexes_info.schema_name = table_info.schema_name "
"LEFT JOIN pg_description ON pg_description.objoid = table_info.oid" ) "LEFT JOIN pg_description ON pg_description.objoid = table_info.oid" )
.arg( sqlWithTableInfo, sqlWithPrivilages, sqlWithIndexes ); .arg( sqlWithTableInfo, sqlWithPrivileges, sqlWithIndexes );
QgsPostgresResult resTable( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sqlMainQuery ) ); QgsPostgresResult resTable( connectionRO()->LoggedPQexec( "QgsPostgresProvider", sqlMainQuery ) );
@ -5233,7 +5233,7 @@ QString QgsPostgresProvider::htmlMetadata() const
if ( resTable.PQntuples() > 0 ) if ( resTable.PQntuples() > 0 )
{ {
tableComment = resTable.PQgetvalue( 0, 6 ); tableComment = resTable.PQgetvalue( 0, 6 );
tableComment = tableComment.replace( QStringLiteral( "\n" ), QStringLiteral( "<br>" ) ); tableComment = tableComment.replace( QLatin1String( "\n" ), QLatin1String( "<br>" ) );
estimateRowCount = resTable.PQgetvalue( 0, 4 ).toLongLong(); estimateRowCount = resTable.PQgetvalue( 0, 4 ).toLongLong();

Some files were not shown because too many files have changed in this diff Show More