diff --git a/src/core/annotations/qgsannotationitem.cpp b/src/core/annotations/qgsannotationitem.cpp index 4fbd2eb0275..2dd0fb1c9ea 100644 --- a/src/core/annotations/qgsannotationitem.cpp +++ b/src/core/annotations/qgsannotationitem.cpp @@ -110,3 +110,221 @@ void QgsMarkerItem::setSymbol( QgsMarkerSymbol *symbol ) { mSymbol.reset( symbol ); } + + + + +QgsLineStringItem::QgsLineStringItem( const QgsLineString &linestring, const QgsCoordinateReferenceSystem &crs ) + : QgsAnnotationItem( crs ) + , mLineString( linestring ) + , mSymbol( qgis::make_unique< QgsLineSymbol >() ) +{ + +} + +QgsLineStringItem::~QgsLineStringItem() = default; + +QString QgsLineStringItem::type() const +{ + return QStringLiteral( "linestring" ); +} + +void QgsLineStringItem::render( QgsRenderContext &context, QgsFeedback * ) +{ + QPolygonF pts = mLineString.asQPolygonF(); + + //transform the QPolygonF to screen coordinates + if ( context.coordinateTransform().isValid() ) + { + try + { + context.coordinateTransform().transformPolygon( pts ); + } + catch ( QgsCsException & ) + { + // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid + } + } + + // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors + pts.erase( std::remove_if( pts.begin(), pts.end(), + []( const QPointF point ) + { + return !std::isfinite( point.x() ) || !std::isfinite( point.y() ); + } ), pts.end() ); + + QPointF *ptr = pts.data(); + for ( int i = 0; i < pts.size(); ++i, ++ptr ) + { + context.mapToPixel().transformInPlace( ptr->rx(), ptr->ry() ); + } + + mSymbol->startRender( context ); + mSymbol->renderPolyline( pts, nullptr, context ); + mSymbol->stopRender( context ); +} + +bool QgsLineStringItem::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const +{ + element.setAttribute( QStringLiteral( "wkt" ), mLineString.asWkt() ); + crs().writeXml( element, document ); + + element.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "lineSymbol" ), mSymbol.get(), document, context ) ); + + return true; +} + +QgsLineStringItem *QgsLineStringItem::create() +{ + return new QgsLineStringItem( QgsLineString(), QgsCoordinateReferenceSystem() ); +} + +QgsLineStringItem *QgsLineStringItem::createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) +{ + const QString wkt = element.attribute( QStringLiteral( "wkt" ) ); + QgsLineString ls; + ls.fromWkt( wkt ); + + QgsCoordinateReferenceSystem crs; + crs.readXml( element ); + + std::unique_ptr< QgsLineStringItem > item = qgis::make_unique< QgsLineStringItem >( ls, crs ); + + const QDomElement symbolElem = element.firstChildElement( QStringLiteral( "symbol" ) ); + if ( !symbolElem.isNull() ) + item->setSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) ); + + return item.release(); +} + +QgsLineStringItem *QgsLineStringItem::clone() +{ + std::unique_ptr< QgsLineStringItem > item = qgis::make_unique< QgsLineStringItem >( mLineString, crs() ); + item->setSymbol( mSymbol->clone() ); + return item.release(); +} + +const QgsLineSymbol *QgsLineStringItem::symbol() const +{ + return mSymbol.get(); +} + +void QgsLineStringItem::setSymbol( QgsLineSymbol *symbol ) +{ + mSymbol.reset( symbol ); +} + + + +QgsPolygonItem::QgsPolygonItem( const QgsPolygon &polygon, const QgsCoordinateReferenceSystem &crs ) + : QgsAnnotationItem( crs ) + , mPolygon( polygon ) + , mSymbol( qgis::make_unique< QgsFillSymbol >() ) +{ + +} + +QgsPolygonItem::~QgsPolygonItem() = default; + +QString QgsPolygonItem::type() const +{ + return QStringLiteral( "polygon" ); +} + +void QgsPolygonItem::render( QgsRenderContext &context, QgsFeedback * ) +{ + + auto transformRing = [&context]( QPolygonF & pts ) + { + //transform the QPolygonF to screen coordinates + if ( context.coordinateTransform().isValid() ) + { + try + { + context.coordinateTransform().transformPolygon( pts ); + } + catch ( QgsCsException & ) + { + // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid + } + } + + // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors + pts.erase( std::remove_if( pts.begin(), pts.end(), + []( const QPointF point ) + { + return !std::isfinite( point.x() ) || !std::isfinite( point.y() ); + } ), pts.end() ); + + QPointF *ptr = pts.data(); + for ( int i = 0; i < pts.size(); ++i, ++ptr ) + { + context.mapToPixel().transformInPlace( ptr->rx(), ptr->ry() ); + } + }; + + QPolygonF exterior = mPolygon.exteriorRing()->asQPolygonF(); + transformRing( exterior ); + QList rings; + rings.reserve( mPolygon.numInteriorRings() ); + for ( int i = 0; i < mPolygon.numInteriorRings(); ++i ) + { + QPolygonF ring = mPolygon.interiorRing( i )->asQPolygonF(); + transformRing( ring ); + rings.append( ring ); + } + + mSymbol->startRender( context ); + mSymbol->renderPolygon( exterior, rings.empty() ? nullptr : &rings, nullptr, context ); + mSymbol->stopRender( context ); +} + +bool QgsPolygonItem::writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const +{ + element.setAttribute( QStringLiteral( "wkt" ), mPolygon.asWkt() ); + crs().writeXml( element, document ); + + element.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "lineSymbol" ), mSymbol.get(), document, context ) ); + + return true; +} + +QgsPolygonItem *QgsPolygonItem::create() +{ + return new QgsPolygonItem( QgsPolygon(), QgsCoordinateReferenceSystem() ); +} + +QgsPolygonItem *QgsPolygonItem::createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) +{ + const QString wkt = element.attribute( QStringLiteral( "wkt" ) ); + QgsPolygon poly; + poly.fromWkt( wkt ); + + QgsCoordinateReferenceSystem crs; + crs.readXml( element ); + + std::unique_ptr< QgsPolygonItem > item = qgis::make_unique< QgsPolygonItem >( poly, crs ); + + const QDomElement symbolElem = element.firstChildElement( QStringLiteral( "symbol" ) ); + if ( !symbolElem.isNull() ) + item->setSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) ); + + return item.release(); +} + +QgsPolygonItem *QgsPolygonItem::clone() +{ + std::unique_ptr< QgsPolygonItem > item = qgis::make_unique< QgsPolygonItem >( mPolygon, crs() ); + item->setSymbol( mSymbol->clone() ); + return item.release(); +} + +const QgsFillSymbol *QgsPolygonItem::symbol() const +{ + return mSymbol.get(); +} + +void QgsPolygonItem::setSymbol( QgsFillSymbol *symbol ) +{ + mSymbol.reset( symbol ); +} diff --git a/src/core/annotations/qgsannotationitem.h b/src/core/annotations/qgsannotationitem.h index 40e24eaaa71..610aaa98a37 100644 --- a/src/core/annotations/qgsannotationitem.h +++ b/src/core/annotations/qgsannotationitem.h @@ -21,9 +21,13 @@ #include "qgis_sip.h" #include "qgscoordinatereferencesystem.h" #include "qgsrendercontext.h" +#include "qgslinestring.h" +#include "qgspolygon.h" class QgsFeedback; class QgsMarkerSymbol; +class QgsLineSymbol; +class QgsFillSymbol; /** * \ingroup core @@ -112,4 +116,63 @@ class CORE_EXPORT QgsMarkerItem : public QgsAnnotationItem }; + +class CORE_EXPORT QgsLineStringItem : public QgsAnnotationItem +{ + public: + + QgsLineStringItem( const QgsLineString &linestring, const QgsCoordinateReferenceSystem &crs ); + ~QgsLineStringItem() override; + + QString type() const override; + void render( QgsRenderContext &context, QgsFeedback *feedback ) override; + bool writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const override; + static QgsLineStringItem *create() SIP_FACTORY; + static QgsLineStringItem *createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; + + QgsLineStringItem *clone() override SIP_FACTORY; + + const QgsLineSymbol *symbol() const; + void setSymbol( QgsLineSymbol *symbol SIP_TRANSFER ); + + private: + + QgsLineString mLineString; + std::unique_ptr< QgsLineSymbol > mSymbol; + +#ifdef SIP_RUN + QgsLineStringItem( const QgsLineStringItem &other ); +#endif + +}; + + +class CORE_EXPORT QgsPolygonItem : public QgsAnnotationItem +{ + public: + + QgsPolygonItem( const QgsPolygon &polygon, const QgsCoordinateReferenceSystem &crs ); + ~QgsPolygonItem() override; + + QString type() const override; + void render( QgsRenderContext &context, QgsFeedback *feedback ) override; + bool writeXml( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const override; + static QgsPolygonItem *create() SIP_FACTORY; + static QgsPolygonItem *createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; + + QgsPolygonItem *clone() override SIP_FACTORY; + + const QgsFillSymbol *symbol() const; + void setSymbol( QgsFillSymbol *symbol SIP_TRANSFER ); + + private: + + QgsPolygon mPolygon; + std::unique_ptr< QgsFillSymbol > mSymbol; + +#ifdef SIP_RUN + QgsPolygonItem( const QgsPolygonItem &other ); +#endif + +}; #endif // QGSANNOTATIONITEM_H diff --git a/src/core/annotations/qgsannotationitemregistry.cpp b/src/core/annotations/qgsannotationitemregistry.cpp index 423137fa12b..33f5e7aba69 100644 --- a/src/core/annotations/qgsannotationitemregistry.cpp +++ b/src/core/annotations/qgsannotationitemregistry.cpp @@ -35,7 +35,10 @@ bool QgsAnnotationItemRegistry::populate() mMetadata.insert( QStringLiteral( "marker" ), new QgsAnnotationItemMetadata( QStringLiteral( "marker" ), QObject::tr( "Marker" ), QObject::tr( "Markers" ), QgsMarkerItem::create, QgsMarkerItem::createFromElement ) ); - + mMetadata.insert( QStringLiteral( "linestring" ), new QgsAnnotationItemMetadata( QStringLiteral( "linestring" ), QObject::tr( "Polyline" ), QObject::tr( "Polylines" ), + QgsLineStringItem::create, QgsLineStringItem::createFromElement ) ); + mMetadata.insert( QStringLiteral( "polygon" ), new QgsAnnotationItemMetadata( QStringLiteral( "polygon" ), QObject::tr( "Polygon" ), QObject::tr( "Polygons" ), + QgsPolygonItem::create, QgsPolygonItem::createFromElement ) ); return true; }