diff --git a/python/core/auto_generated/qgstessellator.sip.in b/python/core/auto_generated/qgstessellator.sip.in new file mode 100644 index 00000000000..094985f0a6c --- /dev/null +++ b/python/core/auto_generated/qgstessellator.sip.in @@ -0,0 +1,66 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgstessellator.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsTessellator +{ +%Docstring +Class that takes care of tessellation of polygons into triangles. + +It is expected that client code will create the tessellator object, then repeatedly call +addPolygon() method that will generate triangles, and finally call data() to get final vertex data. + +Optionally provides extrusion by adding triangles that serve as walls when extrusion height is non-zero. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgstessellator.h" +%End + public: + QgsTessellator( double originX, double originY, bool addNormals, bool invertNormals = false, bool addBackFaces = false ); +%Docstring +Creates tessellator with a specified origin point of the world (in map coordinates) +%End + + void addPolygon( const QgsPolygon &polygon, float extrusionHeight ); +%Docstring +Tessellates a triangle and adds its vertex entries to the output data array +%End + + QVector data() const; +%Docstring +Returns array of triangle vertex data + +Vertice coordinates are stored as (x, z, -y) +%End + + int dataVerticesCount() const; +%Docstring +Returns the number of vertices stored in the output data array +%End + + int stride() const; +%Docstring +Returns size of one vertex entry in bytes +%End + + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgstessellator.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index aaa1384d501..413564e3d86 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -3,6 +3,7 @@ %Include auto_generated/expression/qgsexpressionnode.sip %Include auto_generated/expression/qgsexpressionnodeimpl.sip %Include auto_generated/expression/qgsexpressionfunction.sip +%Include auto_generated/qgstessellator.sip %Include auto_generated/qgis.sip %Include auto_generated/qgsaction.sip %Include auto_generated/qgsactionscope.sip diff --git a/src/3d/CMakeLists.txt b/src/3d/CMakeLists.txt index 4986989cdc6..403917dae7f 100644 --- a/src/3d/CMakeLists.txt +++ b/src/3d/CMakeLists.txt @@ -9,7 +9,6 @@ SET(QGIS_3D_SRCS qgscameracontroller.cpp qgsphongmaterialsettings.cpp qgstessellatedpolygongeometry.cpp - qgstessellator.cpp qgstilingscheme.cpp qgsvectorlayer3drenderer.cpp @@ -41,12 +40,6 @@ SET(QGIS_3D_SRCS terrain/qgsterraintileloader_p.cpp #terrain/quantizedmeshgeometry.cpp #terrain/quantizedmeshterraingenerator.cpp - - ${CMAKE_SOURCE_DIR}/external/poly2tri/common/shapes.cc - ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/advancing_front.cc - ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/cdt.cc - ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/sweep_context.cc - ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/sweep.cc ) SET(QGIS_3D_MOC_HDRS @@ -82,7 +75,6 @@ SET(QGIS_3D_HDRS qgscameracontroller.h qgsphongmaterialsettings.h qgstessellatedpolygongeometry.h - qgstessellator.h qgstilingscheme.h qgsvectorlayer3drenderer.h @@ -127,7 +119,6 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/metadata ${CMAKE_SOURCE_DIR}/src/core/expression ${CMAKE_SOURCE_DIR}/src/core/3d - ${CMAKE_SOURCE_DIR}/external/poly2tri ${CMAKE_BINARY_DIR}/src/core ${CMAKE_BINARY_DIR}/src/3d ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e1265bd0736..6e8c53a5135 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -10,6 +10,12 @@ SET(QGIS_CORE_SRCS ${CMAKE_SOURCE_DIR}/external/nmea/time.c ${CMAKE_SOURCE_DIR}/external/nmea/tok.c + ${CMAKE_SOURCE_DIR}/external/poly2tri/common/shapes.cc + ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/advancing_front.cc + ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/cdt.cc + ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/sweep_context.cc + ${CMAKE_SOURCE_DIR}/external/poly2tri/sweep/sweep.cc + gps/qgsgpsconnection.cpp gps/qgsgpsconnectionregistry.cpp gps/qgsgpsdconnection.cpp @@ -291,6 +297,7 @@ SET(QGIS_CORE_SRCS qgsstringstatisticalsummary.cpp qgsstringutils.cpp qgstaskmanager.cpp + qgstessellator.cpp qgstextlabelfeature.cpp qgstextrenderer.cpp qgstolerance.cpp @@ -811,6 +818,8 @@ SET(QGIS_CORE_HDRS expression/qgsexpressionnodeimpl.h expression/qgsexpressionfunction.h + qgstessellator.h + qgis.h qgis_sip.h qgsaction.h @@ -1204,6 +1213,7 @@ INCLUDE_DIRECTORIES( metadata mesh ${CMAKE_SOURCE_DIR}/external/nmea + ${CMAKE_SOURCE_DIR}/external/poly2tri ) IF (WITH_INTERNAL_QEXTSERIALPORT) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/external/qextserialport) diff --git a/src/3d/qgstessellator.cpp b/src/core/qgstessellator.cpp similarity index 99% rename from src/3d/qgstessellator.cpp rename to src/core/qgstessellator.cpp index 223240afa9c..c34fdacc917 100644 --- a/src/3d/qgstessellator.cpp +++ b/src/core/qgstessellator.cpp @@ -540,6 +540,11 @@ QgsPoint getPointFromData( QVector< float >::const_iterator &it ) return QgsPoint( x, y, z ); } +int QgsTessellator::dataVerticesCount() const +{ + return mData.size() / 3; +} + std::unique_ptr QgsTessellator::asMultiPolygon() const { std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >(); diff --git a/src/3d/qgstessellator.h b/src/core/qgstessellator.h similarity index 82% rename from src/3d/qgstessellator.h rename to src/core/qgstessellator.h index 358a3a53952..a1b4013318d 100644 --- a/src/3d/qgstessellator.h +++ b/src/core/qgstessellator.h @@ -16,16 +16,18 @@ #ifndef QGSTESSELLATOR_H #define QGSTESSELLATOR_H -#include "qgis_3d.h" +#include "qgis_core.h" +#include "qgis.h" class QgsPolygon; class QgsMultiPolygon; #include #include +#include "qgspoint.h" /** - * \ingroup 3d + * \ingroup core * Class that takes care of tessellation of polygons into triangles. * * It is expected that client code will create the tessellator object, then repeatedly call @@ -33,9 +35,9 @@ class QgsMultiPolygon; * * Optionally provides extrusion by adding triangles that serve as walls when extrusion height is non-zero. * - * \since QGIS 3.0 + * \since QGIS 3.4 (since QGIS 3.0 in QGIS_3D library) */ -class _3D_EXPORT QgsTessellator +class CORE_EXPORT QgsTessellator { public: //! Creates tessellator with a specified origin point of the world (in map coordinates) @@ -44,15 +46,23 @@ class _3D_EXPORT QgsTessellator //! Tessellates a triangle and adds its vertex entries to the output data array void addPolygon( const QgsPolygon &polygon, float extrusionHeight ); - //! Returns array of triangle vertex data + /** + * Returns array of triangle vertex data + * + * Vertice coordinates are stored as (x, z, -y) + */ QVector data() const { return mData; } + + //! Returns the number of vertices stored in the output data array + int dataVerticesCount() const; + //! Returns size of one vertex entry in bytes int stride() const { return mStride; } /** * Returns the triangulation as a multipolygon geometry. */ - std::unique_ptr< QgsMultiPolygon > asMultiPolygon() const; + std::unique_ptr< QgsMultiPolygon > asMultiPolygon() const SIP_SKIP; private: double mOriginX = 0, mOriginY = 0; diff --git a/src/quickgui/CMakeLists.txt b/src/quickgui/CMakeLists.txt index 38a3a90ee37..c308eaa7c67 100644 --- a/src/quickgui/CMakeLists.txt +++ b/src/quickgui/CMakeLists.txt @@ -1,19 +1,29 @@ ############################################################ # sources SET(QGIS_QUICK_GUI_MOC_HDRS + qgsquickfeaturelayerpair.h + qgsquickfeaturehighlight.h + qgsquickidentifykit.h qgsquickmapcanvasmap.h qgsquickmapsettings.h + qgsquickmaptransform.h qgsquickmessagelogmodel.h qgsquickscalebarkit.h qgsquickutils.h ) SET(QGIS_QUICK_GUI_HDRS + qgsquickhighlightsgnode.h ) SET(QGIS_QUICK_GUI_SRC + qgsquickfeaturelayerpair.cpp + qgsquickfeaturehighlight.cpp + qgsquickhighlightsgnode.cpp + qgsquickidentifykit.cpp qgsquickmapcanvasmap.cpp qgsquickmapsettings.cpp + qgsquickmaptransform.cpp qgsquickmessagelogmodel.cpp qgsquickscalebarkit.cpp qgsquickutils.cpp diff --git a/src/quickgui/plugin/qgsquickplugin.cpp b/src/quickgui/plugin/qgsquickplugin.cpp index caf923b21f5..d34bce25062 100644 --- a/src/quickgui/plugin/qgsquickplugin.cpp +++ b/src/quickgui/plugin/qgsquickplugin.cpp @@ -29,8 +29,12 @@ #include "qgscoordinatetransformcontext.h" #include "qgsvectorlayer.h" +#include "qgsquickfeaturehighlight.h" +#include "qgsquickidentifykit.h" +#include "qgsquickfeaturelayerpair.h" #include "qgsquickmapcanvasmap.h" #include "qgsquickmapsettings.h" +#include "qgsquickmaptransform.h" #include "qgsquickmessagelogmodel.h" #include "qgsquickplugin.h" #include "qgsquickscalebarkit.h" @@ -53,10 +57,14 @@ void QgsQuickPlugin::registerTypes( const char *uri ) qRegisterMetaType< QgsFeatureId > ( "QgsFeatureId" ); qRegisterMetaType< QgsPoint >( "QgsPoint" ); qRegisterMetaType< QgsPointXY >( "QgsPointXY" ); + qRegisterMetaType< QgsQuickFeatureLayerPair >( "QgsQuickFeatureLayerPair" ); qmlRegisterType< QgsProject >( uri, 0, 1, "Project" ); + qmlRegisterType< QgsQuickFeatureHighlight >( uri, 0, 1, "FeatureHighlight" ); + qmlRegisterType< QgsQuickIdentifyKit >( uri, 0, 1, "IdentifyKit" ); qmlRegisterType< QgsQuickMapCanvasMap >( uri, 0, 1, "MapCanvasMap" ); qmlRegisterType< QgsQuickMapSettings >( uri, 0, 1, "MapSettings" ); + qmlRegisterType< QgsQuickMapTransform >( uri, 0, 1, "MapTransform" ); qmlRegisterType< QgsQuickMessageLogModel >( uri, 0, 1, "MessageLogModel" ); qmlRegisterType< QgsQuickScaleBarKit >( uri, 0, 1, "ScaleBarKit" ); qmlRegisterType< QgsVectorLayer >( uri, 0, 1, "VectorLayer" ); diff --git a/src/quickgui/qgsquickfeaturehighlight.cpp b/src/quickgui/qgsquickfeaturehighlight.cpp new file mode 100644 index 00000000000..7bf0b3850be --- /dev/null +++ b/src/quickgui/qgsquickfeaturehighlight.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + qgsqguickfeaturehighlight.cpp + -------------------------------------- + Date : May 2018 + Copyright : (C) 2018 by Peter Petrik + Email : zilolv 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 + +#include "qgsvectorlayer.h" + +#include "qgsquickfeaturehighlight.h" +#include "qgsquickmapsettings.h" +#include "qgsquickhighlightsgnode.h" + + +QgsQuickFeatureHighlight::QgsQuickFeatureHighlight( QQuickItem *parent ) + : QQuickItem( parent ) +{ + setFlags( QQuickItem::ItemHasContents ); + setAntialiasing( true ); + + // transform to device coords + mTransform.appendToItem( this ); + + connect( this, &QgsQuickFeatureHighlight::mapSettingsChanged, this, &QgsQuickFeatureHighlight::onMapSettingsChanged ); + connect( this, &QgsQuickFeatureHighlight::featureLayerPairChanged, this, &QgsQuickFeatureHighlight::markDirty ); + connect( this, &QgsQuickFeatureHighlight::colorChanged, this, &QgsQuickFeatureHighlight::markDirty ); + connect( this, &QgsQuickFeatureHighlight::widthChanged, this, &QgsQuickFeatureHighlight::markDirty ); +} + +void QgsQuickFeatureHighlight::markDirty() +{ + mDirty = true; + update(); +} + +void QgsQuickFeatureHighlight::onMapSettingsChanged() +{ + mTransform.setMapSettings( mMapSettings ); + markDirty(); +} + +QSGNode *QgsQuickFeatureHighlight::updatePaintNode( QSGNode *n, QQuickItem::UpdatePaintNodeData * ) +{ + if ( !mDirty || !mMapSettings || !mFeatureLayerPair.isValid() ) + return n; + + delete n; + n = new QSGNode; + + QgsVectorLayer *layer = mFeatureLayerPair.layer(); + Q_ASSERT( layer ); // we checked the validity of feature-layer pair + QgsCoordinateTransform transf( layer->crs(), mMapSettings->destinationCrs(), mMapSettings->transformContext() ); + + QgsFeature feature = mFeatureLayerPair.feature(); + if ( feature.hasGeometry() ) + { + QgsGeometry geom( feature.geometry() ); + try + { + geom.transform( transf ); + std::unique_ptr rb( new QgsQuickHighlightSGNode( geom, mColor, mWidth ) ); + rb->setFlag( QSGNode::OwnedByParent ); + n->appendChildNode( rb.release() ); + } + catch ( QgsCsException &e ) + { + Q_UNUSED( e ); + // Caught an error in transform + } + } + mDirty = false; + + return n; +} diff --git a/src/quickgui/qgsquickfeaturehighlight.h b/src/quickgui/qgsquickfeaturehighlight.h new file mode 100644 index 00000000000..6f3f8f30fba --- /dev/null +++ b/src/quickgui/qgsquickfeaturehighlight.h @@ -0,0 +1,99 @@ +/*************************************************************************** + qgsqguickfeaturehighlight.h + --------------------------- + Date : May 2018 + Copyright : (C) 2018 by Peter Petrik + Email : zilolv 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 QGSQUICKFEATUREHIGHLIGHT_H +#define QGSQUICKFEATUREHIGHLIGHT_H + +#include + +#include "qgsquickfeaturelayerpair.h" +#include "qgis_quick.h" +#include "qgsquickmaptransform.h" + +class QgsQuickMapSettings; + +/** + * \ingroup quick + * + * Creates map highlights for a geometry provided by a FeatureModel. + * + * The highlights are compatible with the QtQuick scene graph and + * can be direcly shown on map canvas + * + * \note QML Type: FeatureHighlight + * + * \since QGIS 3.4 + */ +class QUICK_EXPORT QgsQuickFeatureHighlight : public QQuickItem +{ + Q_OBJECT + + /** + * Associated map settings. Should be initialized from QML component before the first use. + */ + Q_PROPERTY( QgsQuickMapSettings *mapSettings MEMBER mMapSettings NOTIFY mapSettingsChanged ) + + /** + * Feature to highlight + */ + Q_PROPERTY( QgsQuickFeatureLayerPair featureLayerPair MEMBER mFeatureLayerPair NOTIFY featureLayerPairChanged ) + + /** + * Color of the highlighted geometry (feature). + * + * Default is yellow color + */ + Q_PROPERTY( QColor color MEMBER mColor NOTIFY colorChanged ) + + /** + * Pen width of the highlighted geometry (feature). + * + * Default is 20, see QSGGeometry::setLineWidth() + */ + Q_PROPERTY( float width MEMBER mWidth NOTIFY widthChanged ) + + public: + //! Creates a new feature highlight + explicit QgsQuickFeatureHighlight( QQuickItem *parent = nullptr ); + + signals: + //! \copydoc QgsQuickFeatureHighlight::featureLayerPair + void featureLayerPairChanged(); + + //! \copydoc QgsQuickFeatureHighlight::color + void colorChanged(); + + //! \copydoc QgsQuickFeatureHighlight::width + void widthChanged(); + + //! \copydoc QgsQuickFeatureHighlight::mapSettings + void mapSettingsChanged(); + + private slots: + void markDirty(); + void onMapSettingsChanged(); + + private: + QSGNode *updatePaintNode( QSGNode *n, UpdatePaintNodeData * ) override; + + QColor mColor = Qt::yellow; + bool mDirty = false; + float mWidth = 20; + QgsQuickFeatureLayerPair mFeatureLayerPair; + QgsQuickMapSettings *mMapSettings = nullptr; // not owned + QgsQuickMapTransform mTransform; +}; + +#endif // QGSQUICKFEATUREHIGHLIGHT_H diff --git a/src/quickgui/qgsquickfeaturelayerpair.cpp b/src/quickgui/qgsquickfeaturelayerpair.cpp new file mode 100644 index 00000000000..8c5395ce562 --- /dev/null +++ b/src/quickgui/qgsquickfeaturelayerpair.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + qgsquickfeaturelayerpair.cpp + --------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 "qgsvectorlayer.h" +#include "qgsfeature.h" + +#include "qgsquickfeaturelayerpair.h" + +QgsQuickFeatureLayerPair::QgsQuickFeatureLayerPair() = default; + +QgsQuickFeatureLayerPair::QgsQuickFeatureLayerPair( const QgsFeature &feature, QgsVectorLayer *layer ) + : mLayer( layer ) + , mFeature( feature ) +{ +} + +QgsVectorLayer *QgsQuickFeatureLayerPair::layer() const +{ + return mLayer; +} + +QgsFeature QgsQuickFeatureLayerPair::feature() const +{ + return mFeature; +} + +bool QgsQuickFeatureLayerPair::isValid() const +{ + return ( mLayer && mFeature.isValid() && hasValidGeometry() ); +} + +bool QgsQuickFeatureLayerPair::operator==( const QgsQuickFeatureLayerPair &other ) const +{ + return ( mLayer == other.layer() ) && ( mFeature == other.feature() ); +} + +bool QgsQuickFeatureLayerPair::operator!=( const QgsQuickFeatureLayerPair &other ) const +{ + return ( mLayer != other.layer() ) || ( mFeature != other.feature() ); +} + +bool QgsQuickFeatureLayerPair::hasValidGeometry() const +{ + Q_ASSERT( mLayer ); + + if ( !mFeature.hasGeometry() ) + return false; + + if ( mFeature.geometry().type() != mLayer->geometryType() ) + return false; + + if ( QgsWkbTypes::hasZ( mLayer->wkbType() ) != QgsWkbTypes::hasZ( mFeature.geometry().wkbType() ) ) + return false; + + return true; +} diff --git a/src/quickgui/qgsquickfeaturelayerpair.h b/src/quickgui/qgsquickfeaturelayerpair.h new file mode 100644 index 00000000000..659a3c0016e --- /dev/null +++ b/src/quickgui/qgsquickfeaturelayerpair.h @@ -0,0 +1,102 @@ +/*************************************************************************** + qgsquickfeaturelayerpair.h + --------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Peter Petrik + Email : zilolv 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 QGSQUICKFEATURELAYERPAIR_H +#define QGSQUICKFEATURELAYERPAIR_H + +#include + +#include "qgsfeature.h" + +#include "qgis_quick.h" + +class QgsVectorLayer; + +/** + * \ingroup quick + * Pair of QgsFeature and QgsVectorLayer + * + * Vector layer is commonly used to gather geometry type or CRS + * for the feature. + * + * Note that the feature may or may not be part of the layer's features + * + * \note QML Type: QgsQuickFeatureLayerPair + * + * \since QGIS 3.4 + */ +class QUICK_EXPORT QgsQuickFeatureLayerPair +{ + Q_GADGET + + /** + * Vector layer to which the feature belongs. May be nullptr if pair is not valid + * + * This is a readonly property. + */ + Q_PROPERTY( QgsVectorLayer *layer READ layer ) + + /** + * Feature that belongs to layer. + * + * This is a readonly property. + */ + Q_PROPERTY( QgsFeature feature READ feature ) + + /** + * Whether + * - layer is not nullptr + * - feature is valid + * - feature has geometry and the geometry is the same as geometry expected by layer + * + * This is a readonly property. + */ + Q_PROPERTY( bool valid READ isValid ) + + public: + //! Constructs invalid feature-layer pair. + QgsQuickFeatureLayerPair(); + + /** + * Constructor of a new feature-layer pair + * \param feature QgsFeature associated. + * \param layer Vector layer which the feature belongs to + */ + QgsQuickFeatureLayerPair( const QgsFeature &feature, QgsVectorLayer *layer ); + + //! \copydoc QgsQuickFeatureLayerPair::layer + QgsVectorLayer *layer() const; + + //! \copydoc QgsQuickFeatureLayerPair::feature + QgsFeature feature() const; + + //! \copydoc QgsQuickFeatureLayerPair::valid + bool isValid() const; + + bool operator==( const QgsQuickFeatureLayerPair &other ) const; + bool operator!=( const QgsQuickFeatureLayerPair &other ) const; + + private: + bool hasValidGeometry() const; + + QgsVectorLayer *mLayer = nullptr; // not owned + QgsFeature mFeature; +}; + +typedef QList QgsQuickFeatureLayerPairs; + +Q_DECLARE_METATYPE( QgsQuickFeatureLayerPair ) + +#endif // QGSQUICKFEATURELAYERPAIR_H diff --git a/src/quickgui/qgsquickhighlightsgnode.cpp b/src/quickgui/qgsquickhighlightsgnode.cpp new file mode 100644 index 00000000000..9e6a5c8aa54 --- /dev/null +++ b/src/quickgui/qgsquickhighlightsgnode.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** + qgsquickhighlightsgnode.cpp + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 "qgsquickhighlightsgnode.h" + +#include "qgstessellator.h" +#include "qgsgeometrycollection.h" +#include "qgsgeometry.h" +#include "qgslinestring.h" +#include "qgspoint.h" +#include "qgspolygon.h" + +QgsQuickHighlightSGNode::QgsQuickHighlightSGNode( const QgsGeometry &geom, + const QColor &color, float width ) + : QSGNode() + , mWidth( width ) +{ + mMaterial.setColor( color ); + handleGeometryCollection( geom.constGet(), geom.type() ); +} + +void QgsQuickHighlightSGNode::handleGeometryCollection( const QgsAbstractGeometry *geom, QgsWkbTypes::GeometryType type ) +{ + const QgsGeometryCollection *collection = qgsgeometry_cast( geom ); + if ( collection && !collection->isEmpty() ) + { + for ( int i = 0; i < collection->numGeometries(); ++i ) + { + const QgsAbstractGeometry *geomN = collection->geometryN( i ); + handleSingleGeometry( geomN, type ); + } + } + else + { + handleSingleGeometry( geom, type ); + } +} + +void QgsQuickHighlightSGNode::handleSingleGeometry( const QgsAbstractGeometry *geom, QgsWkbTypes::GeometryType type ) +{ + switch ( type ) + { + case QgsWkbTypes::PointGeometry: + { + const QgsPoint *point = qgsgeometry_cast( geom ); + if ( point ) + appendChildNode( createPointGeometry( point ) ); + break; + } + + case QgsWkbTypes::LineGeometry: + { + const QgsLineString *line = qgsgeometry_cast( geom ); + if ( line ) + appendChildNode( createLineGeometry( line ) ); + break; + } + + case QgsWkbTypes::PolygonGeometry: + { + const QgsPolygon *poly = qgsgeometry_cast( geom ); + if ( poly ) + appendChildNode( createPolygonGeometry( poly ) ); + break; + } + + case QgsWkbTypes::UnknownGeometry: + case QgsWkbTypes::NullGeometry: + break; + } +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createLineGeometry( const QgsLineString *line ) +{ + Q_ASSERT( line ); + + std::unique_ptr node = qgis::make_unique< QSGGeometryNode>(); + std::unique_ptr sgGeom = qgis::make_unique< QSGGeometry>( QSGGeometry::defaultAttributes_Point2D(), line->numPoints() ); + QSGGeometry::Point2D *vertices = sgGeom->vertexDataAsPoint2D(); + + const double *x = line->xData(); + const double *y = line->yData(); + + for ( int i = 0; i < line->numPoints(); ++i ) + { + vertices[i].set( + static_cast< float >( x[i] ), + static_cast< float >( y[i] ) + ); + } + + sgGeom->setLineWidth( mWidth ); + sgGeom->setDrawingMode( GL_LINE_STRIP ); + node->setGeometry( sgGeom.release() ); + node->setMaterial( &mMaterial ); + node->setFlag( QSGNode::OwnsGeometry ); + node->setFlag( QSGNode::OwnedByParent ); + return node.release(); +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createPointGeometry( const QgsPoint *point ) +{ + Q_ASSERT( point ); + + std::unique_ptr node = qgis::make_unique< QSGGeometryNode>(); + std::unique_ptr sgGeom = qgis::make_unique( QSGGeometry::defaultAttributes_Point2D(), 1 ); + + QSGGeometry::Point2D *vertices = sgGeom->vertexDataAsPoint2D(); + vertices[0].set( + static_cast< float >( point->x() ), + static_cast< float >( point->y() ) + ); + sgGeom->setDrawingMode( GL_POINTS ); + sgGeom->setLineWidth( mWidth ); + + node->setGeometry( sgGeom.release() ); + node->setMaterial( &mMaterial ); + node->setFlag( QSGNode::OwnsGeometry ); + node->setFlag( QSGNode::OwnedByParent ); + return node.release(); +} + +QSGGeometryNode *QgsQuickHighlightSGNode::createPolygonGeometry( const QgsPolygon *polygon ) +{ + Q_ASSERT( polygon ); + + const QgsRectangle bounds = polygon->boundingBox(); + QgsTessellator tes( bounds.xMinimum(), bounds.yMinimum(), false, false, false ); + tes.addPolygon( *polygon, 0.0 ); + + QSGGeometryNode *node = new QSGGeometryNode; + QSGGeometry *sgGeom = new QSGGeometry( QSGGeometry::defaultAttributes_Point2D(), tes.dataVerticesCount() ); + + QSGGeometry::Point2D *vertices = sgGeom->vertexDataAsPoint2D(); + + // we need to revert translation in tessellator + float translateX = static_cast< float >( bounds.xMinimum() ); + float translateY = static_cast< float >( bounds.yMinimum() ); + + const QVector data = tes.data(); + int i = 0; + for ( auto it = data.constBegin(); it != data.constEnd(); ) + { + float x = *it; + vertices[i].x = translateX + x; + ++it; + + ++it; // we do not need z coordinate + + float y = -( *it ); + vertices[i].y = translateY + y; + ++it; + + ++i; + } + sgGeom->setDrawingMode( GL_TRIANGLES ); + node->setGeometry( sgGeom ); + node->setMaterial( &mMaterial ); + node->setFlag( QSGNode::OwnsGeometry ); + node->setFlag( QSGNode::OwnedByParent ); + return node; +} diff --git a/src/quickgui/qgsquickhighlightsgnode.h b/src/quickgui/qgsquickhighlightsgnode.h new file mode 100644 index 00000000000..e3958cb65c4 --- /dev/null +++ b/src/quickgui/qgsquickhighlightsgnode.h @@ -0,0 +1,65 @@ +/*************************************************************************** + qgsquickhighlightsgnode.h + -------------------------------------- + Date : Nov 2017 + Copyright : (C) 2017 by Matthias Kuhn + Email : matthias@opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKHIGHLIGHTSGNODE_H +#define QGSQUICKHIGHLIGHTSGNODE_H + +#include +#include + +#include "qgsgeometry.h" +#include "qgis_quick.h" + +class QgsLineString; +class QgsPoint; +class QgsPolygon; + +/** + * \ingroup quick + * + * This is used to transform (render) QgsGeometry to node for QtQuick scene graph. + * + * \note QML Type: not exported + * + * \since QGIS 3.4 + */ +class QUICK_EXPORT QgsQuickHighlightSGNode : public QSGNode +{ + public: + + /** + * Constructor of new QT Quick scene node based on geometry + * + * \param geom Geometry to render in the map coordinates + * \param color color used to render geom + * \param width width of pen, see QSGGeometry::setLineWidth() + */ + QgsQuickHighlightSGNode( const QgsGeometry &geom, const QColor &color, float width ); + //! Destructor + ~QgsQuickHighlightSGNode() = default; + + private: + void handleGeometryCollection( const QgsAbstractGeometry *geom, QgsWkbTypes::GeometryType type ); + void handleSingleGeometry( const QgsAbstractGeometry *geom, QgsWkbTypes::GeometryType type ); + + QSGGeometryNode *createLineGeometry( const QgsLineString *line ); + QSGGeometryNode *createPointGeometry( const QgsPoint *point ); + QSGGeometryNode *createPolygonGeometry( const QgsPolygon *polygon ); + + QSGFlatColorMaterial mMaterial; + float mWidth = 20; +}; + +#endif // QGSQUICKHIGHLIGHTSGNODE diff --git a/src/quickgui/qgsquickidentifykit.cpp b/src/quickgui/qgsquickidentifykit.cpp new file mode 100644 index 00000000000..4ad68aaa4c3 --- /dev/null +++ b/src/quickgui/qgsquickidentifykit.cpp @@ -0,0 +1,259 @@ +/*************************************************************************** + qgsquickidentifykit.cpp + --------------------- + Date : 30.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgsmessagelog.h" +#include "qgsproject.h" +#include "qgslogger.h" +#include "qgsrenderer.h" +#include "qgsvectorlayer.h" + +#include "qgsquickidentifykit.h" +#include "qgsquickmapsettings.h" + +#include "qgis.h" + +QgsQuickIdentifyKit::QgsQuickIdentifyKit( QObject *parent ) + : QObject( parent ) +{ +} + +QgsQuickMapSettings *QgsQuickIdentifyKit::mapSettings() const +{ + return mMapSettings; +} + +void QgsQuickIdentifyKit::setMapSettings( QgsQuickMapSettings *mapSettings ) +{ + if ( mapSettings == mMapSettings ) + return; + + mMapSettings = mapSettings; + emit mapSettingsChanged(); +} + +QgsQuickFeatureLayerPairs QgsQuickIdentifyKit::identify( const QPointF &point, QgsVectorLayer *layer ) +{ + QgsQuickFeatureLayerPairs results; + + if ( !mMapSettings ) + { + QgsDebugMsg( QStringLiteral( "Unable to use IdentifyKit without mapSettings property set." ) ); + return results; + } + QgsPointXY mapPoint = mMapSettings->mapSettings().mapToPixel().toMapCoordinates( point.toPoint() ); + + if ( layer ) + { + QgsFeatureList featureList = identifyVectorLayer( layer, mapPoint ); + for ( const QgsFeature &feature : featureList ) + { + results.append( QgsQuickFeatureLayerPair( feature, layer ) ); + } + QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results for layer %2" ).arg( results.count() ).arg( layer->name() ) ); + } + else + { + QStringList noIdentifyLayerIdList; + if ( mMapSettings->project() ) + { + noIdentifyLayerIdList = mMapSettings->project()->nonIdentifiableLayers(); + } + + for ( QgsMapLayer *layer : mMapSettings->mapSettings().layers() ) + { + if ( mMapSettings->project() && noIdentifyLayerIdList.contains( layer->id() ) ) + continue; + + QgsVectorLayer *vl = qobject_cast( layer ); + if ( vl ) + { + QgsFeatureList featureList = identifyVectorLayer( vl, mapPoint ); + + for ( const QgsFeature &feature : featureList ) + { + results.append( QgsQuickFeatureLayerPair( feature, vl ) ); + } + } + } + + QgsDebugMsg( QStringLiteral( "IdentifyKit identified %1 results" ).arg( results.count() ) ); + } + + return results; +} + +static QgsQuickFeatureLayerPair _closestFeature( const QgsQuickFeatureLayerPairs &results, const QgsMapSettings &mapSettings, const QPointF &point ) +{ + QgsPointXY mapPoint = mapSettings.mapToPixel().toMapCoordinates( point.toPoint() ); + QgsGeometry mapPointGeom( QgsGeometry::fromPointXY( mapPoint ) ); + + double distMin = 1e10; + int iMin = -1; + for ( int i = 0; i < results.count(); ++i ) + { + const QgsQuickFeatureLayerPair &res = results.at( i ); + QgsGeometry geom( res.feature().geometry() ); + try + { + geom.transform( mapSettings.layerTransform( res.layer() ) ); + } + catch ( QgsCsException &e ) + { + Q_UNUSED( e ); + // Caught an error in transform + continue; + } + + double dist = geom.distance( mapPointGeom ); + if ( dist < distMin ) + { + iMin = i; + distMin = dist; + } + } + + if ( results.empty() ) + { + return QgsQuickFeatureLayerPair(); + } + else + { + return results.at( iMin ); + } +} + +QgsQuickFeatureLayerPair QgsQuickIdentifyKit::identifyOne( const QPointF &point, QgsVectorLayer *layer ) +{ + QgsQuickFeatureLayerPairs results = identify( point, layer ); + return _closestFeature( results, mMapSettings->mapSettings(), point ); +} + +QgsFeatureList QgsQuickIdentifyKit::identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const +{ + QgsFeatureList results; + + if ( !layer || !layer->isSpatial() ) + return results; + + if ( !layer->isInScaleRange( mMapSettings->mapSettings().scale() ) ) + return results; + + QgsFeatureList featureList; + + // toLayerCoordinates will throw an exception for an 'invalid' point. + // For example, if you project a world map onto a globe using EPSG 2163 + // and then click somewhere off the globe, an exception will be thrown. + try + { + // create the search rectangle + double searchRadius = searchRadiusMU(); + + QgsRectangle r; + r.setXMinimum( point.x() - searchRadius ); + r.setXMaximum( point.x() + searchRadius ); + r.setYMinimum( point.y() - searchRadius ); + r.setYMaximum( point.y() + searchRadius ); + + r = toLayerCoordinates( layer, r ); + + QgsFeatureRequest req; + req.setFilterRect( r ); + req.setLimit( mFeaturesLimit ); + req.setFlags( QgsFeatureRequest::ExactIntersect ); + + QgsFeatureIterator fit = layer->getFeatures( req ); + QgsFeature f; + while ( fit.nextFeature( f ) ) + featureList << QgsFeature( f ); + } + catch ( QgsCsException &cse ) + { + QgsDebugMsg( tr( "Invalid point and proceed with no features found." ) ); + Q_UNUSED( cse ); + } + + bool filter = false; + + QgsRenderContext context( QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ) ); + context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); + QgsFeatureRenderer *renderer = layer->renderer(); + if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) + { + // setup scale for scale dependent visibility (rule based) + renderer->startRender( context, layer->fields() ); + filter = renderer->capabilities() & QgsFeatureRenderer::Filter; + } + + for ( const QgsFeature &feature : featureList ) + { + context.expressionContext().setFeature( feature ); + + if ( filter && !renderer->willRenderFeature( const_cast( feature ), context ) ) + continue; + + results.append( feature ); + } + + if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent ) + { + renderer->stopRender( context ); + } + + return results; +} + +double QgsQuickIdentifyKit::searchRadiusMU( const QgsRenderContext &context ) const +{ + return mSearchRadiusMm * context.scaleFactor() * context.mapToPixel().mapUnitsPerPixel(); +} + +double QgsQuickIdentifyKit::searchRadiusMU() const +{ + QgsRenderContext context = QgsRenderContext::fromMapSettings( mMapSettings->mapSettings() ); + return searchRadiusMU( context ); +} + +QgsRectangle QgsQuickIdentifyKit::toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const +{ + return mMapSettings->mapSettings().mapToLayerCoordinates( layer, rect ); +} + +double QgsQuickIdentifyKit::searchRadiusMm() const +{ + return mSearchRadiusMm; +} + +void QgsQuickIdentifyKit::setSearchRadiusMm( double searchRadiusMm ) +{ + if ( qgsDoubleNear( mSearchRadiusMm, searchRadiusMm ) ) + return; + + mSearchRadiusMm = searchRadiusMm; + emit searchRadiusMmChanged(); +} + +int QgsQuickIdentifyKit::featuresLimit() const +{ + return mFeaturesLimit; +} + +void QgsQuickIdentifyKit::setFeaturesLimit( int limit ) +{ + if ( mFeaturesLimit == limit ) + return; + + mFeaturesLimit = limit; + emit featuresLimitChanged(); +} diff --git a/src/quickgui/qgsquickidentifykit.h b/src/quickgui/qgsquickidentifykit.h new file mode 100644 index 00000000000..652ed4db1e1 --- /dev/null +++ b/src/quickgui/qgsquickidentifykit.h @@ -0,0 +1,139 @@ +/*************************************************************************** + qgsquickidentifykit.h + --------------------- + Date : 30.8.2016 + Copyright : (C) 2016 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKIDENTIFYKIT_H +#define QGSQUICKIDENTIFYKIT_H + +#include +#include + +#include "qgsfeature.h" +#include "qgsmapsettings.h" +#include "qgspoint.h" +#include "qgsrendercontext.h" + +#include "qgis_quick.h" +#include "qgsquickfeaturelayerpair.h" + +class QgsMapLayer; +class QgsQuickMapSettings; +class QgsVectorLayer; + +/** + * \ingroup quick + * + * Convenient set of tools to identify features + * + * - get a list of features in a defined radius from a point. + * - get a feature with the closest distance to the point + * + * \note QML Type: IdentifyKit + * + * \since QGIS 3.4 + */ +class QUICK_EXPORT QgsQuickIdentifyKit : public QObject +{ + Q_OBJECT + + /** + * Map settings. Set directly when creating QML object. + */ + Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) + + /** + * Search radius for the identify functions + * + * Default is 8. + */ + Q_PROPERTY( double searchRadiusMm READ searchRadiusMm WRITE setSearchRadiusMm NOTIFY searchRadiusMmChanged ) + + /** + * Maximum number of features returned from the QgsQuickIdentifyKit::identify() + * + * Default is 100. + */ + Q_PROPERTY( int featuresLimit READ featuresLimit WRITE setFeaturesLimit NOTIFY featuresLimitChanged ) + + public: + //! Constructor of new identify kit. + explicit QgsQuickIdentifyKit( QObject *parent = nullptr ); + + //! \copydoc QgsQuickIdentifyKit::mapSettings + QgsQuickMapSettings *mapSettings() const; + + //! \copydoc QgsQuickIdentifyKit::mapSettings + void setMapSettings( QgsQuickMapSettings *mapSettings ); + + //! \copydoc QgsQuickIdentifyKit::searchRadiusMm + double searchRadiusMm() const; + + //! \copydoc QgsQuickIdentifyKit::searchRadiusMm + void setSearchRadiusMm( double searchRadiusMm ); + + //! \copydoc QgsQuickIdentifyKit::featuresLimit + int featuresLimit() const; + + //! \copydoc QgsQuickIdentifyKit::featuresLimit + void setFeaturesLimit( int limit ); + + /** + * Gets the closest feature to the point within the search radius + * + * If layer is nullptr, identifies the closest feature from all identifiable layers + * If layer is not nullptr, identifies the closest feature from given layer + * + * To modify search radius, use QgsQuickIdentifyKit::searchRadiusMm + * + * \param point position to search a feature from + * \param layer if defined, search for a feature only from this layer + */ + Q_INVOKABLE QgsQuickFeatureLayerPair identifyOne( const QPointF &point, QgsVectorLayer *layer = nullptr ); + + /** + * Gets all features in the search radius + * + * If layer is nullptr, identifies features from all identifiable layers + * If layer is not nullptr, identifies only features from given layer + * + * To limit number of results, use QgsQuickIdentifyKit::featuresLimit + * To modify search radius, use QgsQuickIdentifyKit::searchRadiusMm + * + * \param point position to search features ob + * \param layer if defined, search for features only from this layer + */ + Q_INVOKABLE QgsQuickFeatureLayerPairs identify( const QPointF &point, QgsVectorLayer *layer = nullptr ); + + signals: + //! \copydoc QgsQuickIdentifyKit::mapSettings + void mapSettingsChanged(); + //! \copydoc QgsQuickIdentifyKit::searchRadiusMm + void searchRadiusMmChanged(); + //! \copydoc QgsQuickIdentifyKit::featuresLimit + void featuresLimitChanged(); + + private: + QgsQuickMapSettings *mMapSettings = nullptr; // not owned + + double searchRadiusMU( const QgsRenderContext &context ) const; + double searchRadiusMU() const; + + QgsRectangle toLayerCoordinates( QgsMapLayer *layer, const QgsRectangle &rect ) const; + QgsFeatureList identifyVectorLayer( QgsVectorLayer *layer, const QgsPointXY &point ) const; + + double mSearchRadiusMm = 8; + int mFeaturesLimit = 100; +}; + +#endif // QGSQUICKIDENTIFYKIT_H diff --git a/src/quickgui/qgsquickmaptransform.cpp b/src/quickgui/qgsquickmaptransform.cpp new file mode 100644 index 00000000000..11400b60720 --- /dev/null +++ b/src/quickgui/qgsquickmaptransform.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** + qgsquickmaptransform.cpp + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 "qgsquickmaptransform.h" +#include "qgsquickmapsettings.h" + +void QgsQuickMapTransform::applyTo( QMatrix4x4 *matrix ) const +{ + *matrix *= mMatrix; + matrix->optimize(); +} + +QgsQuickMapSettings *QgsQuickMapTransform::mapSettings() const +{ + return mMapSettings; +} + +void QgsQuickMapTransform::setMapSettings( QgsQuickMapSettings *mapSettings ) +{ + if ( mapSettings == mMapSettings ) + return; + + if ( mMapSettings ) + disconnect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickMapTransform::updateMatrix ); + + mMapSettings = mapSettings; + + if ( mMapSettings ) + connect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickMapTransform::updateMatrix ); + + emit mapSettingsChanged(); +} + +void QgsQuickMapTransform::updateMatrix() +{ + QMatrix4x4 matrix; + float scaleFactor = static_cast( 1.0 / mMapSettings->mapUnitsPerPixel() ); + + matrix.scale( scaleFactor, -scaleFactor ); + matrix.translate( static_cast( -mMapSettings->visibleExtent().xMinimum( ) ), + static_cast( -mMapSettings->visibleExtent().yMaximum() ) ); + + mMatrix = matrix; + update(); +} diff --git a/src/quickgui/qgsquickmaptransform.h b/src/quickgui/qgsquickmaptransform.h new file mode 100644 index 00000000000..eb47d6c0e8c --- /dev/null +++ b/src/quickgui/qgsquickmaptransform.h @@ -0,0 +1,77 @@ +/*************************************************************************** + qgsquickmaptransform.h + -------------------------------------- + Date : 27.12.2014 + Copyright : (C) 2014 by Matthias Kuhn + Email : matthias (at) opengis.ch + *************************************************************************** + * * + * 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 QGSQUICKMAPTRANSFORM_H +#define QGSQUICKMAPTRANSFORM_H + +#include +#include + +#include "qgis_quick.h" + +class QgsQuickMapSettings; + +/** + * \ingroup quick + * The QgsQuickMapTransform is transformation that can be attached to any QQuickItem. + * + * If the item is based on the map coordinates, QgsQuickMapTransform will + * transform it to the device coordintes based on the attached map settings. + * + * \note QML Type: MapTransform + * + * \since QGIS 3.4 + */ +class QUICK_EXPORT QgsQuickMapTransform : public QQuickTransform +{ + Q_OBJECT + + /** + * Associated map settings. Should be initialized before the first use from mapcanvas map settings. + */ + Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged ) + + public: + //! create new map transform + QgsQuickMapTransform() = default; + ~QgsQuickMapTransform() = default; + + /** + * Apply transformation based on current map settings to a matrix. + * + * Also optimize resulting matrix after transformation + * \param matrix Matrix to be transformed + */ + void applyTo( QMatrix4x4 *matrix ) const; + + //! \copydoc QgsQuickMapTransform::mapSettings + QgsQuickMapSettings *mapSettings() const; + + //! \copydoc QgsQuickMapTransform::mapSettings + void setMapSettings( QgsQuickMapSettings *mapSettings ); + + signals: + //! \copydoc QgsQuickMapTransform::mapSettings + void mapSettingsChanged(); + + private slots: + void updateMatrix(); + + private: + QgsQuickMapSettings *mMapSettings = nullptr; + QMatrix4x4 mMatrix; +}; + +#endif // QGSQUICKMAPTRANSFORM_H diff --git a/src/quickgui/qgsquickutils.cpp b/src/quickgui/qgsquickutils.cpp index 7ad60f6d478..492f6f631d5 100644 --- a/src/quickgui/qgsquickutils.cpp +++ b/src/quickgui/qgsquickutils.cpp @@ -18,7 +18,9 @@ #include "qgis.h" #include "qgsdistancearea.h" #include "qgslogger.h" +#include "qgsvectorlayer.h" +#include "qgsquickfeaturelayerpair.h" #include "qgsquickmapsettings.h" #include "qgsquickutils.h" #include "qgsunittypes.h" @@ -52,6 +54,11 @@ void QgsQuickUtils::logMessage( const QString &message, const QString &tag, Qgis QgsMessageLog::logMessage( message, tag, level ); } +QgsQuickFeatureLayerPair QgsQuickUtils::featureFactory( const QgsFeature &feature, QgsVectorLayer *layer ) const +{ + return QgsQuickFeatureLayerPair( feature, layer ); +} + QString QgsQuickUtils::dumpScreenInfo() const { QRect rec = QApplication::desktop()->screenGeometry(); diff --git a/src/quickgui/qgsquickutils.h b/src/quickgui/qgsquickutils.h index b17b162a2ef..a236c03156b 100644 --- a/src/quickgui/qgsquickutils.h +++ b/src/quickgui/qgsquickutils.h @@ -24,8 +24,11 @@ #include "qgsmessagelog.h" #include "qgsquickmapsettings.h" +#include "qgsquickfeaturelayerpair.h" #include "qgis_quick.h" +#include "qgsfeature.h" +class QgsVectorLayer; class QgsCoordinateReferenceSystem; /** @@ -73,6 +76,15 @@ class QUICK_EXPORT QgsQuickUtils: public QObject const QString &tag = QString( "QgsQuick" ), Qgis::MessageLevel level = Qgis::Warning ); + /** + * QgsQuickFeatureLayerPair factory for tuple of QgsFeature and QgsVectorLayer used in QgsQUick library. + * \param feature QgsFeature linked to new QgsQuickFeature instance. + * \param layer QgsVectorLayer which the feature belongs to, optional. + * + * \since QGIS 3.4 + */ + Q_INVOKABLE QgsQuickFeatureLayerPair featureFactory( const QgsFeature &feature, QgsVectorLayer *layer = nullptr ) const; + /** * Returns a string with information about screen size and resolution * diff --git a/tests/src/quickgui/CMakeLists.txt b/tests/src/quickgui/CMakeLists.txt index 4658b776f7a..bf6c55af774 100644 --- a/tests/src/quickgui/CMakeLists.txt +++ b/tests/src/quickgui/CMakeLists.txt @@ -78,6 +78,7 @@ ENDMACRO (ADD_QGIS_TEST) ############################################################# # Tests: +ADD_QGIS_TEST(qgsquickidentifykit testqgsquickidentifykit.cpp) ADD_QGIS_TEST(qgsquickutils testqgsquickutils.cpp) ADD_QGIS_TEST(qgsquickscalebarkit testqgsquickscalebarkit.cpp) diff --git a/tests/src/quickgui/app/main.qml b/tests/src/quickgui/app/main.qml index c29f5a32097..3f62f572d1f 100644 --- a/tests/src/quickgui/app/main.qml +++ b/tests/src/quickgui/app/main.qml @@ -32,12 +32,26 @@ ApplicationWindow { mapSettings.project: __project mapSettings.layers: __layers + QgsQuick.IdentifyKit { + id: identifyKit + mapSettings: mapCanvas.mapSettings + } + onClicked: { var screenPoint = Qt.point(mouse.x, mouse.y) - console.log("clicked:" + screenPoint) + var res = identifyKit.identifyOne(screenPoint); + highlight.featureLayerPair = res } } + QgsQuick.FeatureHighlight { + anchors.fill: mapCanvas + id: highlight + color: "red" + mapSettings: mapCanvas.mapSettings + z: 1 + } + Drawer { id: logPanel visible: true diff --git a/tests/src/quickgui/testqgsquickidentifykit.cpp b/tests/src/quickgui/testqgsquickidentifykit.cpp new file mode 100644 index 00000000000..db4c422a8a1 --- /dev/null +++ b/tests/src/quickgui/testqgsquickidentifykit.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + testqgsquickidentifykit.cpp.cpp + -------------------------------------- + Date : May 2018 + Copyright : (C) 2018 by Viktor Sklencar + Email : vsklencar 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 +#include +#include + +#include "qgsapplication.h" +#include "qgstest.h" +#include "qgis.h" + +#include "qgsvectorlayer.h" +#include "qgsfeature.h" +#include "qgsgeometry.h" +#include "qgsvectordataprovider.h" + +#include "qgsquickmapcanvasmap.h" +#include "qgsquickidentifykit.h" + + +class TestQgsQuickScaleBarKit: public QObject +{ + Q_OBJECT + private slots: + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + + void identifyOne(); // tests identifyOne function without given layer + void identifyOneDefinedVector(); // tests identifyOne function with given layer + void identifyInRadius(); +}; + +void TestQgsQuickScaleBarKit::identifyOne() +{ + QgsCoordinateReferenceSystem crsGPS = QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); + QVERIFY( crsGPS.authid() == "EPSG:4326" ); + + QgsRectangle extent = QgsRectangle( -120, 23, -82, 47 ); + QgsQuickMapCanvasMap canvas; + + QgsVectorLayer *tempLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ); + QVERIFY( tempLayer->isValid() ); + + QgsQuickMapSettings *ms = canvas.mapSettings(); + ms->setDestinationCrs( crsGPS ); + ms->setExtent( extent ); + ms->setOutputSize( QSize( 1000, 500 ) ); + ms->setLayers( QList() << tempLayer ); + + QgsQuickIdentifyKit kit; + kit.setMapSettings( ms ); + + double pointX = -31.208; + double pointY = 20.407999999999998; + double pointX2 = pointX + 1; + + // add feature + QgsFeature f1( tempLayer->dataProvider()->fields(), 1 ); + QgsPointXY point( pointX, pointY ); + QgsGeometry geom = QgsGeometry::fromPointXY( point ) ; + f1.setGeometry( geom ); + + // add another feature + QgsFeature f2( tempLayer->dataProvider()->fields(), 1 ); + QgsPointXY point2( pointX2, pointY ); + QgsGeometry geom2 = QgsGeometry::fromPointXY( point2 ) ; + f2.setGeometry( geom2 ); + + tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 ); + + // exactly matches f1 point + QgsPointXY screenPoint( 1954.0, 554.0 ); + QgsQuickFeatureLayerPair identifiedFeature = kit.identifyOne( screenPoint.toQPointF() ); + QVERIFY( identifiedFeature.isValid() ); + QVERIFY( identifiedFeature.feature().geometry().asPoint() == point ); +} + +void TestQgsQuickScaleBarKit::identifyOneDefinedVector() +{ + QgsCoordinateReferenceSystem crsGPS = QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); + QVERIFY( crsGPS.authid() == "EPSG:4326" ); + + QgsRectangle extent = QgsRectangle( -120, 23, -82, 47 ); + QgsQuickMapCanvasMap canvas; + + QgsVectorLayer *tempLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ); + QVERIFY( tempLayer->isValid() ); + + QgsVectorLayer *tempLayer2 = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326" ), QStringLiteral( "vl2" ), QStringLiteral( "memory" ) ); + QVERIFY( tempLayer->isValid() ); + + QgsQuickMapSettings *ms = canvas.mapSettings(); + ms->setDestinationCrs( crsGPS ); + ms->setExtent( extent ); + ms->setOutputSize( QSize( 1000, 500 ) ); + ms->setLayers( QList() << tempLayer ); + + QgsQuickIdentifyKit kit; + kit.setMapSettings( ms ); + + double pointX = -31.208; + double pointY = 20.407999999999998; + double pointX2 = pointX + 1; + + // add feature + QgsFeature f1( tempLayer->dataProvider()->fields(), 1 ); + QgsPointXY point( pointX, pointY ); + QgsGeometry geom = QgsGeometry::fromPointXY( point ) ; + f1.setGeometry( geom ); + + // add another feature + QgsFeature f2( tempLayer2->dataProvider()->fields(), 1 ); + QgsPointXY point2( pointX2, pointY ); + QgsGeometry geom2 = QgsGeometry::fromPointXY( point2 ) ; + f2.setGeometry( geom2 ); + + tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 ); + tempLayer2->dataProvider()->addFeatures( QgsFeatureList() << f2 ); + + QgsPointXY screenPoint( 1954.0, 554.0 ); + QgsQuickFeatureLayerPair identifiedFeature = kit.identifyOne( screenPoint.toQPointF(), tempLayer2 ); + QVERIFY( identifiedFeature.isValid() ); + QVERIFY( identifiedFeature.feature().geometry().asPoint() == point2 ); + +} + +void TestQgsQuickScaleBarKit::identifyInRadius() +{ + QgsCoordinateReferenceSystem crsGPS = QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); + QVERIFY( crsGPS.authid() == "EPSG:4326" ); + + QgsRectangle extent = QgsRectangle( -120, 23, -82, 47 ); + QgsQuickMapCanvasMap canvas; + + QgsVectorLayer *tempLayer = new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ); + QVERIFY( tempLayer->isValid() ); + + QgsQuickMapSettings *ms = canvas.mapSettings(); + ms->setDestinationCrs( crsGPS ); + ms->setExtent( extent ); + ms->setOutputSize( QSize( 1000, 500 ) ); + ms->setLayers( QList() << tempLayer ); + + QgsQuickIdentifyKit kit; + kit.setMapSettings( ms ); + + double pointX = -31.208; + double pointY = 20.407999999999998; + double pointX2 = pointX + 5; + + QgsFeature f1( tempLayer->dataProvider()->fields(), 1 ); + QgsPointXY point( pointX, pointY ); + QgsGeometry geom = QgsGeometry::fromPointXY( point ) ; + f1.setGeometry( geom ); + + QgsFeature f2( tempLayer->dataProvider()->fields(), 1 ); + QgsPointXY point2( pointX2, pointY ); + QgsGeometry geom2 = QgsGeometry::fromPointXY( point2 ) ; + f2.setGeometry( geom2 ); + + tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 ); + + kit.setSearchRadiusMm( 1.0 ); + QgsPointXY screenPoint( 1954.0, 554.0 ); + QgsQuickFeatureLayerPairs res = kit.identify( screenPoint.toQPointF() ); + QVERIFY( res.size() == 1 ); + + kit.setSearchRadiusMm( 100.0 ); + res = kit.identify( screenPoint.toQPointF() ); + QVERIFY( res.size() == 2 ); +} + +QGSTEST_MAIN( TestQgsQuickScaleBarKit ) +#include "testqgsquickidentifykit.moc" diff --git a/tests/src/quickgui/testqgsquickutils.cpp b/tests/src/quickgui/testqgsquickutils.cpp index 425545ea0dc..4b5d01b635a 100644 --- a/tests/src/quickgui/testqgsquickutils.cpp +++ b/tests/src/quickgui/testqgsquickutils.cpp @@ -59,7 +59,7 @@ void TestQgsQuickUtils::screenUnitsToMeters() ms.setExtent( QgsRectangle( 49, 16, 50, 17 ) ); ms.setOutputSize( QSize( 1000, 500 ) ); double sutm = utils.screenUnitsToMeters( &ms, 1 ); - QVERIFY( fabs( sutm - 213 ) < 1.0 ); + QGSCOMPARENEAR( sutm, 213, 1.0 ); } QGSTEST_MAIN( TestQgsQuickUtils )