Skip to content

Commit

Permalink
Add some more settings to layout profile item
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jan 24, 2023
1 parent 539d718 commit f3af0df
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 1 deletion.
Expand Up @@ -62,6 +62,68 @@ Returns the list of map layers participating in the elevation profile.
Sets the list of map ``layers`` participating in the elevation profile.

.. seealso:: :py:func:`layers`
%End

void setProfileCurve( QgsCurve *curve /Transfer/ );
%Docstring
Sets the cross section profile ``curve``, which represents the line along which the profile should be generated.

Ownership of ``curve`` is transferred to the item.

The coordinate reference system of the ``curve`` is set via :py:func:`~QgsLayoutItemElevationProfile.setCrs`.

.. seealso:: :py:func:`profileCurve`
%End

QgsCurve *profileCurve() const;
%Docstring
Returns the cross section profile curve, which represents the line along which the profile should be generated.

The coordinate reference system of the curve is retrieved via :py:func:`~QgsLayoutItemElevationProfile.crs`.

.. seealso:: :py:func:`setProfileCurve`
%End

void setCrs( const QgsCoordinateReferenceSystem &crs );
%Docstring
Sets the desired Coordinate Reference System (``crs``) for the profile.

This also represents the CRS associated with the :py:func:`~QgsLayoutItemElevationProfile.profileCurve`.

.. seealso:: :py:func:`crs`
%End

QgsCoordinateReferenceSystem crs() const;
%Docstring
Returns the desired Coordinate Reference System for the profile.

This also represents the CRS associated with the :py:func:`~QgsLayoutItemElevationProfile.profileCurve`.

.. seealso:: :py:func:`setCrs`
%End

void setTolerance( double tolerance );
%Docstring
Sets the tolerance of the request (in :py:func:`~QgsLayoutItemElevationProfile.crs` units).

This value determines how far from the :py:func:`~QgsLayoutItemElevationProfile.profileCurve` is appropriate for inclusion of results. For instance,
when a profile is generated for a point vector layer this tolerance distance will dictate how far from the
actual profile curve a point can reside within to be included in the results. Other sources may completely
ignore this tolerance if it is not appropriate for the particular source.

.. seealso:: :py:func:`tolerance`
%End

double tolerance() const;
%Docstring
Returns the tolerance of the request (in :py:func:`~QgsLayoutItemElevationProfile.crs` units).

This value determines how far from the :py:func:`~QgsLayoutItemElevationProfile.profileCurve` is appropriate for inclusion of results. For instance,
when a profile is generated for a point vector layer this tolerance distance will dictate how far from the
actual profile curve a point can reside within to be included in the results. Other sources may completely
ignore this tolerance if it is not appropriate for the particular source.

.. seealso:: :py:func:`setTolerance`
%End

protected:
Expand Down
77 changes: 77 additions & 0 deletions src/core/layout/qgslayoutitemelevationprofile.cpp
Expand Up @@ -21,6 +21,7 @@
#include "qgslayout.h"
#include "qgsmessagelog.h"
#include "qgsmaplayerlistutils_p.h"
#include "qgscurve.h"

///@cond PRIVATE
class QgsLayoutItemElevationProfilePlot : public Qgs2DPlot
Expand Down Expand Up @@ -394,6 +395,42 @@ void QgsLayoutItemElevationProfile::setLayers( const QList<QgsMapLayer *> &layer
mLayers = _qgis_listRawToRef( layers );
}

void QgsLayoutItemElevationProfile::setProfileCurve( QgsCurve *curve )
{
mCurve.reset( curve );
}

QgsCurve *QgsLayoutItemElevationProfile::profileCurve() const
{
return mCurve.get();
}

void QgsLayoutItemElevationProfile::setCrs( const QgsCoordinateReferenceSystem &crs )
{
if ( mCrs == crs )
return;

mCrs = crs;
}

QgsCoordinateReferenceSystem QgsLayoutItemElevationProfile::crs() const
{
return mCrs;
}

