Merge pull request #7045 from PeterPetrik/qgs-quick-position_and_transformer

[feature] [qgsquick] Add PositionKit, PositionMarker and CoordinateTransformer
This commit is contained in:
Martin Dobias 2018-06-29 09:08:35 +02:00 committed by GitHub
commit 569db8e069
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1724 additions and 24 deletions

View File

@ -13,12 +13,13 @@ QGIS Quick consists of a Qt plugin that provides the QML components and of a sha
\subsection qgsquick_overview_widgets QML Classes
\subsubsection qgsquick_overview_widgets_mapcanvas MapCanvas
Similarly to QgsMapCanvas, this component can be used for displaying GIS data on a canvas. See also QgsQuickMapCanvasMap.
\subsubsection qgsquick_overview_widgets_positionmarker PositionMarker
The element refers to current position according gps location device connected to it. It holds information about longitude, latitude, altitude,
direction of the movement and accuracy of the signal. See also QgsQuickPositionKit.
\subsubsection qgsquick_overview_widgets_scalebar ScaleBar
A QML component that shows the scale ratio between its length and distance on the MapCanvas. There are predefined rounded values
for several zooming levels with 'm' or 'km' postfixes. After any zoom in/out event on canvas recalculates its properties and updates
text. See also QgsQuickScaleBarKit.
\subsubsection qgsquick_overview_widgets_messagelog MessageLog
A simple panel which can be used for publishing logs messages to a user such as basic information about the application or its status.
See also QgsQuickMessageLogModel.

View File

@ -1,4 +1,5 @@
# The following has been generated automatically from src/core/qgsunittypes.h
QgsUnitTypes.SystemOfMeasurement.baseClass = QgsUnitTypes
QgsUnitTypes.DistanceUnit.baseClass = QgsUnitTypes
QgsUnitTypes.AreaUnit.baseClass = QgsUnitTypes
QgsUnitTypes.AngleUnit.baseClass = QgsUnitTypes

View File

