2

I am trying to use the osm api via a QGeoServiceProvider and QGeoCodingManager to derive the latitude and longitude from a given physical address. Thereby I stumbled upon this Question, which seems to do exactly what I need.

However I want to implement this in a qt gui application as a separate function and I don' t understand what is happening in the connect. Could someone please explain or how I can modify it to get it to work in a function ?

    cout << "Try service: " << "osm" << endl;
    // choose provider
    QGeoServiceProvider qGeoService("osm");
    QGeoCodingManager *pQGeoCoder = qGeoService.geocodingManager();
    if (!pQGeoCoder) {
      cerr
        << "GeoCodingManager '" << "osm"
        << "' not available!" << endl;

    }
    QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
    pQGeoCoder->setLocale(qLocaleC);
    // build address
    QGeoAddress qGeoAddr;
    qGeoAddr.setCountry(QString::fromUtf8("Germany"));
    qGeoAddr.setPostalCode(QString::fromUtf8("88250"));
    qGeoAddr.setCity(QString::fromUtf8("Weingarten"));
    qGeoAddr.setStreet(QString::fromUtf8("Heinrich-Hertz-Str. 6"));
    QGeoCodeReply *pQGeoCode = pQGeoCoder->geocode(qGeoAddr);
    if (!pQGeoCode) {
      cerr << "GeoCoding totally failed!" << endl;

    }
    cout << "Searching..." << endl;
    QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
      [&qGeoAddr, pQGeoCode](){
        cout << "Reply: " << pQGeoCode->errorString().toStdString() << endl;
        switch (pQGeoCode->error()) {
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: cerr << #ERROR << endl; break
          CASE(NoError);
          CASE(EngineNotSetError);
          CASE(CommunicationError);
          CASE(ParseError);
          CASE(UnsupportedOptionError);
          CASE(CombinationError);
          CASE(UnknownError);
#undef CASE
          default: cerr << "Undocumented error!" << endl;
        }
        if (pQGeoCode->error() != QGeoCodeReply::NoError) return;
        // eval. result
        QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
        cout << qGeoLocs.size() << " location(s) returned." << endl;
        for (QGeoLocation &qGeoLoc : qGeoLocs) {
          qGeoLoc.setAddress(qGeoAddr);
          QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
          cout
            << "Lat.:  " << qGeoCoord.latitude() << endl
            << "Long.: " << qGeoCoord.longitude() << endl
            << "Alt.:  " << qGeoCoord.altitude() << endl;
        }
      });

So I just removed the QApplication part because I dont have this reference in a function. As a result i get:

Qt Version: 5.10.0
Try service: osm
Searching...

but no coordinates. I assume the connection to where the lat and lon data is acquired fails. But as I mentioned I dont understand the connect here. Any help is appreciated

EDIT so I tried to build a connect to catch the finished Signal as suggested. The code now looks like this:

QGeoServiceProvider qGeoService("osm");
  QGeoCodingManager *pQGeoCoder = qGeoService.geocodingManager();
  if (!pQGeoCoder) {
    cerr
      << "GeoCodingManager '" << "osm"
      << "' not available!" << endl;

  }
  QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
  pQGeoCoder->setLocale(qLocaleC);
  // build address
  //QGeoAddress qGeoAddr;
  qGeoAddr.setCountry(QString::fromUtf8("Germany"));
  qGeoAddr.setPostalCode(QString::fromUtf8("88250"));
  qGeoAddr.setCity(QString::fromUtf8("Weingarten"));
  qGeoAddr.setStreet(QString::fromUtf8("Heinrich-Hertz-Str. 6"));
  this->pQGeoCode = pQGeoCoder->geocode(qGeoAddr);
  if (!pQGeoCode) {
    cerr << "GeoCoding totally failed!" << endl;

  }
  cout << "Searching..." << endl;

  connect(pQGeoCode,SIGNAL(finished()),this,SLOT(getlonlat()));

With the Slot:

