Skip to content

Commit

Permalink
[FEATURE][locator] Add a goto locator
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jul 28, 2020
1 parent 0bb9174 commit 44fd606
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 2 deletions.
210 changes: 209 additions & 1 deletion src/app/locator/qgsinbuiltlocatorfilters.cpp
Expand Up @@ -15,10 +15,16 @@
* *
***************************************************************************/

#include <QToolButton>
#include <QClipboard>
#include <QMap>
#include <QString>
#include <QToolButton>
#include <QUrl>

#include "qgsapplication.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgscoordinateutils.h"
#include "qgsinbuiltlocatorfilters.h"
#include "qgsproject.h"
#include "qgslayertree.h"
Expand Down Expand Up @@ -702,3 +708,205 @@ void QgsBookmarkLocatorFilter::triggerResult( const QgsLocatorResult &result )
QModelIndex index = qvariant_cast<QModelIndex>( result.userData );
QgisApp::instance()->zoomToBookmarkIndex( index );
}

//
// QgsGotoLocatorFilter
//

QgsGotoLocatorFilter::QgsGotoLocatorFilter( QObject *parent )
: QgsLocatorFilter( parent )
{}

QgsGotoLocatorFilter *QgsGotoLocatorFilter::clone() const
{
return new QgsGotoLocatorFilter();
}

void QgsGotoLocatorFilter::fetchResults( const QString &string, const QgsLocatorContext &, QgsFeedback *feedback )
{
if ( feedback->isCanceled() )
return;

const QgsCoordinateReferenceSystem currentCrs = QgisApp::instance()->mapCanvas()->mapSettings().destinationCrs();
const QgsCoordinateReferenceSystem wgs84Crs( QStringLiteral( "EPSG:4326" ) );

// Coordinates such as 106.8468,-6.3804
QStringList coordinates = string.split( ' ' );
if ( coordinates.size() != 2 )
{
coordinates = string.split( ',' );
}

if ( coordinates.size() == 2 )
{
bool okX = false;
bool okY = false;
double posX = coordinates.at( 0 ).toDouble( &okX );
double posY = coordinates.at( 1 ).toDouble( &okY );

if ( okX && okY )
{
QVariantMap data;
QgsPointXY point( posX, posY );
data.insert( QStringLiteral( "point" ), point );

bool withinWgs84 = wgs84Crs.bounds().contains( point );
if ( currentCrs != wgs84Crs )
{
QgsLocatorResult result;
result.filter = this;
result.displayString = tr( "Go to %1 %2 (Map CRS, %3)" ).arg( QString::number( point.x(), 'g', 10 ), QString::number( point.y(), 'g', 10 ), currentCrs.userFriendlyIdentifier() );
result.userData = data;
result.score = 0.9;
emit resultFetched( result );
}

if ( withinWgs84 )
{
if ( currentCrs != wgs84Crs )
{
QgsCoordinateTransform transform( wgs84Crs, currentCrs, QgsProject::instance()->transformContext() );
QgsPointXY transformedPoint;
try
{
transformedPoint = transform.transform( point );
}
catch ( const QgsException &e )
{
Q_UNUSED( e )
return;
}
data[QStringLiteral( "point" )] = transformedPoint;
}

QgsLocatorResult result;
result.filter = this;
result.displayString = tr( "Go to %1° %2° (%3)" ).arg( QString::number( point.x(), 'g', 10 ), QString::number( point.y(), 'g', 10 ), wgs84Crs.userFriendlyIdentifier() );
result.userData = data;
result.score = 1.0;
emit resultFetched( result );
}
}
return;
}

QMap<int, double> scales;
scales[0] = 739571909;
scales[1] = 369785954;
scales[2] = 184892977;
scales[3] = 92446488;
scales[4] = 46223244;
scales[5] = 23111622;
scales[6] = 11555811;
scales[7] = 5777905;
scales[8] = 2888952;
scales[9] = 1444476;
scales[10] = 722238;
scales[11] = 361119;
scales[12] = 180559;
scales[13] = 90279;
scales[14] = 45139;
scales[15] = 22569;
scales[16] = 11284;
scales[17] = 5642;
scales[18] = 2821;
scales[19] = 1500;
scales[20] = 1000;

QUrl url( string );
if ( url.isValid() )
{
double scale = 0.0;
bool okX = false;
bool okY = false;
double posX = 0.0;
double posY = 0.0;
if ( url.hasFragment() )
{
// Check for OSM/Leaflet/OpenLayers pattern (e.g. http://www.openstreetmap.org/#map=6/46.423/4.746)
QStringList fragments = url.fragment().split( '&' );
for ( const QString &fragment : fragments )
{
if ( fragment.startsWith( QStringLiteral( "map=" ) ) )
{
QStringList params = fragment.mid( 4 ).split( '/' );
if ( params.size() >= 3 )
{
if ( scales.contains( params.at( 0 ).toInt() ) )
{
scale = scales.value( params.at( 0 ).toInt() );
}
posX = params.at( 2 ).toDouble( &okX );
posY = params.at( 1 ).toDouble( &okY );
}
break;
}
}
}

if ( !okX && !okY && string.indexOf( QStringLiteral( "google.com/maps/" ) ) != -1 )
{
QRegularExpression locationRx( QStringLiteral( "\\/@([0-9\\-\\.\\,]*)z" ) );
QRegularExpressionMatch match = locationRx.match( string );
if ( match.hasMatch() )
{
QStringList params = match.captured( 1 ).split( ',' );
if ( params.size() == 3 )
{
if ( scales.contains( params.at( 2 ).toInt() ) )
{
scale = scales.value( params.at( 2 ).toInt() );
}
posX = params.at( 1 ).toDouble( &okX );
posY = params.at( 0 ).toDouble( &okY );
}
}
}

if ( okX && okY )
{
QVariantMap data;
if ( scale > 0.0 )
{
data.insert( QStringLiteral( "scale" ), scale );
}

QgsPointXY point( posX, posY );
bool withinWgs84 = wgs84Crs.bounds().contains( point );
if ( withinWgs84 && currentCrs != wgs84Crs )
{
QgsCoordinateTransform transform( wgs84Crs, currentCrs, QgsProject::instance()->transformContext() );
QgsPointXY transformedPoint = transform.transform( point );
data.insert( QStringLiteral( "point" ), transformedPoint );
}
else
{
data.insert( QStringLiteral( "point" ), point );
}

QgsLocatorResult result;
result.filter = this;
result.displayString = tr( "Go to %1° %2° %3(%4)" ).arg( QString::number( point.x(), 'g', 10 ), QString::number( point.y(), 'g', 10 ),
scale > 0.0 ? tr( "at scale 1:%1 " ).arg( scale ) : QString(),
wgs84Crs.userFriendlyIdentifier() );
result.userData = data;
result.score = 1.0;
emit resultFetched( result );
}
}
}

