1 /***************************************************************************
2   qgsquickutils.h
3   --------------------------------------
4   Date                 : Nov 2017
5   Copyright            : (C) 2017 by Peter Petrik
6   Email                : zilolv at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #ifndef QGSQUICKUTILS_H
17 #define QGSQUICKUTILS_H
18 
19 
20 #include <QObject>
21 #include <QString>
22 #include <QUrl>
23 #include <QtPositioning/QGeoCoordinate>
24 
25 #include <limits>
26 
27 #include "qgis.h"
28 #include "qgsexpressioncontextutils.h"
29 #include "qgsmessagelog.h"
30 #include "qgspoint.h"
31 #include "qgspointxy.h"
32 #include "qgsunittypes.h"
33 #include "qgsquickmapsettings.h"
34 #include "qgsquickfeaturelayerpair.h"
35 #include "qgis_quick.h"
36 #include "qgscoordinateformatter.h"
37 
38 
39 class QgsFeature;
40 class QgsVectorLayer;
41 class QgsCoordinateReferenceSystem;
42 
43 /**
44  * \ingroup quick
45  *
46  * \brief Encapsulating the common utilities for QgsQuick library.
47  *
48  * \note QML Type: Utils (Singleton)
49  *
50  * \since QGIS 3.2
51  */
52 class QUICK_EXPORT QgsQuickUtils: public QObject
53 {
54     Q_OBJECT
55 
56     /**
57       * "dp" is useful for building building components that work well with different screen densities.
58       * It stands for density-independent pixels. A width of 10dp is going to be the same physical size
59       * on all screens regardless their density. In QML code, all values are specified in screen pixels,
60       * so in order to set a width of 10dp, one would use the following code: "width: 10 * QgsQuick.Utils.dp"
61       *
62       * 1dp is approximately 0.16mm. When screen has 160 DPI (baseline), the value of "dp" is 1.
63       * On high DPI screen the value will be greater, e.g. 1.5.
64       *
65       * This is a readonly property.
66       */
67     Q_PROPERTY( qreal dp READ screenDensity CONSTANT )
68 
69   public:
70     //! Create new utilities
71     QgsQuickUtils( QObject *parent = nullptr );
72     //! Destructor
73     ~QgsQuickUtils() = default;
74 
75     //! \copydoc QgsQuickUtils::dp
76     qreal screenDensity() const;
77 
78     /**
79       * Creates crs from epsg code in QML
80       *
81       * \since QGIS 3.4
82       */
83     Q_INVOKABLE static QgsCoordinateReferenceSystem coordinateReferenceSystemFromEpsgId( long epsg );
84 
85     /**
86       * Creates QgsPointXY in QML
87       *
88       * \since QGIS 3.4
89       */
90     Q_INVOKABLE static QgsPointXY pointXY( double x, double y );
91 
92     /**
93       * Creates QgsPoint in QML
94       *
95       * \since QGIS 3.4
96       */
97     Q_INVOKABLE static QgsPoint point( double x, double y, double z = std::numeric_limits<double>::quiet_NaN(), double m = std::numeric_limits<double>::quiet_NaN() );
98 
99     /**
100       * Converts QGeoCoordinate to QgsPoint
101       *
102       * \since QGIS 3.4
103       */
104     Q_INVOKABLE static QgsPoint coordinateToPoint( const QGeoCoordinate &coor );
105 
106     /**
107       * Transforms point between different crs from QML
108       *
109       * \since QGIS 3.4
110       */
111     Q_INVOKABLE static QgsPointXY transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
112         const QgsCoordinateReferenceSystem &destCrs,
113         const QgsCoordinateTransformContext &context,
114         const QgsPointXY &srcPoint );
115 
116     /**
117       * Calculates the distance in meter representing baseLengthPixels pixels on the screen based on the current map settings.
118       */
119     Q_INVOKABLE static double screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels );
120 
121     /**
122       * Returns whether file on path exists
123       * \since QGIS 3.4
124       */
125     Q_INVOKABLE static bool fileExists( const QString &path );
126 
127     /**
128      * Returns relative path of the file to given prefixPath. If prefixPath does not match a path parameter,
129      * returns an empty string. If a path starts with "file://", this prefix is ignored.
130      * \param path Absolute path to file
131      * \param prefixPath
132      * \since QGIS 3.8
133      */
134     Q_INVOKABLE static QString getRelativePath( const QString &path, const QString &prefixPath );
135 
136     /**
137       * Log message in QgsMessageLog
138       */
139     Q_INVOKABLE static void logMessage( const QString &message,
140                                         const QString &tag = QString( "QgsQuick" ),
141                                         Qgis::MessageLevel level = Qgis::Warning );
142 
143     /**
144       * QgsQuickFeatureLayerPair factory for tuple of QgsFeature and QgsVectorLayer used in QgsQUick library.
145       * \param feature QgsFeature linked to new QgsQuickFeature instance.
146       * \param layer QgsVectorLayer which the feature belongs to, optional.
147       *
148       * \since QGIS 3.4
149       */
150     Q_INVOKABLE static QgsQuickFeatureLayerPair featureFactory( const QgsFeature &feature, QgsVectorLayer *layer = nullptr );
151 
152     /**
153       * Returns QUrl to image from library's /images folder.
154       *
155       * \since QGIS 3.4
156       */
157     Q_INVOKABLE static const QUrl getThemeIcon( const QString &name );
158 
159     /**
160       * Returns url to field editor component for a feature form.
161       * If the widgetName does not match any supported widget, text edit is returned.
162       * \param widgetName name of the attribute field widget
163       *
164       * \since QGIS 3.4
165       */
166     Q_INVOKABLE static const QUrl getEditorComponentSource( const QString &widgetName );
167 
168     /**
169      * \copydoc QgsCoordinateFormatter::format()
170      *
171      * \since QGIS 3.4
172      */
173     Q_INVOKABLE static QString formatPoint(
174       const QgsPoint &point,
175       QgsCoordinateFormatter::Format format = QgsCoordinateFormatter::FormatPair,
176       int decimals = 3,
177       QgsCoordinateFormatter::FormatFlags flags = QgsCoordinateFormatter::FlagDegreesUseStringSuffix );
178 
179     /**
180       * Converts distance to human readable distance
181       *
182       * This is useful for scalebar texts or output of the GPS accuracy
183       *
184       * The resulting units are determined automatically,
185       * based on requested system of measurement.
186       * e.g. 1222.234 m is converted to 1.2 km
187       *
188       * \param distance distance in units
189       * \param units units of dist
190       * \param decimals decimal to use
191       * \param destSystem system of measurement of the result
192       * \returns string represetation of dist in desired destSystem. For distance less than 0, 0 is returned.
193       *
194       * \since QGIS 3.4
195       */
196     Q_INVOKABLE static QString formatDistance( double distance,
197         QgsUnitTypes::DistanceUnit units,
198         int decimals,
199         QgsUnitTypes::SystemOfMeasurement destSystem = QgsUnitTypes::MetricSystem );
200 
201     /**
202       * Deletes file from a given path.
203       *
204       * \param filePath Absolute path to file
205       * \returns bool TRUE, if removal was successful, otherwise FALSE.
206       *
207       * \since QGIS 3.8
208       */
209     Q_INVOKABLE static bool removeFile( const QString &filePath );
210 
211     /**
212       * Converts distance to human readable distance in destination system of measurement
213       *
214       * \sa QgsQuickUtils::formatDistance()
215       *
216       * \param srcDistance distance in units
217       * \param srcUnits units of dist
218       * \param destSystem system of measurement of the result
219       * \param destDistance output: distance if desired system of measurement
220       * \param destUnits output: unit of destDistance
221       *
222       * \since QGIS 3.4
223       */
224     static void humanReadableDistance( double srcDistance,
225                                        QgsUnitTypes::DistanceUnit srcUnits,
226                                        QgsUnitTypes::SystemOfMeasurement destSystem,
227                                        double &destDistance,
228                                        QgsUnitTypes::DistanceUnit &destUnits );
229 
230     //! Returns a string with information about screen size and resolution - useful for debugging
231     QString dumpScreenInfo() const;
232 
233     /**
234      * Creates a cache for a value relation field.
235      * This can be used to keep the value map in the local memory
236      * if doing multiple lookups in a loop.
237      * \param config The widget configuration
238      * \param formFeature The feature currently being edited with current attribute values
239      * \return A kvp list of values for the widget
240      *
241      * \since QGIS 3.6
242      */
243     Q_INVOKABLE static QVariantMap createValueRelationCache( const QVariantMap &config, const QgsFeature &formFeature = QgsFeature() );
244 
245     /**
246      * Evaluates expression.
247      * \param pair Used to define a context scope.
248      * \param activeProject Used to define a context scope.
249      * \param expression
250      * \return Evaluated expression
251      *
252      * \since QGIS 3.10
253      */
254     Q_INVOKABLE static QString evaluateExpression( const QgsQuickFeatureLayerPair &pair, QgsProject *activeProject, const QString &expression );
255 
256     /**
257      * Selects features in a layer
258      * This method is required since QML cannot perform the conversion of a feature ID to a QgsFeatureId (i.e. a qint64)
259      * \param layer the vector layer
260      * \param fids the list of feature IDs
261      * \param behavior the selection behavior
262      *
263      * \since QGIS 3.12
264      */
265     Q_INVOKABLE static void selectFeaturesInLayer( QgsVectorLayer *layer, const QList<int> &fids, QgsVectorLayer::SelectBehavior behavior = QgsVectorLayer::SetSelection );
266 
267 
268     /**
269     * Returns the QVariant typeName of a \a field.
270     * This is a stable identifier (compared to the provider field name).
271     * \param field QgsField
272     */
273     Q_INVOKABLE static QString fieldType( const QgsField &field );
274 
275 
276     /**
277     * Returns field format's name for given string representing field format defined in QgsDateTimeFieldFormatter.
278     * \param fieldFormat string representing formats from QgsDateTimeFieldFormatter.
279     */
280     Q_INVOKABLE static QString dateTimeFieldFormat( const QString &fieldFormat );
281 
282   private:
283     static void formatToMetricDistance( double srcDistance,
284                                         QgsUnitTypes::DistanceUnit srcUnits,
285                                         double &destDistance,
286                                         QgsUnitTypes::DistanceUnit &destUnits );
287 
288     static void formatToImperialDistance( double srcDistance,
289                                           QgsUnitTypes::DistanceUnit srcUnits,
290                                           double &destDistance,
291                                           QgsUnitTypes::DistanceUnit &destUnits );
292 
293     static void formatToUSCSDistance( double srcDistance,
294                                       QgsUnitTypes::DistanceUnit srcUnits,
295                                       double &destDistance,
296                                       QgsUnitTypes::DistanceUnit &destUnits );
297 
298 
299     static qreal calculateScreenDensity();
300 
301     qreal mScreenDensity;
302 };
303 
304 #endif // QGSQUICKUTILS_H
305