void MainWindow::getlonlat()
    {
      QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
                cout << qGeoLocs.size() << " location(s) returned." << endl;
                for (QGeoLocation &qGeoLoc : qGeoLocs) {
                  qGeoLoc.setAddress(qGeoAddr);
                  QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
                  cout
                    << "Lat.:  " << qGeoCoord.latitude() << endl
                    << "Long.: " << qGeoCoord.longitude() << endl
                    << "Alt.:  " << qGeoCoord.altitude() << endl;
                }
    }

However the finished signal doesn't get triggered. Therefore the result is the same. EDIT

Implementing the Code from your Gui Answer:

MainWindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

// standard C++ header:
#include <iostream>
#include <sstream>

// Qt header:
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>



MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{


  QGeoServiceProvider *pQGeoProvider = nullptr;

  ui->setupUi(this);


  qDebug() << "Qt Version:" << QT_VERSION_STR;
    // main application
    //QApplication app(argc, argv);

    // install signal handlers
    QObject::connect(this->ui->qBtnInit, &QPushButton::clicked,
      [&]() {
        if (pQGeoProvider) delete pQGeoProvider;
        std::ostringstream out;
        pQGeoProvider = init(out);
        log(out.str());
      });
    QObject::connect(this->ui->qBtnFind, &QPushButton::clicked,
      [&]() {
        // init geo coder if not yet done
        if (!pQGeoProvider) {
          std::ostringstream out;
          pQGeoProvider = init(out);
          log(out.str());
          if (!pQGeoProvider) return; // failed
        }
        // fill in request
        QGeoAddress *pQGeoAddr = new QGeoAddress;
        pQGeoAddr->setCountry(this->ui->qTxtCountry->text());
        pQGeoAddr->setPostalCode(this->ui->qTxtZipCode->text());
        pQGeoAddr->setCity(this->ui->qTxtCity->text());
        pQGeoAddr->setStreet(this->ui->qTxtStreet->text());
        QGeoCodeReply *pQGeoCode
          = pQGeoProvider->geocodingManager()->geocode(*pQGeoAddr);
        if (!pQGeoCode) {
          delete pQGeoAddr;
          log("GeoCoding totally failed!\n");
          return;
        }
        { std::ostringstream out;
          out << "Sending request for:\n"
            << pQGeoAddr->country().toUtf8().data() << "; "
            << pQGeoAddr->postalCode().toUtf8().data() << "; "
            << pQGeoAddr->city().toUtf8().data() << "; "
            << pQGeoAddr->street().toUtf8().data() << "...\n";
          log(out.str());
        }
        // install signal handler to process result later
        QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
          [&,pQGeoAddr, pQGeoCode]() {
            // process reply
            std::ostringstream out;
            out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
            switch (pQGeoCode->error()) {
              case QGeoCodeReply::NoError: {
                // eval result
                QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
                out << qGeoLocs.size() << " location(s) returned.\n";
                for (QGeoLocation &qGeoLoc : qGeoLocs) {
                  qGeoLoc.setAddress(*pQGeoAddr);
                  QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
                  out
                    << "Lat.:  " << qGeoCoord.latitude() << '\n'
                    << "Long.: " << qGeoCoord.longitude() << '\n'
                    << "Alt.:  " << qGeoCoord.altitude() << '\n';
                }
              } break;
  #define CASE(ERROR) \
  case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
              CASE(EngineNotSetError);
              CASE(CommunicationError);
              CASE(ParseError);
              CASE(UnsupportedOptionError);
              CASE(CombinationError);
              CASE(UnknownError);
  #undef CASE
              default: out << "Undocumented error!\n";
            }
            // log result
            log(out.str());
            // clean-up
            delete pQGeoAddr;
            /* delete sender in signal handler could be lethal
             * Hence, delete it later...
             */
            pQGeoCode->deleteLater();
          });
      });
    // fill in a sample request with a known address initially
    this->ui->qTxtCountry->setText(QString::fromUtf8("Germany"));
    this->ui->qTxtZipCode->setText(QString::fromUtf8("88250"));
    this->ui->qTxtCity->setText(QString::fromUtf8("Weingarten"));
    this->ui->qTxtStreet->setText(QString::fromUtf8("Danziger Str. 3"));


}