@ -26,6 +26,14 @@ Helper functions for various unit types.
static const QMetaObject staticMetaObject;
public:
enum SystemOfMeasurement
{
UnknownSystem,
MetricSystem,
ImperialSystem,
USCSSystem
};
enum DistanceUnit
{
DistanceMeters,

View File

@ -39,6 +39,16 @@ class CORE_EXPORT QgsUnitTypes
Q_GADGET
public:
//! Systems of unit measurement
enum SystemOfMeasurement
{
UnknownSystem = 0, //!< Unknown system of measurement
MetricSystem, //!< International System of Units (SI)
ImperialSystem, //!< British Imperial
USCSSystem //!< United States customary system
};
Q_ENUM( SystemOfMeasurement )
//! Units of distance
enum DistanceUnit
{

View File

@ -2,13 +2,16 @@
# sources
SET(QGIS_QUICK_GUI_MOC_HDRS
qgsquickfeaturelayerpair.h
qgsquickcoordinatetransformer.h
qgsquickfeaturehighlight.h
qgsquickidentifykit.h
qgsquickmapcanvasmap.h
qgsquickmapsettings.h
qgsquickmaptransform.h
qgsquickmessagelogmodel.h
qgsquickpositionkit.h
qgsquickscalebarkit.h
qgsquicksimulatedpositionsource.h
qgsquickutils.h
)
@ -18,6 +21,7 @@ SET(QGIS_QUICK_GUI_HDRS
SET(QGIS_QUICK_GUI_SRC
qgsquickfeaturelayerpair.cpp
qgsquickcoordinatetransformer.cpp
qgsquickfeaturehighlight.cpp
qgsquickhighlightsgnode.cpp
qgsquickidentifykit.cpp
@ -25,7 +29,9 @@ SET(QGIS_QUICK_GUI_SRC
qgsquickmapsettings.cpp
qgsquickmaptransform.cpp
qgsquickmessagelogmodel.cpp
qgsquickpositionkit.cpp
qgsquickscalebarkit.cpp
qgsquicksimulatedpositionsource.cpp
qgsquickutils.cpp
)
@ -69,6 +75,10 @@ INCLUDE_DIRECTORIES(SYSTEM
ADD_DEFINITIONS(-DCORE_EXPORT=)
SET(QGIS_QUICK_GUI_IMAGE_RCCS ./images/images.qrc)
QT5_ADD_RESOURCES(QGIS_QUICK_GUI_IMAGE_RCC_SRCS ${QGIS_QUICK_GUI_IMAGE_RCCS})
############################################################
# qgis_quick shared library
QT5_WRAP_CPP(QGIS_QUICK_GUI_MOC_SRCS ${QGIS_QUICK_GUI_MOC_HDRS})

View File

@ -0,0 +1,4 @@
<svg fill="#000000" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z"/>
</svg>

After

Width:  |  Height:  |  Size: 208 B

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>ic_navigation_black.svg</file>
</qresource>
</RCC>

View File

@ -12,6 +12,7 @@ SET(QGIS_QUICK_PLUGIN_SRC
SET(QGIS_QUICK_PLUGIN_RESOURCES
qgsquickmapcanvas.qml
qgsquickmessagelog.qml
qgsquickpositionmarker.qml
qgsquickscalebar.qml
qmldir
)
@ -108,8 +109,9 @@ IF(QMLPLUGINDUMP_FOUND)
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${QGIS_QUICK_TYPEINFO_GENERATE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:qgis_quick_plugin> ${QGIS_QUICK_TYPEINFO_GENERATE_DIR}
COMMAND ${QMLPLUGINDUMP_EXECUTABLE}
ARGS QgsQuick ${QGIS_QUICK_VERSION} . --output ${QGIS_QUICK_PLUGIN_TYPEINFO}
ARGS QgsQuick ${QGIS_QUICK_VERSION} . -noinstantiate --output ${QGIS_QUICK_PLUGIN_TYPEINFO}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating qgsquick.qmltypes with qmlplugindump"
POST_BUILD
)
ENDIF()

View File

@ -28,8 +28,10 @@
#include "qgsrelationmanager.h"
#include "qgscoordinatetransformcontext.h"
#include "qgsvectorlayer.h"
#include "qgsunittypes.h"
#include "qgsquickfeaturehighlight.h"
#include "qgsquickcoordinatetransformer.h"
#include "qgsquickidentifykit.h"
#include "qgsquickfeaturelayerpair.h"
#include "qgsquickmapcanvasmap.h"
@ -37,6 +39,7 @@
#include "qgsquickmaptransform.h"
#include "qgsquickmessagelogmodel.h"
#include "qgsquickplugin.h"
#include "qgsquickpositionkit.h"
#include "qgsquickscalebarkit.h"
#include "qgsquickutils.h"
@ -58,14 +61,22 @@ void QgsQuickPlugin::registerTypes( const char *uri )
qRegisterMetaType< QgsPoint >( "QgsPoint" );
qRegisterMetaType< QgsPointXY >( "QgsPointXY" );
qRegisterMetaType< QgsQuickFeatureLayerPair >( "QgsQuickFeatureLayerPair" );
qRegisterMetaType< QgsUnitTypes::SystemOfMeasurement >( "QgsUnitTypes::SystemOfMeasurement" );
qRegisterMetaType< QgsUnitTypes::DistanceUnit >( "QgsUnitTypes::DistanceUnit" );
qRegisterMetaType< QgsCoordinateFormatter::FormatFlags >( "QgsCoordinateFormatter::FormatFlags" );
qRegisterMetaType< QgsCoordinateFormatter::Format >( "QgsCoordinateFormatter::Format" );
qmlRegisterUncreatableType< QgsUnitTypes >( uri, 0, 1, "QgsUnitTypes", "Only enums from QgsUnitTypes can be used" );
qmlRegisterType< QgsProject >( uri, 0, 1, "Project" );
qmlRegisterType< QgsQuickFeatureHighlight >( uri, 0, 1, "FeatureHighlight" );
qmlRegisterType< QgsQuickCoordinateTransformer >( uri, 0, 1, "CoordinateTransformer" );
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< QgsQuickPositionKit >( uri, 0, 1, "PositionKit" );
qmlRegisterType< QgsQuickScaleBarKit >( uri, 0, 1, "ScaleBarKit" );
qmlRegisterType< QgsVectorLayer >( uri, 0, 1, "VectorLayer" );

View File

@ -0,0 +1,115 @@
/***************************************************************************
qgsquickpositionmarker.qml
--------------------------------------
Date : Dec 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. *
* *
***************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 2.2
import QtQml 2.2
import QtGraphicalEffects 1.0
import QgsQuick 0.1 as QgsQuick
/**
* \brief Graphical representation of physical location on the map.
*
* Source position and accuracy taken from PositionKit is drawn as a marker on the map.
* Marker is grayed out when position is not available. When PositionKit support reading of the accuracy,
* the circle is drawn around the marker. PositionKit must be connected, for example GPS position source from QgsQuickPositionKit.
*/
Item {
id: positionMarker
property int size: 48 * QgsQuick.Utils.dp
/**
* Utils for handling position.
*/
property QgsQuick.PositionKit positionKit
/**
* Color of the marker when position is known.
*/
property color baseColor: "darkblue"
/**
* Color of the marker when position is unknown (e.g. GPS signal lost).
*/
property color unavailableColor: "gray"
/**
* Whether circle representing accuracy of the position should be rendered.
*/
property bool withAccuracy: true
/**
* Icon for position marker.
*/
property var markerIcon: QgsQuick.Utils.getThemeIcon("ic_navigation_black")
/**
* Source position accuracy circle-shaped indicator around positionMarker.
*/
Rectangle {
id: accuracyIndicator
visible: withAccuracy &&
positionKit.hasPosition &&
(positionKit.accuracy > 0) &&
(accuracyIndicator.width > positionMarker.size / 2.0)
x: positionKit.screenPosition.x - width/2
y: positionKit.screenPosition.y - height/2
width:positionKit.screenAccuracy
height: accuracyIndicator.width
color: baseColor
border.color: "black"
border.width: 3 * QgsQuick.Utils.dp
radius: width*0.5
opacity: 0.1
}
/**
* Position marker.
*/
Rectangle {
id: navigationMarker
property int borderWidth: 2 * QgsQuick.Utils.dp
width: positionMarker.size + 20 * QgsQuick.Utils.dp
height: width
color: "white"
border.color: baseColor
border.width: borderWidth
radius: width*0.5
antialiasing: true
x: positionKit.screenPosition.x - width/2
y: positionKit.screenPosition.y - height/2
Image {
id: navigation
source: positionMarker.markerIcon
fillMode: Image.PreserveAspectFit
rotation: positionKit.direction
anchors.centerIn: parent
width: positionMarker.size
height: width
}
/**
* Makes positionMarker (navigation) grey if position is unknown.
*/
ColorOverlay {
anchors.fill: navigation
source: navigation
color: positionKit.hasPosition ? baseColor : unavailableColor
rotation: positionKit.direction
visible: !(positionKit.hasPosition)
}
}
}

View File

@ -32,6 +32,10 @@ Item {
* representation
*/
property alias preferredWidth: scaleBarKit.preferredWidth
/**
* Preferred system of measurement for the resulting distance. Default is metric system
*/
property alias systemOfMeasurement: scaleBarKit.systemOfMeasurement
/**
* Kit for all calculation of width and text of the scalebar
*

View File

@ -14,6 +14,7 @@ module QgsQuick
plugin qgis_quick_plugin
MapCanvas 0.1 qgsquickmapcanvas.qml
PositionMarker 0.1 qgsquickpositionmarker.qml
ScaleBar 0.1 qgsquickscalebar.qml
MessageLog 0.1 qgsquickmessagelog.qml

View File

@ -0,0 +1,109 @@
/***************************************************************************
qgsquickcoordinatetransformer.cpp
--------------------------------------
Date : 1.6.2017
Copyright : (C) 2017 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 "qgsquickcoordinatetransformer.h"
#include "qgslogger.h"
QgsQuickCoordinateTransformer::QgsQuickCoordinateTransformer( QObject *parent )
: QObject( parent )
{
mCoordinateTransform.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) );
}
QgsPoint QgsQuickCoordinateTransformer::projectedPosition() const
{
return mProjectedPosition;
}
QgsPoint QgsQuickCoordinateTransformer::sourcePosition() const
{
return mSourcePosition;
}
void QgsQuickCoordinateTransformer::setSourcePosition( const QgsPoint &sourcePosition )
{
if ( mSourcePosition == sourcePosition )
return;
mSourcePosition = sourcePosition;
emit sourcePositionChanged();
updatePosition();
}
QgsCoordinateReferenceSystem QgsQuickCoordinateTransformer::destinationCrs() const
{
return mCoordinateTransform.destinationCrs();
}
void QgsQuickCoordinateTransformer::setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs )
{
if ( destinationCrs == mCoordinateTransform.destinationCrs() )
return;
mCoordinateTransform.setDestinationCrs( destinationCrs );
emit destinationCrsChanged();
updatePosition();
}
QgsCoordinateReferenceSystem QgsQuickCoordinateTransformer::sourceCrs() const
{
return mCoordinateTransform.sourceCrs();
}
void QgsQuickCoordinateTransformer::setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs )
{
if ( sourceCrs == mCoordinateTransform.sourceCrs() )
return;
mCoordinateTransform.setSourceCrs( sourceCrs );
emit sourceCrsChanged();
updatePosition();
}
void QgsQuickCoordinateTransformer::setTransformContext( const QgsCoordinateTransformContext &context )
{
mCoordinateTransform.setContext( context );
}
void QgsQuickCoordinateTransformer::updatePosition()
{
double x = mSourcePosition.x();
double y = mSourcePosition.y();
double z = mSourcePosition.z();
// If Z is NaN, coordinate transformation (proj4) will
// also set X and Y to NaN. But we also want to get projected
// coords if we do not have any Z coordinate.
if ( std::isnan( z ) )
{
z = 0;
}
try
{
mCoordinateTransform.transformInPlace( x, y, z );
}
catch ( const QgsCsException &exp )
{
QgsDebugMsg( exp.what() );
}
mProjectedPosition = QgsPoint( x, y );
mProjectedPosition.addZValue( mSourcePosition.z() );
emit projectedPositionChanged();
}

View File

@ -0,0 +1,110 @@
/***************************************************************************
qgsquickcoordinatetransformer.h
--------------------------------------
Date : 1.6.2017
Copyright : (C) 2017 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 QGSQUICKCOORDINATETRANSFORMER_H
#define QGSQUICKCOORDINATETRANSFORMER_H
#include <QObject>
#include "qgspoint.h"
#include "qgis_quick.h"
#include "qgscoordinatetransformcontext.h"
#include "qgscoordinatereferencesystem.h"
#include "qgspoint.h"
/**
* \ingroup quick
* Helper class for transform of coordinates (QgsPoint) to a different coordinate reference system.
*
* It requires connection of transformation context from mapSettings, source position and source CRS to
* calculate projected position in desired destination CRS
*
* \note QML Type: CoordinateTransformer
*
* \since QGIS 3.4
*/
class QUICK_EXPORT QgsQuickCoordinateTransformer : public QObject
{
Q_OBJECT
//! Projected (destination) position (in destination CRS)
Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY projectedPositionChanged )
//! Source position (in source CRS)
Q_PROPERTY( QgsPoint sourcePosition READ sourcePosition WRITE setSourcePosition NOTIFY sourcePositionChanged )
//! Destination CRS
Q_PROPERTY( QgsCoordinateReferenceSystem destinationCrs READ destinationCrs WRITE setDestinationCrs NOTIFY destinationCrsChanged )
//! Source CRS, default 4326
Q_PROPERTY( QgsCoordinateReferenceSystem sourceCrs READ sourceCrs WRITE setSourceCrs NOTIFY sourceCrsChanged )
//! Transformation context, can be set from QgsQuickMapSettings::transformContext()
Q_PROPERTY( QgsCoordinateTransformContext transformContext WRITE setTransformContext )
public:
//! Creates new coordinate transformer
explicit QgsQuickCoordinateTransformer( QObject *parent = 0 );
//!\copydoc QgsQuickCoordinateTransformer::projectedPosition
QgsPoint projectedPosition() const;
//!\copydoc QgsQuickCoordinateTransformer::sourcePosition
QgsPoint sourcePosition() const;
//!\copydoc QgsQuickCoordinateTransformer::sourcePosition
void setSourcePosition( const QgsPoint &sourcePosition );
//!\copydoc QgsQuickCoordinateTransformer::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const;
//!\copydoc QgsQuickCoordinateTransformer::destinationCrs
void setDestinationCrs( const QgsCoordinateReferenceSystem &destinationCrs );
//!\copydoc QgsQuickCoordinateTransformer::sourceCrs
QgsCoordinateReferenceSystem sourceCrs() const;
//!\copydoc QgsQuickCoordinateTransformer::sourceCrs
void setSourceCrs( const QgsCoordinateReferenceSystem &sourceCrs );
//!\copydoc QgsQuickCoordinateTransformer::transformContext
void setTransformContext( const QgsCoordinateTransformContext &context );
signals:
//!\copydoc QgsQuickCoordinateTransformer::projectedPosition
void projectedPositionChanged();
//!\copydoc QgsQuickCoordinateTransformer::sourcePosition
void sourcePositionChanged();
//!\copydoc QgsQuickCoordinateTransformer::destinationCrs
void destinationCrsChanged();
//!\copydoc QgsQuickCoordinateTransformer::sourceCrs
void sourceCrsChanged();
//!\copydoc QgsQuickCoordinateTransformer::transformContext
void transformContextChanged();
private:
void updatePosition();
QgsPoint mProjectedPosition;
QgsPoint mSourcePosition;
QgsCoordinateTransform mCoordinateTransform;
};
#endif // QGSQUICKCOORDINATETRANSFORMER_H

View File

@ -0,0 +1,334 @@
/***************************************************************************
qgsquickpositionkit.cpp
--------------------------------------
Date : Dec. 2017
Copyright : (C) 2017 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 <memory>
#include "qgis.h"
#include "qgslogger.h"
#include "qgsmessagelog.h"
#include "qgsquickpositionkit.h"
#include "qgsquickutils.h"
#include "qgsquicksimulatedpositionsource.h"
QgsQuickPositionKit::QgsQuickPositionKit( QObject *parent )
: QObject( parent )
{
connect( this,
&QgsQuickPositionKit::simulatePositionLongLatRadChanged,
this,
&QgsQuickPositionKit::onSimulatePositionLongLatRadChanged );
useGpsLocation();
}
QGeoPositionInfoSource *QgsQuickPositionKit::gpsSource()
{
// this should give us "true" position source
// on Linux it comes from Geoclue library
std::unique_ptr<QGeoPositionInfoSource> source( QGeoPositionInfoSource::createDefaultSource( nullptr ) );
if ( source->error() != QGeoPositionInfoSource::NoError )
{
QgsMessageLog::logMessage( QStringLiteral( "%1 (%2)" )
.arg( tr( "Unable to create default GPS Position Source" ) )
.arg( QString::number( ( long )source->error() ) )
, QStringLiteral( "QgsQuick" )
, Qgis::Warning );
return nullptr;
}
else
{
return source.release();
}
}
QGeoPositionInfoSource *QgsQuickPositionKit::simulatedSource( double longitude, double latitude, double radius )
{
return new QgsQuickSimulatedPositionSource( this, longitude, latitude, radius );
}
void QgsQuickPositionKit::useSimulatedLocation( double longitude, double latitude, double radius )
{
std::unique_ptr<QGeoPositionInfoSource> source( simulatedSource( longitude, latitude, radius ) );
mIsSimulated = true;
replacePositionSource( source.release() );
}
void QgsQuickPositionKit::updateScreenPosition()
{
if ( !mMapSettings )
return;
QPointF screenPosition = mapSettings()->coordinateToScreen( projectedPosition() );
if ( screenPosition != mScreenPosition )
{
mScreenPosition = screenPosition;
emit screenPositionChanged();
}
}
void QgsQuickPositionKit::updateScreenAccuracy()
{
if ( !mMapSettings )
return;
double screenAccuracy = calculateScreenAccuracy();
if ( !qgsDoubleNear( screenAccuracy, mScreenAccuracy ) )
{
mScreenAccuracy = screenAccuracy;
emit screenAccuracyChanged();
}
}
void QgsQuickPositionKit::useGpsLocation()
{
QGeoPositionInfoSource *source = gpsSource();
mIsSimulated = false;
replacePositionSource( source );
}
void QgsQuickPositionKit::replacePositionSource( QGeoPositionInfoSource *source )
{
if ( mSource.get() == source )
return;
if ( mSource )
{
mSource->disconnect();
}
mSource.reset( source );
if ( mSource )
{
connect( mSource.get(), &QGeoPositionInfoSource::positionUpdated, this, &QgsQuickPositionKit::onPositionUpdated );
connect( mSource.get(), &QGeoPositionInfoSource::updateTimeout, this, &QgsQuickPositionKit::onUpdateTimeout );
mSource->startUpdates();
QgsDebugMsg( QStringLiteral( "Position source changed: %1" ).arg( mSource->sourceName() ) );
}
}
QgsQuickMapSettings *QgsQuickPositionKit::mapSettings() const
{
return mMapSettings;
}
void QgsQuickPositionKit::updateProjectedPosition()
{
if ( !mMapSettings )
return;
QgsPointXY srcPoint = QgsPointXY( mPosition.x(), mPosition.y() );
QgsPointXY projectedPositionXY = QgsQuickUtils::transformPoint(
positionCRS(),
mMapSettings->destinationCrs(),
mMapSettings->transformContext(),
srcPoint );
QgsPoint projectedPosition( projectedPositionXY );
projectedPosition.addZValue( mPosition.z() );
if ( projectedPosition != mProjectedPosition )
{
mProjectedPosition = projectedPosition;
emit projectedPositionChanged();
}
}
void QgsQuickPositionKit::onPositionUpdated( const QGeoPositionInfo &info )
{
bool hasPosition = info.coordinate().isValid();
if ( hasPosition != mHasPosition )
{
mHasPosition = hasPosition;
emit hasPositionChanged();
}
// Calculate position
QgsPoint position = QgsPoint(
info.coordinate().longitude(),
info.coordinate().latitude(),
info.coordinate().altitude() ); // can be NaN
if ( position != mPosition )
{
mPosition = position;
emit positionChanged();
}
// calculate accuracy
double accuracy;
if ( info.hasAttribute( QGeoPositionInfo::HorizontalAccuracy ) )
accuracy = info.attribute( QGeoPositionInfo::HorizontalAccuracy );
else
accuracy = -1;
if ( !qgsDoubleNear( accuracy, mAccuracy ) )
{
mAccuracy = accuracy;
emit accuracyChanged();
}
// calculate direction
double direction;
if ( info.hasAttribute( QGeoPositionInfo::Direction ) )
direction = info.attribute( QGeoPositionInfo::Direction );
else
direction = -1;
if ( !qgsDoubleNear( direction, mDirection ) )
{
mDirection = direction;
emit directionChanged();
}
// recalculate projected/screen variables
onMapSettingsUpdated();
}
void QgsQuickPositionKit::onMapSettingsUpdated()
{
updateProjectedPosition();
updateScreenAccuracy();
updateScreenPosition();
}
void QgsQuickPositionKit::onSimulatePositionLongLatRadChanged( QVector<double> simulatePositionLongLatRad )
{
if ( simulatePositionLongLatRad.size() > 2 )
{
double longitude = simulatePositionLongLatRad[0];
double latitude = simulatePositionLongLatRad[1];
double radius = simulatePositionLongLatRad[2];
QgsDebugMsg( QStringLiteral( "Use simulated position around longlat: %1, %2, %3" ).arg( longitude ).arg( latitude ).arg( radius ) );
useSimulatedLocation( longitude, latitude, radius );
}
else
{
QgsDebugMsg( QStringLiteral( "Unable to set simulated position due to the input errors." ) );
useGpsLocation();
}
}
double QgsQuickPositionKit::calculateScreenAccuracy()
{
if ( !mMapSettings )
return 2.0;
if ( accuracy() > 0 )
{
double scpm = QgsQuickUtils::screenUnitsToMeters( mMapSettings, 1 );
if ( scpm > 0 )
return 2 * ( accuracy() / scpm );
else
return 2.0;
}
return 2.0;
}
void QgsQuickPositionKit::onUpdateTimeout()
{
if ( mHasPosition )
{
mHasPosition = false;
emit hasPositionChanged();
}
}
QPointF QgsQuickPositionKit::screenPosition() const
{
return mScreenPosition;
}
double QgsQuickPositionKit::screenAccuracy() const
{
return mScreenAccuracy;
}
QVector<double> QgsQuickPositionKit::simulatePositionLongLatRad() const
{
return mSimulatePositionLongLatRad;
}
void QgsQuickPositionKit::setSimulatePositionLongLatRad( const QVector<double> &simulatePositionLongLatRad )
{
mSimulatePositionLongLatRad = simulatePositionLongLatRad;
emit simulatePositionLongLatRadChanged( simulatePositionLongLatRad );
}
QgsCoordinateReferenceSystem QgsQuickPositionKit::positionCRS() const
{
return QgsCoordinateReferenceSystem::fromEpsgId( 4326 );
}
QgsPoint QgsQuickPositionKit::projectedPosition() const
{
return mProjectedPosition;
}
bool QgsQuickPositionKit::hasPosition() const
{
return mHasPosition;
}
QgsPoint QgsQuickPositionKit::position() const
{
return mPosition;
}
double QgsQuickPositionKit::accuracy() const
{
return mAccuracy;
}
QgsUnitTypes::DistanceUnit QgsQuickPositionKit::accuracyUnits() const
{
return QgsUnitTypes::DistanceMeters;
}
double QgsQuickPositionKit::direction() const
{
return mDirection;
}
bool QgsQuickPositionKit::isSimulated() const
{
return mIsSimulated;
}
void QgsQuickPositionKit::setMapSettings( QgsQuickMapSettings *mapSettings )
{
if ( mMapSettings == mapSettings )
return;
if ( mMapSettings )
{
mMapSettings->disconnect();
}
mMapSettings = mapSettings;
if ( mMapSettings )
{
connect( mMapSettings, &QgsQuickMapSettings::extentChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
connect( mMapSettings, &QgsQuickMapSettings::destinationCrsChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
connect( mMapSettings, &QgsQuickMapSettings::mapUnitsPerPixelChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
connect( mMapSettings, &QgsQuickMapSettings::visibleExtentChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
connect( mMapSettings, &QgsQuickMapSettings::outputSizeChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
connect( mMapSettings, &QgsQuickMapSettings::outputDpiChanged, this, &QgsQuickPositionKit::onMapSettingsUpdated );
}
emit mapSettingsChanged();
}

View File

@ -0,0 +1,252 @@
/***************************************************************************
qgsquickpositionkit.h
--------------------------------------
Date : Dec. 2017
Copyright : (C) 2017 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 QGSQUICKPOSITIONKIT_H
#define QGSQUICKPOSITIONKIT_H
#include <QObject>
#include <QtPositioning>
#include "qgspoint.h"
#include "qgis_quick.h"
#include "qgsquickmapsettings.h"
#include "qgsquickcoordinatetransformer.h"
/**
* \ingroup quick
* Convenient set of tools to read GPS position and accuracy.
*
* Also, if one can use use_simulated_location to specify simulated position.
* Simulated position source generates random points in circles around the selected
* point and radius. Real GPS position is not used in this mode.
*
* \note QML Type: PositionKit
*
* \since QGIS 3.4
*/
class QUICK_EXPORT QgsQuickPositionKit : public QObject
{
Q_OBJECT
/**
* GPS position in WGS84 coords.
*
* This is a readonly property.
*/
Q_PROPERTY( QgsPoint position READ position NOTIFY positionChanged )
/**
* GPS position in map coords.
*
* This is a readonly property.
*/
Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY projectedPositionChanged )
/**
* GPS position in device coords (pixels).
*
* This is a readonly property.
*/
Q_PROPERTY( QPointF screenPosition READ screenPosition NOTIFY screenPositionChanged )
/**
* GPS position is available (position property is a valid number).
*
* This is a readonly property.
*/
Q_PROPERTY( bool hasPosition READ hasPosition NOTIFY hasPositionChanged )
/**
* GPS horizontal accuracy in accuracyUnits, -1 if not available.
*
* This is a readonly property.
*/
Q_PROPERTY( double accuracy READ accuracy NOTIFY accuracyChanged )
/**
* Screen horizontal accuracy, 2 if not available or resolution is too small.
*
* This is a readonly property.
*/
Q_PROPERTY( double screenAccuracy READ screenAccuracy NOTIFY screenAccuracyChanged )
/**
* GPS direction, bearing in degrees clockwise from north to direction of travel. -1 if not available
*
* This is a readonly property.
*/
Q_PROPERTY( double direction READ direction NOTIFY directionChanged )
/**
* GPS position and accuracy is simulated (not real from GPS sensor). Default false (use real GPS)
*
* This is a readonly property. To change to simulated position, see QgsQuickPositionKit::simulatePositionLongLatRad
*/
Q_PROPERTY( bool isSimulated READ isSimulated NOTIFY isSimulatedChanged )
/**
* Associated map settings. Should be initialized before the first use from mapcanvas map settings.
*
* This is a readonly property.
*/
Q_PROPERTY( QgsQuickMapSettings *mapSettings READ mapSettings WRITE setMapSettings NOTIFY mapSettingsChanged )
/**
* Uses of GPS and simulated position and sets its parameters
*
* Vector containing longitude, latitude and radius (meters) of simulated position e.g. [-97.36, 36.93, 2]
* If empty vector is assigned, GPS source will be used.
*
* From QML context, also functions useSimulatedLocation() or useGpsLocation() could be used instead
*/
Q_PROPERTY( QVector<double> simulatePositionLongLatRad READ simulatePositionLongLatRad WRITE setSimulatePositionLongLatRad NOTIFY simulatePositionLongLatRadChanged )
public:
//! Creates new position kit
explicit QgsQuickPositionKit( QObject *parent = 0 );
//! \copydoc QgsQuickPositionKit::position
bool hasPosition() const;
//! \copydoc QgsQuickPositionKit::position
QgsPoint position() const;
//! \copydoc QgsQuickPositionKit::projectedPosition
QgsPoint projectedPosition() const;
//! \copydoc QgsQuickPositionKit::screenPosition
QPointF screenPosition() const;
//! \copydoc QgsQuickPositionKit::accuracy
double accuracy() const;
//! \copydoc QgsQuickPositionKit::screenAccuracy
double screenAccuracy() const;
/**
* GPS horizontal accuracy units - meters (constant)
*/
QgsUnitTypes::DistanceUnit accuracyUnits() const;
//! \copydoc QgsQuickPositionKit::direction
double direction() const;
//! \copydoc QgsQuickPositionKit::isSimulated
bool isSimulated() const;
//! \copydoc QgsQuickPositionKit::mapSettings
void setMapSettings( QgsQuickMapSettings *mapSettings );
//! \copydoc QgsQuickPositionKit::mapSettings
QgsQuickMapSettings *mapSettings() const;
//! \copydoc QgsQuickPositionKit::simulatePositionLongLatRad
QVector<double> simulatePositionLongLatRad() const;
//! \copydoc QgsQuickPositionKit::simulatePositionLongLatRad
void setSimulatePositionLongLatRad( const QVector<double> &simulatePositionLongLatRad );
/**
* Coordinate reference system of position - WGS84 (constant)
*/
Q_INVOKABLE QgsCoordinateReferenceSystem positionCRS() const;
/**
* Use simulated GPS source.
*
* Simulated GPS source emulates point on circle around defined point in specified radius
*
* We do not want to have the origin point as property
* We basically want to set it once based on project/map cente and keep
* it that way regardless of mapsettings change (e.g. zoom etc)
*
* \param longitude longitude of the centre of the emulated points
* \param latitude latitude of the centre of the emulated points
* \param radius distance of emulated points from the centre (in degrees WSG84)
*/
Q_INVOKABLE void useSimulatedLocation( double longitude, double latitude, double radius );
/**
* Use real GPS source (not simulated)
*/
Q_INVOKABLE void useGpsLocation();
signals:
//! \copydoc QgsQuickPositionKit::position
void positionChanged();
//! \copydoc QgsQuickPositionKit::projectedPosition
void projectedPositionChanged();
//! \copydoc QgsQuickPositionKit::screenPosition
void screenPositionChanged();
//! hasPosition changed
void hasPositionChanged();
//! \copydoc QgsQuickPositionKit::accuracy
double accuracyChanged() const;
//! \copydoc QgsQuickPositionKit::screenAccuracy
double screenAccuracyChanged() const;
//! \copydoc QgsQuickPositionKit::accuracyUnits
Q_INVOKABLE QString accuracyUnitsChanged() const;
//! \copydoc QgsQuickPositionKit::direction
double directionChanged() const;
//! \copydoc QgsQuickPositionKit::isSimulated
void isSimulatedChanged();
//! \copydoc QgsQuickPositionKit::mapSettings
void mapSettingsChanged();
//! \copydoc QgsQuickPositionKit::simulatePositionLongLatRad
void simulatePositionLongLatRadChanged( QVector<double> simulatePositionLongLatRad );
private slots:
void onPositionUpdated( const QGeoPositionInfo &info );
void onMapSettingsUpdated();
void onUpdateTimeout();
void onSimulatePositionLongLatRadChanged( QVector<double> simulatePositionLongLatRad );
private:
void replacePositionSource( QGeoPositionInfoSource *source );
QString calculateStatusLabel();
double calculateScreenAccuracy();
void updateProjectedPosition();
void updateScreenPosition();
void updateScreenAccuracy();
QGeoPositionInfoSource *gpsSource();
QGeoPositionInfoSource *simulatedSource( double longitude, double latitude, double radius );
QgsPoint mPosition;
QgsPoint mProjectedPosition;
QPointF mScreenPosition;
double mAccuracy = -1;
double mScreenAccuracy = 2;
double mDirection = -1;
bool mHasPosition = false;
bool mIsSimulated = false;
QVector<double> mSimulatePositionLongLatRad;
std::unique_ptr<QGeoPositionInfoSource> mSource;
QgsQuickMapSettings *mMapSettings = nullptr; // not owned
};
#endif // QGSQUICKPOSITIONKIT_H

View File

@ -82,16 +82,14 @@ void QgsQuickScaleBarKit::updateScaleBar()
if ( !mMapSettings )
return;
double dist = QgsQuickUtils().screenUnitsToMeters( mMapSettings, mPreferredWidth ); // meters
if ( dist > 1000.0 )
{
dist = dist / 1000.0; // meters to kilometers
mUnits = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::DistanceKilometers );
}
else
{
mUnits = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::DistanceMeters );
}
double distInMeters = QgsQuickUtils().screenUnitsToMeters( mMapSettings, mPreferredWidth ); // meters
double dist;
QgsUnitTypes::DistanceUnit distUnits;
QgsQuickUtils().humanReadableDistance( distInMeters, QgsUnitTypes::DistanceMeters,
mSystemOfMeasurement,
dist, distUnits );
mUnits = QgsUnitTypes::toAbbreviatedString( distUnits );
// we want to show nice round distances e.g. 200 km instead of e.g. 273 km
// so we determine which "nice" number to use and also update the scale bar

View File

@ -37,6 +37,9 @@ class QgsQuickMapSettings;
* distance in meters or kilometers (int) rounded to "nice" number (e.g. 72.4 to 100)
* and units text (e.g. km)
*
* System of measurement for result could be set too, so for example the resulting scalebar
* can show results in the imperial units.
*
* \note QML Type: ScaleBarKit
*
* \since QGIS 3.2
@ -56,12 +59,19 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject
Q_PROPERTY( int preferredWidth MEMBER mPreferredWidth NOTIFY preferredWidthChanged )
/**
* Units of distance (e.g. km or m) Read-only (result).
* Preferred system of measurement for the result
*/
Q_PROPERTY( QgsUnitTypes::SystemOfMeasurement systemOfMeasurement MEMBER mSystemOfMeasurement NOTIFY systemOfMeasurementChanged )
/**
* Units of distance (e.g. km or m) of result in desired systemOfMeasurement Read-only (result).
*/
Q_PROPERTY( QString units READ units NOTIFY scaleBarChanged )
/**
* Distance rounded to "nice" number (e.g. 100, 20) corresponding to width. To be used with units property for labels. Read-only (result).
* Distance rounded to "nice" number (e.g. 100, 20) corresponding to width and system of measurement
*
* To be used with units property for labels. Read-only (result).
*/
Q_PROPERTY( int distance READ distance NOTIFY scaleBarChanged )
@ -107,6 +117,9 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject
//! \copydoc QgsQuickScaleBarKit::preferredWidth
void preferredWidthChanged();
//! \copydoc QgsQuickScaleBarKit::systemOfMeasurement
void systemOfMeasurementChanged();
public slots:
//! recalculate width, distance and units.
void updateScaleBar();
@ -116,7 +129,8 @@ class QUICK_EXPORT QgsQuickScaleBarKit : public QObject
int mPreferredWidth; // pixels
int mWidth; // pixels
int mDistance; // in meters or kilometers, rounded
QString mUnits; // km or m
QString mUnits; // e.g. km or m
QgsUnitTypes::SystemOfMeasurement mSystemOfMeasurement = QgsUnitTypes::MetricSystem;
};

View File

@ -0,0 +1,104 @@
/***************************************************************************
qgsquicksimulatedpositionsource.cpp
--------------------------------------
Date : Dec. 2017
Copyright : (C) 2017 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 "qgsquicksimulatedpositionsource.h"
#include <QTimer>
/// @cond PRIVATE
QgsQuickSimulatedPositionSource::QgsQuickSimulatedPositionSource( QObject *parent, double longitude, double latitude, double flightRadius )
: QGeoPositionInfoSource( parent )
, mTimer( qgis::make_unique< QTimer >() )
, mFlightRadius( flightRadius )
, mLongitude( longitude )
, mLatitude( latitude )
{
connect( mTimer.get(), &QTimer::timeout, this, &QgsQuickSimulatedPositionSource::readNextPosition );
}
void QgsQuickSimulatedPositionSource::startUpdates()
{
int interval = updateInterval();
if ( interval < minimumUpdateInterval() )
interval = minimumUpdateInterval();
mTimer->start( interval );
readNextPosition();
}
void QgsQuickSimulatedPositionSource::stopUpdates()
{
mTimer->stop();
}
void QgsQuickSimulatedPositionSource::requestUpdate( int /*timeout*/ )
{
readNextPosition();
}
void QgsQuickSimulatedPositionSource::readNextPosition()
{
if ( mFlightRadius <= 0 )
readConstantPosition();
else
readRandomPosition();
}
void QgsQuickSimulatedPositionSource::readRandomPosition()
{
double latitude = mLatitude, longitude = mLongitude;
latitude += sin( mAngle * M_PI / 180 ) * mFlightRadius;
longitude += cos( mAngle * M_PI / 180 ) * mFlightRadius;
mAngle += 1;
QGeoCoordinate coordinate( latitude, longitude );
double altitude = std::rand() % 40 + 20; // rand altitude <20,55>m and lost (0)
if ( altitude <= 55 )
{
coordinate.setAltitude( altitude ); // 3D
}
QDateTime timestamp = QDateTime::currentDateTime();
QGeoPositionInfo info( coordinate, timestamp );
if ( info.isValid() )
{
mLastPosition = info;
info.setAttribute( QGeoPositionInfo::Direction, 360 - int( mAngle ) % 360 );
int accuracy = std::rand() % 40 + 20; // rand accuracy <20,55>m and lost (-1)
if ( accuracy > 55 )
{
accuracy = -1;
}
info.setAttribute( QGeoPositionInfo::HorizontalAccuracy, accuracy );
emit positionUpdated( info );
}
}
void QgsQuickSimulatedPositionSource::readConstantPosition()
{
QGeoCoordinate coordinate( mLatitude, mLongitude );
coordinate.setAltitude( 20 );
QDateTime timestamp = QDateTime::currentDateTime();
QGeoPositionInfo info( coordinate, timestamp );
info.setAttribute( QGeoPositionInfo::Direction, 0 );
info.setAttribute( QGeoPositionInfo::HorizontalAccuracy, 20 );
emit positionUpdated( info );
}
/// @endcond

View File

@ -0,0 +1,86 @@
/***************************************************************************
qgsquicksimulatedpositionsource.h
--------------------------------------
Date : Dec. 2017
Copyright : (C) 2017 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 QGSQUICKSIMULATEDPOSITIONSOURCE_H
#define QGSQUICKSIMULATEDPOSITIONSOURCE_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 "qgis_quick.h"
#include <QObject>
#include <QTimer>
#include <QtPositioning>
#include <qgspoint.h>
/**
* \ingroup quick
* This is an internal (implementation) class used to generate (fake) GPS position source
* Useful for for testing purposes (e.g. testing of the application with map for different
* location then your physical (GPS) location)
*
* Simulated position source generates random points in circles around the selected
* point and radius. Real GPS position is not used in this mode.
*
* For disabling (random) updates, use flight radius <= 0 (useful for testing)
*
* \note QML Type: not exported
*
* \since QGIS 3.4
*/
class QUICK_NO_EXPORT QgsQuickSimulatedPositionSource : public QGeoPositionInfoSource
{
Q_OBJECT
public:
QgsQuickSimulatedPositionSource( QObject *parent, double longitude, double latitude, double flightRadius );
QGeoPositionInfo lastKnownPosition( bool /*fromSatellitePositioningMethodsOnly = false*/ ) const { return mLastPosition; }
PositioningMethods supportedPositioningMethods() const { return AllPositioningMethods; }
int minimumUpdateInterval() const { return 1000; }
Error error() const { return QGeoPositionInfoSource::NoError; }
public slots:
virtual void startUpdates();
virtual void stopUpdates();
virtual void requestUpdate( int timeout = 5000 );
private slots:
void readNextPosition();
private:
void readRandomPosition();
void readConstantPosition();
std::unique_ptr< QTimer > mTimer;
QGeoPositionInfo mLastPosition;
double mAngle = 0;
double mFlightRadius = 0;
double mLongitude = 0;
double mLatitude = 0;
};
/// @endcond
#endif // QGSQUICKSIMULATEDPOSITIONSOURCE_H

View File

@ -16,6 +16,8 @@
#include <QString>
#include "qgis.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgsdistancearea.h"
#include "qgslogger.h"
#include "qgsvectorlayer.h"
@ -32,7 +34,40 @@ QgsQuickUtils::QgsQuickUtils( QObject *parent )
{
}
double QgsQuickUtils::screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels ) const
/**
* Makes QgsCoordinateReferenceSystem::fromEpsgId accessible for QML components
*/
QgsCoordinateReferenceSystem QgsQuickUtils::coordinateReferenceSystemFromEpsgId( long epsg )
{
return QgsCoordinateReferenceSystem::fromEpsgId( epsg );
}
QgsPointXY QgsQuickUtils::pointXY( double x, double y ) const
{
return QgsPointXY( x, y );
}
QgsPoint QgsQuickUtils::point( double x, double y, double z, double m ) const
{
return QgsPoint( x, y, z, m );
}
QgsPoint QgsQuickUtils::coordinateToPoint( const QGeoCoordinate &coor ) const
{
return QgsPoint( coor.longitude(), coor.latitude(), coor.altitude() );
}
QgsPointXY QgsQuickUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
const QgsCoordinateReferenceSystem &destCrs,
const QgsCoordinateTransformContext &context,
const QgsPointXY &srcPoint )
{
QgsCoordinateTransform mTransform( srcCrs, destCrs, context );
QgsPointXY pt = mTransform.transform( srcPoint );
return pt;
}
double QgsQuickUtils::screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels )
{
if ( mapSettings == nullptr ) return 0.0;
@ -59,6 +94,169 @@ QgsQuickFeatureLayerPair QgsQuickUtils::featureFactory( const QgsFeature &featur
return QgsQuickFeatureLayerPair( feature, layer );
}
const QUrl QgsQuickUtils::getThemeIcon( const QString &name ) const
{
QString path = QStringLiteral( "qrc:/%1.svg" ).arg( name );
QgsDebugMsg( QStringLiteral( "Using icon %1 from %2" ).arg( name, path ) );
return QUrl( path );
}
QString QgsQuickUtils::formatPoint(
const QgsPoint &point,
QgsCoordinateFormatter::Format format,
int decimals,
QgsCoordinateFormatter::FormatFlags flags )
{
return QgsCoordinateFormatter::format( point, format, decimals, flags );
}
QString QgsQuickUtils::formatDistance( double distance,
QgsUnitTypes::DistanceUnit units,
int decimals,
QgsUnitTypes::SystemOfMeasurement destSystem )
{
double destDistance;
QgsUnitTypes::DistanceUnit destUnits;
humanReadableDistance( distance, units, destSystem, destDistance, destUnits );
return QStringLiteral( "%1 %2" )
.arg( QString::number( destDistance, 'f', decimals ) )
.arg( QgsUnitTypes::toAbbreviatedString( destUnits ) );
}
void QgsQuickUtils::humanReadableDistance( double srcDistance, QgsUnitTypes::DistanceUnit srcUnits,
QgsUnitTypes::SystemOfMeasurement destSystem,
double &destDistance, QgsUnitTypes::DistanceUnit &destUnits )
{
if ( ( destSystem == QgsUnitTypes::MetricSystem ) || ( destSystem == QgsUnitTypes::UnknownSystem ) )
{
return formatToMetricDistance( srcDistance, srcUnits, destDistance, destUnits );
}
else if ( destSystem == QgsUnitTypes::ImperialSystem )
{
return formatToImperialDistance( srcDistance, srcUnits, destDistance, destUnits );
}
else if ( destSystem == QgsUnitTypes::USCSSystem )
{
return formatToUSCSDistance( srcDistance, srcUnits, destDistance, destUnits );
}
else
{
Q_ASSERT( false ); //should never happen
}
}
void QgsQuickUtils::formatToMetricDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits )
{
double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceMillimeters );
if ( dist < 0 )
{
destDistance = 0;
destUnits = QgsUnitTypes::DistanceMillimeters;
return;
}
double mmToKm = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceKilometers, QgsUnitTypes::DistanceMillimeters );
if ( dist > mmToKm )
{
destDistance = dist / mmToKm;
destUnits = QgsUnitTypes::DistanceKilometers;
return;
}
double mmToM = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, QgsUnitTypes::DistanceMillimeters );
if ( dist > mmToM )
{
destDistance = dist / mmToM;
destUnits = QgsUnitTypes::DistanceMeters;
return;
}
double mmToCm = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceCentimeters, QgsUnitTypes::DistanceMillimeters );
if ( dist > mmToCm )
{
destDistance = dist / mmToCm;
destUnits = QgsUnitTypes::DistanceCentimeters;
return;
}
destDistance = dist;
destUnits = QgsUnitTypes::DistanceMillimeters;
}
void QgsQuickUtils::formatToImperialDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits )
{
double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceFeet );
if ( dist < 0 )
{
destDistance = 0;
destUnits = QgsUnitTypes::DistanceFeet;
return;
}
double feetToMile = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMiles, QgsUnitTypes::DistanceFeet );
if ( dist > feetToMile )
{
destDistance = dist / feetToMile;
destUnits = QgsUnitTypes::DistanceMiles;
return;
}
double feetToYard = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceYards, QgsUnitTypes::DistanceFeet );
if ( dist > feetToYard )
{
destDistance = dist / feetToYard;
destUnits = QgsUnitTypes::DistanceYards;
return;
}
destDistance = dist;
destUnits = QgsUnitTypes::DistanceFeet;
return;
}
void QgsQuickUtils::formatToUSCSDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits )
{
double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceFeet );
if ( dist < 0 )
{
destDistance = 0;
destUnits = QgsUnitTypes::DistanceFeet;
return;
}
double feetToMile = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceNauticalMiles, QgsUnitTypes::DistanceFeet );
if ( dist > feetToMile )
{
destDistance = dist / feetToMile;
destUnits = QgsUnitTypes::DistanceNauticalMiles;
return;
}
double feetToYard = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceYards, QgsUnitTypes::DistanceFeet );
if ( dist > feetToYard )
{
destDistance = dist / feetToYard;
destUnits = QgsUnitTypes::DistanceYards;
return;
}
destDistance = dist;
destUnits = QgsUnitTypes::DistanceFeet;
return;
}
QString QgsQuickUtils::dumpScreenInfo() const
{
QRect rec = QApplication::desktop()->screenGeometry();

View File

@ -19,14 +19,20 @@
#include <QObject>
#include <QString>
#include <QtPositioning/QGeoCoordinate>
#include <limits>
#include "qgis.h"
#include "qgsmessagelog.h"
#include "qgspoint.h"
#include "qgspointxy.h"
#include "qgsunittypes.h"
#include "qgsquickmapsettings.h"
#include "qgsquickfeaturelayerpair.h"
#include "qgis_quick.h"
#include "qgsfeature.h"
#include "qgscoordinateformatter.h"
class QgsVectorLayer;
class QgsCoordinateReferenceSystem;
@ -60,16 +66,54 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
public:
//! Create new utilities
QgsQuickUtils( QObject *parent = nullptr );
//! dtor
//! Destructor
~QgsQuickUtils() = default;
//! \copydoc QgsQuickUtils::dp
qreal screenDensity() const;
/**
* Calculate the distance in meter representing baseLengthPixels pixels on the screen based on the current map settings.
* Creates crs from epsg code in QML
*
* \since QGIS 3.4
*/
Q_INVOKABLE double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels ) const;
Q_INVOKABLE static QgsCoordinateReferenceSystem coordinateReferenceSystemFromEpsgId( long epsg );
/**
* Creates QgsPointXY in QML
*
* \since QGIS 3.4
*/
Q_INVOKABLE QgsPointXY pointXY( double x, double y ) const;
/**
* Creates QgsPoint in QML
*
* \since QGIS 3.4
*/
Q_INVOKABLE QgsPoint point( double x, double y, double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN() ) const;
/**
* Converts QGeoCoordinate to QgsPoint
*
* \since QGIS 3.4
*/
Q_INVOKABLE QgsPoint coordinateToPoint( const QGeoCoordinate &coor ) const;
/**
* Transforms point between different crs from QML
*
* \since QGIS 3.4
*/
Q_INVOKABLE static QgsPointXY transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
const QgsCoordinateReferenceSystem &destCrs,
const QgsCoordinateTransformContext &context,
const QgsPointXY &srcPoint );
/**
* Calculates the distance in meter representing baseLengthPixels pixels on the screen based on the current map settings.
*/
Q_INVOKABLE static double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels );
//! Log message in QgsMessageLog
Q_INVOKABLE void logMessage( const QString &message,
@ -86,13 +130,84 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
Q_INVOKABLE QgsQuickFeatureLayerPair featureFactory( const QgsFeature &feature, QgsVectorLayer *layer = nullptr ) const;
/**
* Returns a string with information about screen size and resolution
* Returns QUrl to image from library's /images folder.
*
* \since QGIS 3.4
*/
Q_INVOKABLE const QUrl getThemeIcon( const QString &name ) const;
/**
* \copydoc QgsCoordinateFormatter::format()
*
* Useful to log for debugging of graphical problems on various display sizes
* \since QGIS 3.4
*/
Q_INVOKABLE static QString formatPoint(
const QgsPoint &point,
QgsCoordinateFormatter::Format format = QgsCoordinateFormatter::FormatPair,
int decimals = 3,
QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
/**
* Converts distance to human readable distance
*
* This is useful for scalebar texts or output of the GPS accuracy
*
* The resulting units are determined automatically,
* based on requested system of measurement.
* e.g. 1222.234 m is converted to 1.2 km
*
* \param distance distance in units
* \param units units of dist
* \param decimals decimal to use
* \param destSystem system of measurement of the result
* \returns string represetation of dist in desired destSystem. For distance less than 0, 0 is returned.
*
* \since QGIS 3.4
*/
Q_INVOKABLE static QString formatDistance( double distance,
QgsUnitTypes::DistanceUnit units,
int decimals,
QgsUnitTypes::SystemOfMeasurement destSystem = QgsUnitTypes::MetricSystem );
/**
* Converts distance to human readable distance in destination system of measurement
*
* \sa QgsQuickUtils::formatDistance()
*
* \param srcDistance distance in units
* \param srcUnits units of dist
* \param destSystem system of measurement of the result
* \param destDistance output: distance if desired system of measurement
* \param destUnits output: unit of destDistance
*
* \since QGIS 3.4
*/
static void humanReadableDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
QgsUnitTypes::SystemOfMeasurement destSystem,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits );
//! Returns a string with information about screen size and resolution - useful for debugging
QString dumpScreenInfo() const;
private:
static void formatToMetricDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits );
static void formatToImperialDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits );
static void formatToUSCSDistance( double srcDistance,
QgsUnitTypes::DistanceUnit srcUnits,
double &destDistance,
QgsUnitTypes::DistanceUnit &destUnits );
static qreal calculateScreenDensity();
qreal mScreenDensity;