void QgsLayoutItemElevationProfile::setTolerance( double tolerance )
{
if ( mTolerance == tolerance )
return;

mTolerance = tolerance;
}

double QgsLayoutItemElevationProfile::tolerance() const
{
return mTolerance;
}

void QgsLayoutItemElevationProfile::draw( QgsLayoutItemRenderContext &context )
{
// size must be in pixels, not layout units
Expand All @@ -409,6 +446,20 @@ bool QgsLayoutItemElevationProfile::writePropertiesToElement( QDomElement &layou
layoutProfileElem.appendChild( plotElement );
}

layoutProfileElem.setAttribute( QStringLiteral( "tolerance" ), mTolerance );
if ( mCrs.isValid() )
{
QDomElement crsElem = doc.createElement( QStringLiteral( "crs" ) );
mCrs.writeXml( crsElem, doc );
layoutProfileElem.appendChild( crsElem );
}
if ( mCurve )
{
QDomElement curveElem = doc.createElement( QStringLiteral( "curve" ) );
curveElem.appendChild( doc.createTextNode( mCurve->asWkt( ) ) );
layoutProfileElem.appendChild( curveElem );
}

{
QDomElement layersElement = doc.createElement( QStringLiteral( "layers" ) );
for ( const QgsMapLayerRef &layer : mLayers )
Expand All @@ -431,6 +482,32 @@ bool QgsLayoutItemElevationProfile::readPropertiesFromElement( const QDomElement
mPlot->readXml( plotElement, context );
}

const QDomNodeList crsNodeList = itemElem.elementsByTagName( QStringLiteral( "crs" ) );
QgsCoordinateReferenceSystem crs;
if ( !crsNodeList.isEmpty() )
{
const QDomElement crsElem = crsNodeList.at( 0 ).toElement();
crs.readXml( crsElem );
}
mCrs = crs;

const QDomNodeList curveNodeList = itemElem.elementsByTagName( QStringLiteral( "curve" ) );
if ( !curveNodeList.isEmpty() )
{
const QDomElement curveElem = curveNodeList.at( 0 ).toElement();
const QgsGeometry curve = QgsGeometry::fromWkt( curveElem.text() );
if ( const QgsCurve *curveGeom = qgsgeometry_cast< const QgsCurve * >( curve.constGet() ) )
{
mCurve.reset( curveGeom->clone() );
}
else
{
mCurve.reset();
}
}

mTolerance = itemElem.attribute( QStringLiteral( "tolerance" ) ).toDouble();

{
mLayers.clear();
const QDomElement layersElement = itemElem.firstChildElement( QStringLiteral( "layers" ) );
Expand Down
67 changes: 67 additions & 0 deletions src/core/layout/qgslayoutitemelevationprofile.h
Expand Up @@ -79,6 +79,68 @@ class CORE_EXPORT QgsLayoutItemElevationProfile: public QgsLayoutItem
*/
void setLayers( const QList< QgsMapLayer * > &layers );

/**
* Sets the cross section profile \a curve, which represents the line along which the profile should be generated.
*
* Ownership of \a curve is transferred to the item.
*
* The coordinate reference system of the \a curve is set via setCrs().
*
* \see profileCurve()
*/
void setProfileCurve( QgsCurve *curve SIP_TRANSFER );

/**
* Returns the cross section profile curve, which represents the line along which the profile should be generated.
*
* The coordinate reference system of the curve is retrieved via crs().
*
* \see setProfileCurve()
*/
QgsCurve *profileCurve() const;

/**
* Sets the desired Coordinate Reference System (\a crs) for the profile.
*
* This also represents the CRS associated with the profileCurve().
*
* \see crs()
*/
void setCrs( const QgsCoordinateReferenceSystem &crs );

/**
* Returns the desired Coordinate Reference System for the profile.
*
* This also represents the CRS associated with the profileCurve().
*
* \see setCrs()
*/
QgsCoordinateReferenceSystem crs() const;

/**
* Sets the tolerance of the request (in crs() units).
*
* This value determines how far from the profileCurve() is appropriate for inclusion of results. For instance,
* when a profile is generated for a point vector layer this tolerance distance will dictate how far from the
* actual profile curve a point can reside within to be included in the results. Other sources may completely
* ignore this tolerance if it is not appropriate for the particular source.
*
* \see tolerance()
*/
void setTolerance( double tolerance );

/**
* Returns the tolerance of the request (in crs() units).
*
* This value determines how far from the profileCurve() is appropriate for inclusion of results. For instance,
* when a profile is generated for a point vector layer this tolerance distance will dictate how far from the
* actual profile curve a point can reside within to be included in the results. Other sources may completely
* ignore this tolerance if it is not appropriate for the particular source.
*
* \see setTolerance()
*/
double tolerance() const;

protected:
void draw( QgsLayoutItemRenderContext &context ) override;
bool writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const override;
Expand All @@ -90,6 +152,11 @@ class CORE_EXPORT QgsLayoutItemElevationProfile: public QgsLayoutItem

QList< QgsMapLayerRef > mLayers;

QgsCoordinateReferenceSystem mCrs;
std::unique_ptr< QgsCurve> mCurve;

double mTolerance = 0;

};

#endif //QGSLAYOUTITEMELEVATIONPROFILE_H
48 changes: 47 additions & 1 deletion tests/src/python/test_qgslayoutelevationprofile.py
Expand Up @@ -42,7 +42,9 @@
QgsCategorizedSymbolRenderer,
QgsRendererCategory,
QgsRasterLayer,
QgsApplication)
QgsApplication,
QgsGeometry,
QgsCoordinateReferenceSystem)
from qgis.testing import (start_app,
unittest
)
Expand Down Expand Up @@ -104,6 +106,50 @@ def test_layers(self):