MainWindow::~MainWindow()
{
  delete ui;
}


void MainWindow::log(const QString &qString)
{
  this->ui->qTxtLog->setPlainText(this->ui->qTxtLog->toPlainText() + qString);
  this->ui->qTxtLog->moveCursor(QTextCursor::End);
}
void MainWindow::log(const char *text)
{
  log(QString::fromUtf8(text));
}
void MainWindow::log(const std::string &text)
{
  log(text.c_str());
}

QGeoServiceProvider* MainWindow::init(std::ostream &out)
{
  // check for available services
  QStringList qGeoSrvList
    = QGeoServiceProvider::availableServiceProviders();
  for (QString entry : qGeoSrvList) {
    out << "Try service: " << entry.toStdString() << '\n';
    // choose provider
    QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
    if (!pQGeoProvider) {
      out
        << "ERROR: GeoServiceProvider '" << entry.toStdString()
        << "' not available!\n";
      continue;
    }
    QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
    if (!pQGeoCoder) {
      out
        << "ERROR: GeoCodingManager '" << entry.toStdString()
        << "' not available!\n";
      delete pQGeoProvider;
      continue;
    }
    QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
    pQGeoCoder->setLocale(qLocaleC);
    out << "Using service " << entry.toStdString() << '\n';
    return pQGeoProvider; // success
  }
  out << "ERROR: No suitable GeoServiceProvider found!\n";
  return nullptr; // all attempts failed
}

The main.cpp:

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  MainWindow w;
  w.show();

  return a.exec();
}

And here the Header File for the MainWindow:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
// standard C++ header:
#include <iostream>
#include <sstream>

// Qt header:

#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>
namespace Ui {
  class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
  explicit MainWindow(QWidget *parent = 0);
  virtual ~MainWindow();


  void log(const QString &qString);

  void log(const char *text);

  void log(const std::string &text);