void QgsGotoLocatorFilter::triggerResult( const QgsLocatorResult &result )
{
QVariantMap data = result.userData.toMap();
QgsMapCanvas *mapCanvas = QgisApp::instance()->mapCanvas();
mapCanvas->setCenter( data[QStringLiteral( "point" )].value<QgsPointXY>() );
if ( data.contains( QStringLiteral( "scale" ) ) )
{
mapCanvas->zoomScale( data[QStringLiteral( "scale" )].toDouble() );
}
else
{
mapCanvas->refresh();
}
}
19 changes: 19 additions & 0 deletions src/app/locator/qgsinbuiltlocatorfilters.h
Expand Up @@ -219,6 +219,25 @@ class APP_EXPORT QgsSettingsLocatorFilter : public QgsLocatorFilter
QMap<QString, QString> settingsPage( const QString &type, const QString &page );
};

class APP_EXPORT QgsGotoLocatorFilter : public QgsLocatorFilter
{
Q_OBJECT

public:


QgsGotoLocatorFilter( QObject *parent = nullptr );
QgsGotoLocatorFilter *clone() const override;
virtual QString name() const override { return QStringLiteral( "goto" ); }
virtual QString displayName() const override { return tr( "Go to" ); }
virtual Priority priority() const override { return Medium; }
QString prefix() const override { return QStringLiteral( "goto" ); }

void fetchResults( const QString &string, const QgsLocatorContext &context, QgsFeedback *feedback ) override;
void triggerResult( const QgsLocatorResult &result ) override;

};