self.assertEqual([m.id() for m in profile2.layers()], [layer2.id(), layer3.id()])

def test_settings(self):
project = QgsProject()
layout = QgsPrintLayout(project)
profile = QgsLayoutItemElevationProfile(layout)
layout.addLayoutItem(profile)
project.layoutManager().addLayout(layout)

# test that default settings are written/restored
with tempfile.TemporaryDirectory() as temp_dir:
self.assertTrue(project.write(os.path.join(temp_dir, 'p.qgs')))

p2 = QgsProject()
self.assertTrue(p2.read(os.path.join(temp_dir, 'p.qgs')))

layout2 = p2.layoutManager().printLayouts()[0]
profile2 = [i for i in layout2.items() if isinstance(i, QgsLayoutItemElevationProfile)][0]

self.assertFalse(profile2.crs().isValid())
self.assertEqual(profile2.tolerance(), 0)
self.assertIsNone(profile2.profileCurve())

curve = QgsGeometry.fromWkt('LineString(0 0, 10 10)')
profile.setProfileCurve(curve.constGet().clone())
self.assertEqual(profile.profileCurve().asWkt(), 'LineString (0 0, 10 10)')

profile.setCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
self.assertEqual(profile.crs(), QgsCoordinateReferenceSystem('EPSG:3857'))
profile.setTolerance(101)
self.assertEqual(profile.tolerance(), 101)

# test that settings are written/restored
with tempfile.TemporaryDirectory() as temp_dir:
self.assertTrue(project.write(os.path.join(temp_dir, 'p.qgs')))

p2 = QgsProject()
self.assertTrue(p2.read(os.path.join(temp_dir, 'p.qgs')))

layout2 = p2.layoutManager().printLayouts()[0]
profile2 = [i for i in layout2.items() if isinstance(i, QgsLayoutItemElevationProfile)][0]

self.assertEqual(profile2.crs(), QgsCoordinateReferenceSystem('EPSG:3857'))
self.assertEqual(profile2.tolerance(), 101)
self.assertEqual(profile2.profileCurve().asWkt(), 'LineString (0 0, 10 10)')


if __name__ == '__main__':
unittest.main()

0 comments on commit f3af0df

Please sign in to comment.