  QGeoServiceProvider* init(std::ostream &out);

private:
  Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H
Astraeus
  • 37
  • 8
  • The request to `QGeoCodeReply` is processed asynchronously (internally). Hence, the reply is not returned immediately. Instead a signal handler `QGeoCodeReply::finished` is called as soon as the reply from server has been received (or the timeout is matched i.e. request failed). Therefore, the signal handler has to be `connect`ed, and therefore the `QCoreApplication` must be instanced and `exec`ed until the result has been received. (Otherwise, the application won't run until results received, and signal handler won't never be called.) – Scheff's Cat May 15 '18 at 08:16
  • OT. Btw. we moved from "Heinrich-Hertz-Str. 6" to "Stettiner Str. 3" just at beginning of this month... ;-) – Scheff's Cat May 15 '18 at 08:18
  • These `GeoServiceProvider`s are actually special internet services. So, it makes very sense to make the access to such a service asynchronously. In your GUI application, you already have an instance of `QApplication`. (In that sample, I used `QCoreApplication` as I wanted to do the sample as console-only app.) However, I'm afraid you cannot prevent the `QGeoCodeReply::finished` signal handler. If you would put request and waiting for finished in a function call, you had at least to process application events until finished is received in this function. – Scheff's Cat May 15 '18 at 08:26
  • Nice :D, Thanks for your answer. I get what you mean and this seems to be the case. But I want to implement this in my MainWindow class, not in the main.cpp so I dont see how I can instantiate another QCoreApplication. Can you think of a solution for this case ? – Astraeus May 15 '18 at 08:30
  • So unless I exit my QApplication the finished Signal will not be emitted? – Astraeus May 15 '18 at 08:38
  • You got it wrong: In my sample the `qApp` is exited after/because the `finished` was received (because waiting for the `finished` geo code request was the only reason to keep it running). You shouldn't, of course, not exit your GUI application when you received the `finished`. (`finished` means the request was finished.) ;-) – Scheff's Cat May 15 '18 at 08:41
  • Okay this makes sense ;) But still I can' t find a solution on how to get it to work. I tried to force the finished signal, which results in zero locations fund, which is logical since the query probably hasnt been processed yet. – Astraeus May 15 '18 at 08:45
  • Please, have a look at [`QGeoCodingManager::geocode()`](http://doc.qt.io/qt-5/qgeocodingmanager.html#geocode-1). This starts a request. The returned `QGeoCodeReply*` is like a handle to this request. But how can you know that the geo coding manager completed the request? You could periodically call [`QGeoCodeReply::isFinished()`](http://doc.qt.io/qt-5/qgeocodereply.html#isFinished) (e.g. in a `QTimer`) or you simply connect the `finished` signal of `QGeoCodeReply` which is called as soon as the manager finished this reply. – Scheff's Cat May 15 '18 at 08:48
  • Okay I will try that. Thank you very much for your help so far, I just started with the GeoCoding. If you are interested I will use this for research in autonomous vehicles ;) – Astraeus May 15 '18 at 08:52
  • I seem to be to stupid to get it working. If it isnt too much to ask could you maybe give me a minimal working example if you have the time ? – Astraeus May 15 '18 at 09:38
  • Not immediately, but later this day... – Scheff's Cat May 15 '18 at 09:42
  • May be, it was not the best idea to pack all the relevant stuff in lambdas. When I did this I kept the scope and life-time of the resp. variables very carefully in mind. In your case, make things simpler: Put the vector of identified service providers in a member variable of your `class MainWindow`. The initialization of service providers (in my sample `pQGeoProviders`) might be done in the constructor. (You may delete them in the destructor if you want to make `MainWindow` re-instancable but usually the main window is destroyed when appl. ends. Hence, clean-up can be left to the OS.) ;-) – Scheff's Cat May 15 '18 at 14:43
  • Instead of lambdas, you might write methods for your `class MainWindow`. Since QT5, any method of a class derived from `QObject` (with matching signature) may become a signal handler. Hence, the `class MainWindow` can hold anything as member variables in your case what I carefully considered to be in the environments of my lambdas. – Scheff's Cat May 15 '18 at 14:44

1 Answers1

1

I transformed my older sample to a minimal application with GUI:

// standard C++ header:
#include <iostream>
#include <sstream>

// Qt header:
#include <QtWidgets>
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>

