1 /***************************************************************************
2 qgsquickutils.cpp
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 #include <QGuiApplication>
17 #include <QScreen>
18 #include <QString>
19 #include <QWindow>
20
21 #include "qgis.h"
22 #include "qgscoordinatereferencesystem.h"
23 #include "qgscoordinatetransform.h"
24 #include "qgsdistancearea.h"
25 #include "qgslogger.h"
26 #include "qgsvectorlayer.h"
27 #include "qgsfeature.h"
28 #include "qgsapplication.h"
29 #include "qgsvaluerelationfieldformatter.h"
30 #include "qgsdatetimefieldformatter.h"
31
32 #include "qgsquickfeaturelayerpair.h"
33 #include "qgsquickmapsettings.h"
34 #include "qgsquickutils.h"
35 #include "qgsunittypes.h"
36
37
QgsQuickUtils(QObject * parent)38 QgsQuickUtils::QgsQuickUtils( QObject *parent )
39 : QObject( parent )
40 , mScreenDensity( calculateScreenDensity() )
41 {
42 }
43
44 /**
45 * Makes QgsCoordinateReferenceSystem::fromEpsgId accessible for QML components
46 */
coordinateReferenceSystemFromEpsgId(long epsg)47 QgsCoordinateReferenceSystem QgsQuickUtils::coordinateReferenceSystemFromEpsgId( long epsg )
48 {
49 return QgsCoordinateReferenceSystem::fromEpsgId( epsg );
50 }
51
pointXY(double x,double y)52 QgsPointXY QgsQuickUtils::pointXY( double x, double y )
53 {
54 return QgsPointXY( x, y );
55 }
56
point(double x,double y,double z,double m)57 QgsPoint QgsQuickUtils::point( double x, double y, double z, double m )
58 {
59 return QgsPoint( x, y, z, m );
60 }
61
coordinateToPoint(const QGeoCoordinate & coor)62 QgsPoint QgsQuickUtils::coordinateToPoint( const QGeoCoordinate &coor )
63 {
64 return QgsPoint( coor.longitude(), coor.latitude(), coor.altitude() );
65 }
66
transformPoint(const QgsCoordinateReferenceSystem & srcCrs,const QgsCoordinateReferenceSystem & destCrs,const QgsCoordinateTransformContext & context,const QgsPointXY & srcPoint)67 QgsPointXY QgsQuickUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
68 const QgsCoordinateReferenceSystem &destCrs,
69 const QgsCoordinateTransformContext &context,
70 const QgsPointXY &srcPoint )
71 {
72 QgsCoordinateTransform mTransform( srcCrs, destCrs, context );
73 QgsPointXY pt = mTransform.transform( srcPoint );
74 return pt;
75 }
76
screenUnitsToMeters(QgsQuickMapSettings * mapSettings,int baseLengthPixels)77 double QgsQuickUtils::screenUnitsToMeters( QgsQuickMapSettings *mapSettings, int baseLengthPixels )
78 {
79 if ( mapSettings == nullptr ) return 0.0;
80
81 QgsDistanceArea mDistanceArea;
82 mDistanceArea.setEllipsoid( QStringLiteral( "WGS84" ) );
83 mDistanceArea.setSourceCrs( mapSettings->destinationCrs(), mapSettings->transformContext() );
84
85 // calculate the geographic distance from the central point of extent
86 // to the specified number of points on the right side
87 QSize s = mapSettings->outputSize();
88 QPoint pointCenter( s.width() / 2, s.height() / 2 );
89 QgsPointXY p1 = mapSettings->screenToCoordinate( pointCenter );
90 QgsPointXY p2 = mapSettings->screenToCoordinate( pointCenter + QPoint( baseLengthPixels, 0 ) );
91 return mDistanceArea.measureLine( p1, p2 );
92 }
93
fileExists(const QString & path)94 bool QgsQuickUtils::fileExists( const QString &path )
95 {
96 QFileInfo check_file( path );
97 // check if file exists and if yes: Is it really a file and no directory?
98 return ( check_file.exists() && check_file.isFile() );
99 }
100
getRelativePath(const QString & path,const QString & prefixPath)101 QString QgsQuickUtils::getRelativePath( const QString &path, const QString &prefixPath )
102 {
103 QString modPath = path;
104 QString filePrefix( "file://" );
105
106 if ( path.startsWith( filePrefix ) )
107 {
108 modPath = modPath.replace( filePrefix, QString() );
109 }
110
111 if ( prefixPath.isEmpty() ) return modPath;
112
113 // Do not use a canonical path for non-existing path
114 if ( !QFileInfo( path ).exists() )
115 {
116 if ( !prefixPath.isEmpty() && modPath.startsWith( prefixPath ) )
117 {
118 return modPath.replace( prefixPath, QString() );
119 }
120 }
121 else
122 {
123 QDir absoluteDir( modPath );
124 QDir prefixDir( prefixPath );
125 QString canonicalPath = absoluteDir.canonicalPath();
126 QString prefixCanonicalPath = prefixDir.canonicalPath() + "/";
127
128 if ( prefixCanonicalPath.length() > 1 && canonicalPath.startsWith( prefixCanonicalPath ) )
129 {
130 return canonicalPath.replace( prefixCanonicalPath, QString() );
131 }
132 }
133
134 return QString();
135 }
136
logMessage(const QString & message,const QString & tag,Qgis::MessageLevel level)137 void QgsQuickUtils::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level )
138 {
139 QgsMessageLog::logMessage( message, tag, level );
140 }
141
featureFactory(const QgsFeature & feature,QgsVectorLayer * layer)142 QgsQuickFeatureLayerPair QgsQuickUtils::featureFactory( const QgsFeature &feature, QgsVectorLayer *layer )
143 {
144 return QgsQuickFeatureLayerPair( feature, layer );
145 }
146
getThemeIcon(const QString & name)147 const QUrl QgsQuickUtils::getThemeIcon( const QString &name )
148 {
149 QString path = QStringLiteral( "qrc:/%1.svg" ).arg( name );
150 QgsDebugMsg( QStringLiteral( "Using icon %1 from %2" ).arg( name, path ) );
151 return QUrl( path );
152 }
153
getEditorComponentSource(const QString & widgetName)154 const QUrl QgsQuickUtils::getEditorComponentSource( const QString &widgetName )
155 {
156 QString path( "qgsquick%1.qml" );
157 QStringList supportedWidgets = { QStringLiteral( "textedit" ),
158 QStringLiteral( "valuemap" ),
159 QStringLiteral( "valuerelation" ),
160 QStringLiteral( "checkbox" ),
161 QStringLiteral( "externalresource" ),
162 QStringLiteral( "datetime" ),
163 QStringLiteral( "range" )
164 };
165 if ( supportedWidgets.contains( widgetName ) )
166 {
167 return QUrl( path.arg( widgetName ) );
168 }
169 else
170 {
171 return QUrl( path.arg( QLatin1String( "textedit" ) ) );
172 }
173 }
174
formatPoint(const QgsPoint & point,QgsCoordinateFormatter::Format format,int decimals,QgsCoordinateFormatter::FormatFlags flags)175 QString QgsQuickUtils::formatPoint(
176 const QgsPoint &point,
177 QgsCoordinateFormatter::Format format,
178 int decimals,
179 QgsCoordinateFormatter::FormatFlags flags )
180 {
181 return QgsCoordinateFormatter::format( point, format, decimals, flags );
182 }
183
formatDistance(double distance,QgsUnitTypes::DistanceUnit units,int decimals,QgsUnitTypes::SystemOfMeasurement destSystem)184 QString QgsQuickUtils::formatDistance( double distance,
185 QgsUnitTypes::DistanceUnit units,
186 int decimals,
187 QgsUnitTypes::SystemOfMeasurement destSystem )
188 {
189 double destDistance;
190 QgsUnitTypes::DistanceUnit destUnits;
191
192 humanReadableDistance( distance, units, destSystem, destDistance, destUnits );
193
194 return QStringLiteral( "%1 %2" )
195 .arg( QString::number( destDistance, 'f', decimals ) )
196 .arg( QgsUnitTypes::toAbbreviatedString( destUnits ) );
197 }
198
removeFile(const QString & filePath)199 bool QgsQuickUtils::removeFile( const QString &filePath )
200 {
201 QFile file( filePath );
202 return file.remove( filePath );
203 }
204
205
humanReadableDistance(double srcDistance,QgsUnitTypes::DistanceUnit srcUnits,QgsUnitTypes::SystemOfMeasurement destSystem,double & destDistance,QgsUnitTypes::DistanceUnit & destUnits)206 void QgsQuickUtils::humanReadableDistance( double srcDistance, QgsUnitTypes::DistanceUnit srcUnits,
207 QgsUnitTypes::SystemOfMeasurement destSystem,
208 double &destDistance, QgsUnitTypes::DistanceUnit &destUnits )
209 {
210 if ( ( destSystem == QgsUnitTypes::MetricSystem ) || ( destSystem == QgsUnitTypes::UnknownSystem ) )
211 {
212 return formatToMetricDistance( srcDistance, srcUnits, destDistance, destUnits );
213 }
214 else if ( destSystem == QgsUnitTypes::ImperialSystem )
215 {
216 return formatToImperialDistance( srcDistance, srcUnits, destDistance, destUnits );
217 }
218 else if ( destSystem == QgsUnitTypes::USCSSystem )
219 {
220 return formatToUSCSDistance( srcDistance, srcUnits, destDistance, destUnits );
221 }
222 else
223 {
224 Q_ASSERT( false ); //should never happen
225 }
226 }
227
formatToMetricDistance(double srcDistance,QgsUnitTypes::DistanceUnit srcUnits,double & destDistance,QgsUnitTypes::DistanceUnit & destUnits)228 void QgsQuickUtils::formatToMetricDistance( double srcDistance,
229 QgsUnitTypes::DistanceUnit srcUnits,
230 double &destDistance,
231 QgsUnitTypes::DistanceUnit &destUnits )
232 {
233 double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceMillimeters );
234 if ( dist < 0 )
235 {
236 destDistance = 0;
237 destUnits = QgsUnitTypes::DistanceMillimeters;
238 return;
239 }
240
241 double mmToKm = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceKilometers, QgsUnitTypes::DistanceMillimeters );
242 if ( dist > mmToKm )
243 {
244 destDistance = dist / mmToKm;
245 destUnits = QgsUnitTypes::DistanceKilometers;
246 return;
247 }
248
249 double mmToM = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, QgsUnitTypes::DistanceMillimeters );
250 if ( dist > mmToM )
251 {
252 destDistance = dist / mmToM;
253 destUnits = QgsUnitTypes::DistanceMeters;
254 return;
255 }
256
257 double mmToCm = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceCentimeters, QgsUnitTypes::DistanceMillimeters );
258 if ( dist > mmToCm )
259 {
260 destDistance = dist / mmToCm;
261 destUnits = QgsUnitTypes::DistanceCentimeters;
262 return;
263 }
264
265 destDistance = dist;
266 destUnits = QgsUnitTypes::DistanceMillimeters;
267 }
268
formatToImperialDistance(double srcDistance,QgsUnitTypes::DistanceUnit srcUnits,double & destDistance,QgsUnitTypes::DistanceUnit & destUnits)269 void QgsQuickUtils::formatToImperialDistance( double srcDistance,
270 QgsUnitTypes::DistanceUnit srcUnits,
271 double &destDistance,
272 QgsUnitTypes::DistanceUnit &destUnits )
273 {
274 double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceFeet );
275 if ( dist < 0 )
276 {
277 destDistance = 0;
278 destUnits = QgsUnitTypes::DistanceFeet;
279 return;
280 }
281
282 double feetToMile = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMiles, QgsUnitTypes::DistanceFeet );
283 if ( dist > feetToMile )
284 {
285 destDistance = dist / feetToMile;
286 destUnits = QgsUnitTypes::DistanceMiles;
287 return;
288 }
289
290 double feetToYard = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceYards, QgsUnitTypes::DistanceFeet );
291 if ( dist > feetToYard )
292 {
293 destDistance = dist / feetToYard;
294 destUnits = QgsUnitTypes::DistanceYards;
295 return;
296 }
297
298 destDistance = dist;
299 destUnits = QgsUnitTypes::DistanceFeet;
300 return;
301 }
302
formatToUSCSDistance(double srcDistance,QgsUnitTypes::DistanceUnit srcUnits,double & destDistance,QgsUnitTypes::DistanceUnit & destUnits)303 void QgsQuickUtils::formatToUSCSDistance( double srcDistance,
304 QgsUnitTypes::DistanceUnit srcUnits,
305 double &destDistance,
306 QgsUnitTypes::DistanceUnit &destUnits )
307 {
308 double dist = srcDistance * QgsUnitTypes::fromUnitToUnitFactor( srcUnits, QgsUnitTypes::DistanceFeet );
309 if ( dist < 0 )
310 {
311 destDistance = 0;
312 destUnits = QgsUnitTypes::DistanceFeet;
313 return;
314 }
315
316 double feetToMile = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceNauticalMiles, QgsUnitTypes::DistanceFeet );
317 if ( dist > feetToMile )
318 {
319 destDistance = dist / feetToMile;
320 destUnits = QgsUnitTypes::DistanceNauticalMiles;
321 return;
322 }
323
324 double feetToYard = QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceYards, QgsUnitTypes::DistanceFeet );
325 if ( dist > feetToYard )
326 {
327 destDistance = dist / feetToYard;
328 destUnits = QgsUnitTypes::DistanceYards;
329 return;
330 }
331
332 destDistance = dist;
333 destUnits = QgsUnitTypes::DistanceFeet;
334 return;
335 }
336
dumpScreenInfo() const337 QString QgsQuickUtils::dumpScreenInfo() const
338 {
339 // take the first top level window
340 QScreen *screen = QGuiApplication::topLevelWindows().at( 0 )->screen();
341 double dpiX = screen->physicalDotsPerInchX();
342 double dpiY = screen->physicalDotsPerInchY();
343 int height = screen->geometry().height();
344 int width = screen->geometry().width();
345 double sizeX = static_cast<double>( width ) / dpiX * 25.4;
346 double sizeY = static_cast<double>( height ) / dpiY * 25.4;
347
348 QString msg;
349 msg += tr( "screen resolution: %1x%2 px\n" ).arg( width ).arg( height );
350 msg += tr( "screen DPI: %1x%2\n" ).arg( dpiX ).arg( dpiY );
351 msg += tr( "screen size: %1x%2 mm\n" ).arg( QString::number( sizeX, 'f', 0 ), QString::number( sizeY, 'f', 0 ) );
352 msg += tr( "screen density: %1" ).arg( mScreenDensity );
353 return msg;
354 }
355
createValueRelationCache(const QVariantMap & config,const QgsFeature & formFeature)356 QVariantMap QgsQuickUtils::createValueRelationCache( const QVariantMap &config, const QgsFeature &formFeature )
357 {
358 QVariantMap valueMap;
359 QgsValueRelationFieldFormatter::ValueRelationCache cache = QgsValueRelationFieldFormatter::createCache( config, formFeature );
360
361 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( cache ) )
362 {
363 valueMap.insert( item.key.toString(), item.value );
364 }
365 return valueMap;
366 }
367
evaluateExpression(const QgsQuickFeatureLayerPair & pair,QgsProject * activeProject,const QString & expression)368 QString QgsQuickUtils::evaluateExpression( const QgsQuickFeatureLayerPair &pair, QgsProject *activeProject, const QString &expression )
369 {
370 QList<QgsExpressionContextScope *> scopes;
371 scopes << QgsExpressionContextUtils::globalScope();
372 scopes << QgsExpressionContextUtils::projectScope( activeProject );
373 scopes << QgsExpressionContextUtils::layerScope( pair.layer() );
374
375 QgsExpressionContext context( scopes );
376 context.setFeature( pair.feature() );
377 QgsExpression expr( expression );
378 return expr.evaluate( &context ).toString();
379 }
380
selectFeaturesInLayer(QgsVectorLayer * layer,const QList<int> & fids,QgsVectorLayer::SelectBehavior behavior)381 void QgsQuickUtils::selectFeaturesInLayer( QgsVectorLayer *layer, const QList<int> &fids, QgsVectorLayer::SelectBehavior behavior )
382 {
383 QgsFeatureIds qgsFids;
384 for ( const int &fid : fids )
385 qgsFids << fid;
386 layer->selectByIds( qgsFids, behavior );
387 }
388
fieldType(const QgsField & field)389 QString QgsQuickUtils::fieldType( const QgsField &field )
390 {
391 return QVariant( field.type() ).typeName();
392 }
393
dateTimeFieldFormat(const QString & fieldFormat)394 QString QgsQuickUtils::dateTimeFieldFormat( const QString &fieldFormat )
395 {
396 if ( QgsDateTimeFieldFormatter::DATE_FORMAT == fieldFormat )
397 {
398 return QString( "Date" );
399 }
400 else if ( QgsDateTimeFieldFormatter::TIME_FORMAT == fieldFormat )
401 {
402 return QString( "Time" );
403 }
404 else if ( QgsDateTimeFieldFormatter::DATETIME_FORMAT == fieldFormat )
405 {
406 return QString( "Date Time" );
407 }
408 else
409 {
410 return QString( "Date Time" );
411 }
412 }
413
screenDensity() const414 qreal QgsQuickUtils::screenDensity() const
415 {
416 return mScreenDensity;
417 }
calculateScreenDensity()418 qreal QgsQuickUtils::calculateScreenDensity()
419 {
420 // calculate screen density for calculation of real pixel sizes from density-independent pixels
421 // take the first top level window
422 QScreen *screen = QGuiApplication::topLevelWindows().at( 0 )->screen();
423 double dpiX = screen->physicalDotsPerInchX();
424 double dpiY = screen->physicalDotsPerInchY();
425 double dpi = dpiX < dpiY ? dpiX : dpiY; // In case of asymmetrical DPI. Improbable
426 return dpi / 160.; // 160 DPI is baseline for density-independent pixels in Android
427 }
428