Marble/Runners/VehicleTracking

Search
 Tutorial Series Marble C++ Tutorial Previous Tutorial 12 - Displaying Places the proper way (via GeoDataDocuments) What's Next Tutorial 14 - Yet missing Further Reading n/a

We want to present you another C++ example of Marble Runnable usage, here on KDE TechBase. It's about the cars. Imagine that there are two completely drunk car drivers. They are driving around ukranian city Kiev (EURO-2012). They are driving circles around it with set radius and speed. At the end of tutorial you will have an application with smth like:

Let's think about implementation a bit:

1. Init MarbleWidget
2. Implement placemarks for cars "Bus" and "Car"
3. Change their coordinates using trigonometry functions
4. Apply new coordinates

We need to change coordinates with really small interval (~10 ms). In this way, to not block GUI thread we need to use multi-threading. For that we suggest that model:

2. Create CarWorker #1 (= worker for cars' coordinates)
3. Move CarWorker #1 into thread #1
4. The same for Car #2 (1-3)

Then we will have three threads - GUI, Calculations #1, Calculations #2. To connect calc. threads with GUI we should use Qt S/S (Signal/Slot) connection.

Important
Use here connection type Qt::BlockingQueuedConnection, another way GUI thread will be blocked

Now we have a nice implementation plan. Let's start coding.

1. main() creates an object of the Window (our main QWidget-based window)
2. Window creates the MarbleWidget and sets two GeoDataPlacemarks there
3. main() calls Window::startCars()
5. Window creates two CarWorkers
6. Window moves car workers into their threads

Then each CarWorker creates a QTimer with interval x. On timeout() it calls the CarWorker::iterate() - function to calculate new position of placemark and notify Window (another thread) about it.

Everything is going on until the application finishes.

```//
// This file is part of the Marble Virtual Globe.
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com>
// Copyright 2012 Torsten Rahn      <tackat@kde.org>
//

#include <QtGui/QApplication>
#include <QtCore/QTimer>
#include <QtCore/QHash>
#include <QtCore/qmath.h>
#include <QtCore/QDebug>
#include <QtGui/QVBoxLayout>

#include <marble/MarbleWidget.h>
#include <marble/MarbleGlobal.h>
#include <marble/GeoDataPlacemark.h>
#include <marble/GeoDataLineString.h>
#include <marble/GeoDataTreeModel.h>
#include <marble/MarbleModel.h>

using namespace Marble;

// CarWorker Class
class CarWorker : public QObject
{
Q_OBJECT
public:
CarWorker(const GeoDataCoordinates& city, qreal radius, qreal speed);

signals:
/// This signal will be emitted when we need to
/// move the placemark.
void coordinatesChanged(GeoDataCoordinates coord);

public slots:
void startWork();
void finishWork();

private slots:
void iterate();

private:
QTimer *m_timer;
GeoDataCoordinates m_city;
qreal m_speed;
qreal m_alpha;
};

CarWorker::CarWorker(const GeoDataCoordinates &city, qreal radius, qreal speed) :
QObject(),
m_timer(new QTimer(this)),
m_city(city),
m_speed(speed),
m_alpha(0.0)
{}

void CarWorker::startWork()
{
m_timer->setInterval(0);
connect(m_timer, SIGNAL(timeout()), this, SLOT(iterate()));
m_timer->start();
}

/// Single timer loop iteration:
/// Calculates new coordinates for the car
void CarWorker::iterate()
{
/// A bit of math

GeoDataCoordinates coord(lon, lat, 0.0, GeoDataCoordinates::Degree);
emit coordinatesChanged(coord);

/// Iteration is finished. We need to increase angle
m_alpha += m_speed;
}

void CarWorker::finishWork()
{
m_timer->stop();
}

// Window Class
class Window : public QWidget
{
Q_OBJECT
public:
Window(QWidget *parent = 0);
void startCars();

public slots:
void setCarCoordinates(const GeoDataCoordinates &coord);

private:
MarbleWidget *m_marbleWidget;
CarWorker *m_firstWorker;
CarWorker *m_secondWorker;
GeoDataPlacemark *m_carFirst;
GeoDataPlacemark *m_carSecond;
};

Window::Window(QWidget *parent) :
QWidget(parent),
m_marbleWidget(new MarbleWidget)
{
QVBoxLayout *layout = new QVBoxLayout(this);
setLayout(layout);

m_marbleWidget->setMapThemeId("earth/openstreetmap/openstreetmap.dgml");
m_marbleWidget->setProjection( Mercator );
setGeometry(80, 60, 1000, 800);
GeoDataCoordinates Kiev(30.523333, 50.45, 0.0, GeoDataCoordinates::Degree);
m_marbleWidget->centerOn(Kiev);
m_marbleWidget->setZoom(2300);

m_carFirst = new GeoDataPlacemark("Bus");
m_carSecond = new GeoDataPlacemark("Car");

document->append(m_carFirst);
document->append(m_carSecond);

show();
}

void Window::startCars()
{
GeoDataCoordinates Kiev(30.523333, 50.45, 0.0, GeoDataCoordinates::Degree);

m_firstWorker = new CarWorker(Kiev, (qreal)0.1, (qreal)0.7);

connect(m_firstWorker, SIGNAL(coordinatesChanged(GeoDataCoordinates)),
this, SLOT(setCarCoordinates(GeoDataCoordinates)), Qt::BlockingQueuedConnection);

m_secondWorker = new CarWorker(Kiev, (qreal)0.2, (qreal)-0.5);

connect(m_secondWorker, SIGNAL(coordinatesChanged(GeoDataCoordinates)),
this, SLOT(setCarCoordinates(GeoDataCoordinates)), Qt::BlockingQueuedConnection);

}

void Window::setCarCoordinates(const GeoDataCoordinates &coord)
{
CarWorker *worker = qobject_cast<CarWorker*>(sender());
if (worker == m_firstWorker) {
m_carFirst->setCoordinate(coord);
} else if (worker == m_secondWorker) {
m_carSecond->setCoordinate(coord);
m_marbleWidget->model()->treeModel()->updateFeature(m_carSecond);
}
}

// Main
int main(int argc, char** argv)
{
QApplication app(argc,argv);

Window window;
window.startCars();

return app.exec();
}

#include "vehicletracking.moc"
```

Find your way and explore the world! ;)

Content is available under Creative Commons License SA 4.0 unless otherwise noted.