void log(QTextEdit &qTxtLog, const QString &qString)
{
  qTxtLog.setPlainText(qTxtLog.toPlainText() + qString);
  qTxtLog.moveCursor(QTextCursor::End);
}
void log(QTextEdit &qTxtLog, const char *text)
{
  log(qTxtLog, QString::fromUtf8(text));
}
void log(QTextEdit &qTxtLog, const std::string &text)
{
  log(qTxtLog, text.c_str());
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  // main application
  QApplication app(argc, argv);
  // setup GUI
  QWidget qWin;
  QVBoxLayout qBox;
  QFormLayout qForm;
  QLabel qLblCountry(QString::fromUtf8("Country:"));
  QLineEdit qTxtCountry;
  qForm.addRow(&qLblCountry, &qTxtCountry);
  QLabel qLblZipCode(QString::fromUtf8("Postal Code:"));
  QLineEdit qTxtZipCode;
  qForm.addRow(&qLblZipCode, &qTxtZipCode);
  QLabel qLblCity(QString::fromUtf8("City:"));
  QLineEdit qTxtCity;
  qForm.addRow(&qLblCity, &qTxtCity);
  QLabel qLblStreet(QString::fromUtf8("Street:"));
  QLineEdit qTxtStreet;
  qForm.addRow(&qLblStreet, &qTxtStreet);
  QLabel qLblProvider(QString::fromUtf8("Provider:"));
  QComboBox qLstProviders;
  qForm.addRow(&qLblProvider, &qLstProviders);
  qBox.addLayout(&qForm);
  QPushButton qBtnFind(QString::fromUtf8("Find Coordinates"));
  qBox.addWidget(&qBtnFind);
  QLabel qLblLog(QString::fromUtf8("Log:"));
  qBox.addWidget(&qLblLog);
  QTextEdit qTxtLog;
  qTxtLog.setReadOnly(true);
  qBox.addWidget(&qTxtLog);
  qWin.setLayout(&qBox);
  qWin.show();
  // initialize Geo Service Providers
  std::vector<QGeoServiceProvider*> pQGeoProviders;
  { std::ostringstream out;
    QStringList qGeoSrvList
      = QGeoServiceProvider::availableServiceProviders();
    for (QString entry : qGeoSrvList) {
      out << "Try service: " << entry.toStdString() << '\n';
      // choose provider
      QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
      if (!pQGeoProvider) {
        out
          << "ERROR: GeoServiceProvider '" << entry.toStdString()
          << "' not available!\n";
        continue;
      }
      QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
      if (!pQGeoCoder) {
        out
          << "ERROR: GeoCodingManager '" << entry.toStdString()
          << "' not available!\n";
        delete pQGeoProvider;
        continue;
      }
      QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
      pQGeoCoder->setLocale(qLocaleC);
      qLstProviders.addItem(entry);
      pQGeoProviders.push_back(pQGeoProvider);
      out << "Service " << entry.toStdString() << " available.\n";
    }
    log(qTxtLog, out.str());
  }
  if (pQGeoProviders.empty()) qBtnFind.setEnabled(false);
  // install signal handlers
  QObject::connect(&qBtnFind, QPushButton::clicked,
    [&]() {
      // get current geo service provider
      QGeoServiceProvider *pQGeoProvider
        = pQGeoProviders[qLstProviders.currentIndex()];
      // fill in request
      QGeoAddress *pQGeoAddr = new QGeoAddress;
      pQGeoAddr->setCountry(qTxtCountry.text());
      pQGeoAddr->setPostalCode(qTxtZipCode.text());
      pQGeoAddr->setCity(qTxtCity.text());
      pQGeoAddr->setStreet(qTxtStreet.text());
      QGeoCodeReply *pQGeoCode
        = pQGeoProvider->geocodingManager()->geocode(*pQGeoAddr);
      if (!pQGeoCode) {
        delete pQGeoAddr;
        log(qTxtLog, "GeoCoding totally failed!\n");
        return;
      }
      { std::ostringstream out;
        out << "Sending request for:\n"
          << pQGeoAddr->country().toUtf8().data() << "; "
          << pQGeoAddr->postalCode().toUtf8().data() << "; "
          << pQGeoAddr->city().toUtf8().data() << "; "
          << pQGeoAddr->street().toUtf8().data() << "...\n";
        log(qTxtLog, out.str());
      }
      // install signal handler to process result later
      QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
        [&qTxtLog, pQGeoAddr, pQGeoCode]() {
          // process reply
          std::ostringstream out;
          out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
          switch (pQGeoCode->error()) {
            case QGeoCodeReply::NoError: {
              // eval result
              QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
              out << qGeoLocs.size() << " location(s) returned.\n";
              for (QGeoLocation &qGeoLoc : qGeoLocs) {
                qGeoLoc.setAddress(*pQGeoAddr);
                QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
                out
                  << "Lat.:  " << qGeoCoord.latitude() << '\n'
                  << "Long.: " << qGeoCoord.longitude() << '\n'
                  << "Alt.:  " << qGeoCoord.altitude() << '\n';
              }     
            } break;
#define CASE(ERROR) \
            case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
            CASE(EngineNotSetError);
            CASE(CommunicationError);
            CASE(ParseError);
            CASE(UnsupportedOptionError);
            CASE(CombinationError);
            CASE(UnknownError);
#undef CASE
            default: out << "Undocumented error!\n";
          }
          // log result
          log(qTxtLog, out.str());
          // clean-up
          delete pQGeoAddr;
          /* delete sender in signal handler could be lethal
           * Hence, delete it later...
           */
          pQGeoCode->deleteLater();
        });
    });
  // fill in a sample request with a known address initially
  qTxtCountry.setText(QString::fromUtf8("Germany"));
  qTxtZipCode.setText(QString::fromUtf8("88250"));
  qTxtCity.setText(QString::fromUtf8("Weingarten"));
  qTxtStreet.setText(QString::fromUtf8("Danziger Str. 3"));
  // runtime loop
  app.exec();
  // done
  return 0;
}