View File

@ -81,6 +81,7 @@ ENDMACRO (ADD_QGIS_TEST)
ADD_QGIS_TEST(qgsquickidentifykit testqgsquickidentifykit.cpp)
ADD_QGIS_TEST(qgsquickutils testqgsquickutils.cpp)
ADD_QGIS_TEST(qgsquickscalebarkit testqgsquickscalebarkit.cpp)
ADD_QGIS_TEST(qgsquickpositionkit testqgsquickpositionkit.cpp)
#############################################################

View File

@ -58,6 +58,10 @@ int main( int argc, char *argv[] )
engine.rootContext()->setContextProperty( "__project", &project );
engine.rootContext()->setContextProperty( "__layers", QVariant::fromValue( project.layerTreeRoot()->layerOrder() ) );
// Set simulated position for desktop builds
bool use_simulated_position = true;
engine.rootContext()->setContextProperty( "__use_simulated_position", use_simulated_position );
QQmlComponent component( &engine, QUrl( QStringLiteral( "qrc:/main.qml" ) ) );
QObject *object = component.create();

View File

@ -52,6 +52,7 @@ ApplicationWindow {
z: 1
}
/** Message Log */
Drawer {
id: logPanel
visible: true
@ -75,12 +76,79 @@ ApplicationWindow {
}
}
/** Scale Bar in metric units*/
QgsQuick.ScaleBar {
id: scaleBar
y: window.height - height
height: 50
height: 50 * QgsQuick.Utils.dp
mapSettings: mapCanvas.mapSettings
preferredWidth: 115 * QgsQuick.Utils.dp
z: 1
}
/** Scale Bar in imperial units*/
QgsQuick.ScaleBar {
id: scaleBarImperialUnits
y: window.height - height
x: window.width/2
height: scaleBar.height
mapSettings: mapCanvas.mapSettings
preferredWidth: scaleBar.preferredWidth
systemOfMeasurement: QgsQuick.QgsUnitTypes.ImperialSystem
z: 1
}
/** Position Kit and Marker */
QgsQuick.PositionKit {
id: positionKit
mapSettings: mapCanvas.mapSettings
simulatePositionLongLatRad: __use_simulated_position ? [-97.36, 36.93, 2] : undefined
}
QgsQuick.PositionMarker {
id: positionMarker
positionKit: positionKit
}
Label {
id: gpsPositionLabel
text: {
var label = "Signal Lost"
if ( positionKit.hasPosition )
label = QgsQuick.Utils.formatPoint( positionKit.position )
if (positionKit.accuracy > 0)
label += " (" + QgsQuick.Utils.formatDistance( positionKit.accuracy, positionKit.accuracyUnits, 0 ) + ")"
label;
}
height: scaleBar.height
x: window.width - width
font.pixelSize: 22 * QgsQuick.Utils.dp
font.italic: true
color: "steelblue"
z: 1
}
/** Coordinate transformater */
QgsQuick.CoordinateTransformer {
id: coordinateTransformer
sourcePosition: positionKit.position
sourceCrs: positionKit.positionCRS()
destinationCrs: QgsQuick.Utils.coordinateReferenceSystemFromEpsgId( 3857 ) //web mercator
transformContext: mapCanvas.mapSettings.transformContext()
}
Label {
id: webPositionLabel
text: {
if ( positionKit.hasPosition )
QgsQuick.Utils.formatPoint( coordinateTransformer.projectedPosition ) + " (web mercator)"
}
height: scaleBar.height
x: window.width - width
y: gpsPositionLabel.height + 2 * QgsQuick.Utils.dp
font.pixelSize: 22 * QgsQuick.Utils.dp
font.italic: true
color: "steelblue"
z: 1
}
}