#endif // QGSINBUILTLOCATORFILTERS_H


1 change: 1 addition & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -3839,6 +3839,7 @@ void QgisApp::createStatusBar()
mLocatorWidget->locator()->registerFilter( new QgsExpressionCalculatorLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsBookmarkLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsSettingsLocatorFilter() );
mLocatorWidget->locator()->registerFilter( new QgsGotoLocatorFilter() );
}

void QgisApp::setIconSizes( int size )
Expand Down
3 changes: 2 additions & 1 deletion src/core/locator/qgslocator.cpp
Expand Up @@ -29,7 +29,8 @@ const QList<QString> QgsLocator::CORE_FILTERS = QList<QString>() << QStringLiter
<< QStringLiteral( "calculator" )
<< QStringLiteral( "bookmarks" )
<< QStringLiteral( "optionpages" )
<< QStringLiteral( "edit_features" );
<< QStringLiteral( "edit_features" )
<< QStringLiteral( "goto" );

QgsLocator::QgsLocator( QObject *parent )
: QObject( parent )
Expand Down
28 changes: 28 additions & 0 deletions tests/src/app/testqgsapplocatorfilters.cpp
Expand Up @@ -39,6 +39,7 @@ class TestQgsAppLocatorFilters : public QObject
void testSearchActiveLayer();
void testSearchAllLayers();
void testSearchAllLayersPrioritizeExactMatch();
void testGoto();

private:
QgisApp *mQgisApp = nullptr;
Expand Down Expand Up @@ -272,5 +273,32 @@ QList<QgsLocatorResult> TestQgsAppLocatorFilters::gatherResults( QgsLocatorFilte
return results;
}

void TestQgsAppLocatorFilters::testGoto()
{
QgsGotoLocatorFilter filter;

// goto X,Y
QList< QgsLocatorResult > results = gatherResults( &filter, QStringLiteral( "4 5" ), QgsLocatorContext() );
QCOMPARE( results.count(), 2 );
QCOMPARE( results.at( 0 ).displayString, QObject::tr( "Go to 4 5 (Map CRS, )" ) );
QCOMPARE( results.at( 0 ).userData.toMap()[QStringLiteral( "point" )].value<QgsPointXY>(), QgsPointXY( 4, 5 ) );
QCOMPARE( results.at( 1 ).displayString, QObject::tr( "Go to 4° 5° (EPSG:4326 - WGS 84)" ) );
QCOMPARE( results.at( 1 ).userData.toMap()[QStringLiteral( "point" )].value<QgsPointXY>(), QgsPointXY( 4, 5 ) );

// OSM/Leaflet/OpenLayers
results = gatherResults( &filter, QStringLiteral( "https://www.openstreetmap.org/#map=15/44.5546/6.4936" ), QgsLocatorContext() );
QCOMPARE( results.count(), 1 );
QCOMPARE( results.at( 0 ).displayString, QObject::tr( "Go to 6.4936° 44.5546° at scale 1:22569 (EPSG:4326 - WGS 84)" ) );
QCOMPARE( results.at( 0 ).userData.toMap()[QStringLiteral( "point" )].value<QgsPointXY>(), QgsPointXY( 6.4936, 44.5546 ) );
QCOMPARE( results.at( 0 ).userData.toMap()[QStringLiteral( "scale" )].toDouble(), 22569.0 );

// Google Maps
results = gatherResults( &filter, QStringLiteral( "https://www.google.com/maps/@44.5546,6.4936,15z" ), QgsLocatorContext() );
QCOMPARE( results.count(), 1 );
QCOMPARE( results.at( 0 ).displayString, QObject::tr( "Go to 6.4936° 44.5546° at scale 1:22569 (EPSG:4326 - WGS 84)" ) );
QCOMPARE( results.at( 0 ).userData.toMap()[QStringLiteral( "point" )].value<QgsPointXY>(), QgsPointXY( 6.4936, 44.5546 ) );
QCOMPARE( results.at( 0 ).userData.toMap()[QStringLiteral( "scale" )].toDouble(), 22569.0 );
}

QGSTEST_MAIN( TestQgsAppLocatorFilters )
#include "testqgsapplocatorfilters.moc"

0 comments on commit 44fd606

Please sign in to comment.