Compiled and tested in cygwin on Windows 10 (64 bit):

$ g++ --version
g++ (GCC) 6.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ qmake-qt5 testQGeoAddressGUI.pro

$ make
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_LOCATION_LIB -DQT_QUICK_LIB -DQT_GUI_LIB -DQT_POSITIONING_LIB -DQT_QML_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtLocation -isystem /usr/include/qt5/QtQuick -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtPositioning -isystem /usr/include/qt5/QtQml -isystem /usr/include/qt5/QtNetwork -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQGeoAddressGUI.o testQGeoAddressGUI.cc
g++  -o testQGeoAddressGUI.exe testQGeoAddressGUI.o   -lQt5Widgets -lQt5Location -lQt5Quick -lQt5Gui -lQt5Positioning -lQt5Qml -lQt5Network -lQt5Core -lGL -lpthread 

$ ./testQGeoAddressGUI
Qt Version: 5.9.2

Snapshot of testQGeoAddressGUI

Notes:

When I wrote this sample I was very carefully about scope and life-time of involved variables. (Actually, I changed some local variables to pointers and instances created with new to achieve this.) This is my hint for any reader.

In my 1st test, the application ended up in CommunicationError. I'm quite sure that our company's security policy is responsible for this. (I did the same with my older sample which I tested successfully at home – with the same result.)

A 2nd test (at home) went better. First, I tried to find the address with service provider osm which brought 0 results. Changing the service provider to esri returned one result.

I copied the output to maps.google.de:

Snapshot of maps.google.de after request of 47.8187, 9.64387

This is actually the correct result – as I tested the (new) address of the company EKS InTec where I'm working.


The usage of lambdas in the above sample makes it a bit hard to read. Therefore, I re-visited the sample. Now, all relevant stuff has moved to a class MainWindow (hopefully, closer to the requirement of OP). The lambdas were replaced by simple methods.

// standard C++ header:
#include <iostream>
#include <sstream>

// Qt header:
#include <QtWidgets>
#include <QGeoAddress>
#include <QGeoCodingManager>
#include <QGeoCoordinate>
#include <QGeoLocation>
#include <QGeoServiceProvider>

// main window class
class MainWindow: public QWidget {
  // variables:
  private:
    // list of service providers
    std::vector<QGeoServiceProvider*> pQGeoProviders;
    // Qt widgets (contents of main window)
    QVBoxLayout qBox;
    QFormLayout qForm;
    QLabel qLblCountry;
    QLineEdit qTxtCountry;
    QLabel qLblZipCode;
    QLineEdit qTxtZipCode;
    QLabel qLblCity;
    QLineEdit qTxtCity;
    QLabel qLblStreet;
    QLineEdit qTxtStreet;
    QLabel qLblProvider;
    QComboBox qLstProviders;
    QPushButton qBtnFind;
    QLabel qLblLog;
    QTextEdit qTxtLog;

  // methods:
  public: // ctor/dtor
    MainWindow(QWidget *pQParent = nullptr);
    virtual ~MainWindow();
    MainWindow(const MainWindow&) = delete;
    MainWindow& operator=(const MainWindow&) = delete;

  private: // internal stuff
    void init(); // initializes geo service providers
    void find(); // sends request
    void report(); // processes reply
    void log(const QString &qString)
    {
      qTxtLog.setPlainText(qTxtLog.toPlainText() + qString);
      qTxtLog.moveCursor(QTextCursor::End);
    }
    void log(const char *text) { log(QString::fromUtf8(text)); }
    void log(const std::string &text) { log(text.c_str()); }
};