View File

@ -0,0 +1,61 @@
/***************************************************************************
testqgspositionkit.cpp
--------------------------------------
Date : May 2018
Copyright : (C) 2017 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 <QObject>
#include <QApplication>
#include <QDesktopWidget>
#include "qgsapplication.h"
#include "qgstest.h"
#include "qgis.h"
#include "qgsquickpositionkit.h"
#include "qgsquicksimulatedpositionsource.h"
#include "qgsquickutils.h"
class TestQgsQuickUtils: 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 simulated_position();
private:
QgsQuickUtils utils;
QgsQuickPositionKit positionKit;
};
void TestQgsQuickUtils::simulated_position()
{
QVERIFY( !positionKit.isSimulated() );
positionKit.useSimulatedLocation( -92.36, 38.93, -1 );
QVERIFY( positionKit.isSimulated() );
QVERIFY( positionKit.hasPosition() );
QGSCOMPARENEAR( positionKit.position().y(), 38.93, 1e-4 );
QVERIFY( positionKit.accuracy() > 0 );
const QVector<double> newPosition( { 90.36, 33.93, -1 } );
positionKit.setSimulatePositionLongLatRad( newPosition );
QVERIFY( positionKit.hasPosition() );
QGSCOMPARENEAR( positionKit.position().y(), newPosition[1], 1e-4 );
positionKit.setSimulatePositionLongLatRad( QVector<double>() );
QVERIFY( !positionKit.isSimulated() );
}
QGSTEST_MAIN( TestQgsQuickUtils )
#include "testqgsquickpositionkit.moc"

View File

@ -17,8 +17,13 @@
#include <QDesktopWidget>
#include "qgsapplication.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransformcontext.h"
#include "qgspoint.h"
#include "qgspointxy.h"
#include "qgstest.h"
#include "qgis.h"
#include "qgsunittypes.h"
#include "qgsquickutils.h"
@ -32,6 +37,9 @@ class TestQgsQuickUtils: public QObject
void screen_density();
void dump_screen_info();
void screenUnitsToMeters();
void transformedPoint();
void formatPoint();
void formatDistance();
private:
QgsQuickUtils utils;
@ -62,5 +70,71 @@ void TestQgsQuickUtils::screenUnitsToMeters()
QGSCOMPARENEAR( sutm, 213, 1.0 );
}
void TestQgsQuickUtils::transformedPoint()
{
QgsPointXY pointXY = utils.pointXY( 49.9, 16.3 );
QGSCOMPARENEAR( pointXY.x(), 49.9, 1e-4 );
QGSCOMPARENEAR( pointXY.y(), 16.3, 1e-4 );
QgsPoint point = utils.point( 1.0, -1.0 );
QGSCOMPARENEAR( point.x(), 1.0, 1e-4 );
QGSCOMPARENEAR( point.y(), -1.0, 1e-4 );
QgsCoordinateReferenceSystem crs3857 = QgsCoordinateReferenceSystem::fromEpsgId( 3857 );
QVERIFY( crs3857.authid() == "EPSG:3857" );
QgsCoordinateReferenceSystem crsGPS = QgsCoordinateReferenceSystem::fromEpsgId( 4326 );
QVERIFY( crsGPS.authid() == "EPSG:4326" );
QgsPointXY transformedPoint = utils.transformPoint( crsGPS,
crs3857,
QgsCoordinateTransformContext(),
pointXY );
QGSCOMPARENEAR( transformedPoint.x(), 5554843, 1.0 );
QGSCOMPARENEAR( transformedPoint.y(), 1839491, 1.0 );
}
void TestQgsQuickUtils::formatPoint()
{
QgsPoint point( -2.234521, 34.4444421 );
QString point2str = utils.formatPoint( point );
QVERIFY( point2str == "-2.235,34.444" );
}
void TestQgsQuickUtils::formatDistance()
{
QString dist2str = utils.formatDistance( 1222.234, QgsUnitTypes::DistanceMeters, 2 );
QVERIFY( dist2str == "1.22 km" );
dist2str = utils.formatDistance( 1222.234, QgsUnitTypes::DistanceMeters, 1 );
QVERIFY( dist2str == "1.2 km" );
dist2str = utils.formatDistance( 1222.234, QgsUnitTypes::DistanceMeters, 0 );
QVERIFY( dist2str == "1 km" );
dist2str = utils.formatDistance( 700.22, QgsUnitTypes::DistanceMeters, 1 );
QVERIFY( dist2str == "700.2 m" );
dist2str = utils.formatDistance( 0.22, QgsUnitTypes::DistanceMeters, 0 );
QVERIFY( dist2str == "22 cm" );
dist2str = utils.formatDistance( -0.22, QgsUnitTypes::DistanceMeters, 0 );
QVERIFY( dist2str == "0 mm" );
dist2str = utils.formatDistance( 1.222234, QgsUnitTypes::DistanceKilometers, 2 );
QVERIFY( dist2str == "1.22 km" );
/////////////////////////////////////////////////////////
dist2str = utils.formatDistance( 6000, QgsUnitTypes::DistanceFeet, 1, QgsUnitTypes::ImperialSystem );
QVERIFY( dist2str == "1.1 mi" );
dist2str = utils.formatDistance( 5, QgsUnitTypes::DistanceFeet, 1, QgsUnitTypes::ImperialSystem );
QVERIFY( dist2str == "1.7 yd" );
/////////////////////////////////////////////////////////
dist2str = utils.formatDistance( 7000, QgsUnitTypes::DistanceFeet, 1, QgsUnitTypes::USCSSystem );
QVERIFY( dist2str == "1.2 NM" );
}
QGSTEST_MAIN( TestQgsQuickUtils )
#include "testqgsquickutils.moc"