MainWindow::MainWindow(QWidget *pQParent):
  QWidget(pQParent),
  qLblCountry(QString::fromUtf8("Country:")),
  qLblZipCode(QString::fromUtf8("Postal Code:")),
  qLblCity(QString::fromUtf8("City:")),
  qLblStreet(QString::fromUtf8("Street:")),
  qLblProvider(QString::fromUtf8("Provider:")),
  qBtnFind(QString::fromUtf8("Find Coordinates")),
  qLblLog(QString::fromUtf8("Log:"))
{
  // setup child widgets
  qForm.addRow(&qLblCountry, &qTxtCountry);
  qForm.addRow(&qLblZipCode, &qTxtZipCode);
  qForm.addRow(&qLblCity, &qTxtCity);
  qForm.addRow(&qLblStreet, &qTxtStreet);
  qForm.addRow(&qLblProvider, &qLstProviders);
  qBox.addLayout(&qForm);
  qBox.addWidget(&qBtnFind);
  qBox.addWidget(&qLblLog);
  qBox.addWidget(&qTxtLog);
  setLayout(&qBox);
  // init service provider list
  init();
  // install signal handlers
  QObject::connect(&qBtnFind, &QPushButton::clicked,
    this, &MainWindow::find);
  // fill in a sample request with a known address initially
  qTxtCountry.setText(QString::fromUtf8("Germany"));
  qTxtZipCode.setText(QString::fromUtf8("88250"));
  qTxtCity.setText(QString::fromUtf8("Weingarten"));
  qTxtStreet.setText(QString::fromUtf8("Danziger Str. 3"));
}

MainWindow::~MainWindow()
{
  // clean-up
  for (QGeoServiceProvider *pQGeoProvider : pQGeoProviders) {
    delete pQGeoProvider;
  }
}

void MainWindow::init()
{
  // initialize Geo Service Providers
  { std::ostringstream out;
    QStringList qGeoSrvList
      = QGeoServiceProvider::availableServiceProviders();
    for (QString entry : qGeoSrvList) {
      out << "Try service: " << entry.toStdString() << '\n';
      // choose provider
      QGeoServiceProvider *pQGeoProvider = new QGeoServiceProvider(entry);
      if (!pQGeoProvider) {
        out
          << "ERROR: GeoServiceProvider '" << entry.toStdString()
          << "' not available!\n";
        continue;
      }
      QGeoCodingManager *pQGeoCoder = pQGeoProvider->geocodingManager();
      if (!pQGeoCoder) {
        out
          << "ERROR: GeoCodingManager '" << entry.toStdString()
          << "' not available!\n";
        delete pQGeoProvider;
        continue;
      }
      QLocale qLocaleC(QLocale::C, QLocale::AnyCountry);
      pQGeoCoder->setLocale(qLocaleC);
      qLstProviders.addItem(entry);
      pQGeoProviders.push_back(pQGeoProvider);
      out << "Service " << entry.toStdString() << " available.\n";
    }
    log(out.str());
  }
  if (pQGeoProviders.empty()) qBtnFind.setEnabled(false);
}

std::string format(const QGeoAddress &qGeoAddr)
{
  std::ostringstream out;
  out
    << qGeoAddr.country().toUtf8().data() << "; "
    << qGeoAddr.postalCode().toUtf8().data() << "; "
    << qGeoAddr.city().toUtf8().data() << "; "
    << qGeoAddr.street().toUtf8().data();
  return out.str();
}

void MainWindow::find()
{
  // get current geo service provider
  QGeoServiceProvider *pQGeoProvider
    = pQGeoProviders[qLstProviders.currentIndex()];
  // fill in request
  QGeoAddress qGeoAddr;
  qGeoAddr.setCountry(qTxtCountry.text());
  qGeoAddr.setPostalCode(qTxtZipCode.text());
  qGeoAddr.setCity(qTxtCity.text());
  qGeoAddr.setStreet(qTxtStreet.text());
  QGeoCodeReply *pQGeoCode
    = pQGeoProvider->geocodingManager()->geocode(qGeoAddr);
  if (!pQGeoCode) {
    log("GeoCoding totally failed!\n");
    return;
  }
  { std::ostringstream out;
    out << "Sending request for:\n"
      << format(qGeoAddr) << "...\n";
    log(out.str());
  }
  // install signal handler to process result later
  QObject::connect(pQGeoCode, &QGeoCodeReply::finished,
    this, &MainWindow::report);
  /* This signal handler will delete it's own sender.
   * Hence, the connection need not to be remembered
   * although it has only a limited life-time.
   */
}

void MainWindow::report()
{
  QGeoCodeReply *pQGeoCode
    = dynamic_cast<QGeoCodeReply*>(sender());
  // process reply
  std::ostringstream out;
  out << "Reply: " << pQGeoCode->errorString().toStdString() << '\n';
  switch (pQGeoCode->error()) {
    case QGeoCodeReply::NoError: {
      // eval result
      QList<QGeoLocation> qGeoLocs = pQGeoCode->locations();
      out << qGeoLocs.size() << " location(s) returned.\n";
      for (QGeoLocation &qGeoLoc : qGeoLocs) {
        QGeoAddress qGeoAddr = qGeoLoc.address();
        QGeoCoordinate qGeoCoord = qGeoLoc.coordinate();
        out
          << "Coordinates for "
          << qGeoAddr.text().toUtf8().data() << ":\n"
          << "Lat.:  " << qGeoCoord.latitude() << '\n'
          << "Long.: " << qGeoCoord.longitude() << '\n'
          << "Alt.:  " << qGeoCoord.altitude() << '\n';
      }     
    } break;
#define CASE(ERROR) \
case QGeoCodeReply::ERROR: out << #ERROR << '\n'; break
    CASE(EngineNotSetError);
    CASE(CommunicationError);
    CASE(ParseError);
    CASE(UnsupportedOptionError);
    CASE(CombinationError);
    CASE(UnknownError);
#undef CASE
    default: out << "Undocumented error!\n";
  }
  // log result
  log(out.str());
  // clean-up
  /* delete sender in signal handler could be lethal
   * Hence, delete it later...
   */
  pQGeoCode->deleteLater();
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  // main application
  QApplication app(argc, argv);
  // setup GUI
  MainWindow win;
  win.show();
  // runtime loop
  app.exec();
  // done
  return 0;
}

Note:

The look and behavior is the same like for the above example. I changed the output of reply a bit.

While preparing this sample, I realized that it is actually not necessary to set the address of the returned QGeoLocation as it is already there. IMHO, it is interesting that the returned address looks a bit different than the requested. It seems that it is returned in a (I would say) normalized form.

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • I made an update of sample. (I realized that the user's choice of service provider would make sense.) – Scheff's Cat May 15 '18 at 12:57
  • First of all thank you for this detailed example, this helped me to better understand how the connects are working. And your example works fine. But this is still running in the main.cpp. I would like to have this in a MainWindow class. But when I try to recreat this in QtCreator in the Mainwindow class I run in to Segmentation faults. I will Edit the question once more maybe you have an Idea where I am wrong. – Astraeus May 15 '18 at 13:44
  • I tried this at home - with a much better result. (answer updated) – Scheff's Cat May 15 '18 at 14:33
  • I will accept your Answer because it is a really really good example on how to do this GUI GeoCoding. Still If you get this running in a MainWindow application as mentioned would be a blessing for me. Thanks so far ! – Astraeus May 15 '18 at 14:36
  • 1
    @Astraeus I added a revised form where I moved relevant things to a `class MainWindow`. – Scheff's Cat May 16 '18 at 06:41
  • Wow thank you so much, works like a charm. I am really thankful to you . This is a great example. – Astraeus May 16 '18 at 06:44