Commit f060c3b7 authored by Valentin Platzgummer's avatar Valentin Platzgummer

wima controller mod

parent af4b4687
......@@ -7,7 +7,6 @@
*
****************************************************************************/
/// @file
/// @author Don Gagne <don@thegagnes.com>
......@@ -19,261 +18,260 @@
const int QmlObjectListModel::ObjectRole = Qt::UserRole;
const int QmlObjectListModel::TextRole = Qt::UserRole + 1;
QmlObjectListModel::QmlObjectListModel(QObject* parent)
: QAbstractListModel(parent)
, _dirty(false)
, _skipDirtyFirstItem(false)
{
QmlObjectListModel::QmlObjectListModel(QObject *parent)
: QAbstractListModel(parent), _dirty(false), _skipDirtyFirstItem(false) {}
}
QmlObjectListModel::~QmlObjectListModel() {}
QmlObjectListModel::~QmlObjectListModel()
{
int QmlObjectListModel::rowCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return _objectList.count();
}
int QmlObjectListModel::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return _objectList.count();
QVariant QmlObjectListModel::data(const QModelIndex &index, int role) const {
if (!index.isValid()) {
return QVariant();
}
if (index.row() < 0 || index.row() >= _objectList.count()) {
return QVariant();
}
if (role == ObjectRole) {
return QVariant::fromValue(_objectList[index.row()]);
} else if (role == TextRole) {
return QVariant::fromValue(_objectList[index.row()]->objectName());
} else {
return QVariant();
}
}
QVariant QmlObjectListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() < 0 || index.row() >= _objectList.count()) {
return QVariant();
}
if (role == ObjectRole) {
return QVariant::fromValue(_objectList[index.row()]);
} else if (role == TextRole) {
return QVariant::fromValue(_objectList[index.row()]->objectName());
} else {
return QVariant();
}
QHash<int, QByteArray> QmlObjectListModel::roleNames(void) const {
QHash<int, QByteArray> hash;
hash[ObjectRole] = "object";
hash[TextRole] = "text";
return hash;
}
QHash<int, QByteArray> QmlObjectListModel::roleNames(void) const
{
QHash<int, QByteArray> hash;
hash[ObjectRole] = "object";
hash[TextRole] = "text";
return hash;
bool QmlObjectListModel::setData(const QModelIndex &index,
const QVariant &value, int role) {
if (index.isValid() && role == ObjectRole) {
_objectList.replace(index.row(), value.value<QObject *>());
emit dataChanged(index, index);
return true;
}
return false;
}
bool QmlObjectListModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (index.isValid() && role == ObjectRole) {
_objectList.replace(index.row(), value.value<QObject*>());
emit dataChanged(index, index);
return true;
}
return false;
bool QmlObjectListModel::insertRows(int position, int rows,
const QModelIndex &parent) {
Q_UNUSED(parent);
if (position < 0 || position > _objectList.count() + 1) {
qWarning() << "Invalid position position:count" << position
<< _objectList.count();
}
beginInsertRows(QModelIndex(), position, position + rows - 1);
endInsertRows();
emit countChanged(count());
return true;
}
bool QmlObjectListModel::insertRows(int position, int rows, const QModelIndex& parent)
{
Q_UNUSED(parent);
if (position < 0 || position > _objectList.count() + 1) {
qWarning() << "Invalid position position:count" << position << _objectList.count();
}
beginInsertRows(QModelIndex(), position, position + rows - 1);
endInsertRows();
emit countChanged(count());
return true;
bool QmlObjectListModel::removeRows(int position, int rows,
const QModelIndex &parent) {
Q_UNUSED(parent);
if (position < 0 || position >= _objectList.count()) {
qWarning() << "Invalid position position:count" << position
<< _objectList.count();
} else if (position + rows > _objectList.count()) {
qWarning() << "Invalid rows position:rows:count" << position << rows
<< _objectList.count();
}
beginRemoveRows(QModelIndex(), position, position + rows - 1);
for (int row = 0; row < rows; row++) {
_objectList.removeAt(position);
}
endRemoveRows();
emit countChanged(count());
return true;
}
bool QmlObjectListModel::removeRows(int position, int rows, const QModelIndex& parent)
{
Q_UNUSED(parent);
if (position < 0 || position >= _objectList.count()) {
qWarning() << "Invalid position position:count" << position << _objectList.count();
} else if (position + rows > _objectList.count()) {
qWarning() << "Invalid rows position:rows:count" << position << rows << _objectList.count();
}
beginRemoveRows(QModelIndex(), position, position + rows - 1);
for (int row=0; row<rows; row++) {
_objectList.removeAt(position);
}
endRemoveRows();
emit countChanged(count());
return true;
QObject *QmlObjectListModel::operator[](int index) {
if (index < 0 || index >= _objectList.count()) {
return NULL;
}
return _objectList[index];
}
QObject* QmlObjectListModel::operator[](int index)
{
if (index < 0 || index >= _objectList.count()) {
return NULL;
}
return _objectList[index];
const QObject *QmlObjectListModel::operator[](int index) const {
if (index < 0 || index >= _objectList.count()) {
return NULL;
}
return _objectList[index];
}
const QObject* QmlObjectListModel::operator[](int index) const
{
if (index < 0 || index >= _objectList.count()) {
return NULL;
bool QmlObjectListModel::operator==(const QmlObjectListModel &other) {
if (this->count() == other.count()) {
for (std::size_t i = 0; i < this->count(); ++i) {
if (this->get(i) != other.get(i)) {
return false;
}
}
return _objectList[index];
return true;
} else {
return false;
}
}
void QmlObjectListModel::clear()
{
while (rowCount()) {
removeAt(0);
}
bool QmlObjectListModel::operator==(const QmlObjectListModel &other) {
return !this->operator==(other);
}
QObject* QmlObjectListModel::removeAt(int i)
{
QObject* removedObject = _objectList[i];
if(removedObject) {
// Look for a dirtyChanged signal on the object
if (_objectList[i]->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || i != 0) {
QObject::disconnect(_objectList[i], SIGNAL(dirtyChanged(bool)), this, SLOT(_childDirtyChanged(bool)));
}
}
}
removeRows(i, 1);
setDirty(true);
return removedObject;
void QmlObjectListModel::clear() {
while (rowCount()) {
removeAt(0);
}
}
void QmlObjectListModel::insert(int i, QObject* object)
{
if (i < 0 || i > _objectList.count()) {
qWarning() << "Invalid index index:count" << i << _objectList.count();
}
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
QObject *QmlObjectListModel::removeAt(int i) {
QObject *removedObject = _objectList[i];
if (removedObject) {
// Look for a dirtyChanged signal on the object
if (object->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || i != 0) {
QObject::connect(object, SIGNAL(dirtyChanged(bool)), this, SLOT(_childDirtyChanged(bool)));
}
if (_objectList[i]->metaObject()->indexOfSignal(
QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || i != 0) {
QObject::disconnect(_objectList[i], SIGNAL(dirtyChanged(bool)), this,
SLOT(_childDirtyChanged(bool)));
}
}
_objectList.insert(i, object);
insertRows(i, 1);
setDirty(true);
}
removeRows(i, 1);
setDirty(true);
return removedObject;
}
void QmlObjectListModel::insert(int i, QList<QObject*> objects)
{
if (i < 0 || i > _objectList.count()) {
qWarning() << "Invalid index index:count" << i << _objectList.count();
void QmlObjectListModel::insert(int i, QObject *object) {
if (i < 0 || i > _objectList.count()) {
qWarning() << "Invalid index index:count" << i << _objectList.count();
}
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
// Look for a dirtyChanged signal on the object
if (object->metaObject()->indexOfSignal(
QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || i != 0) {
QObject::connect(object, SIGNAL(dirtyChanged(bool)), this,
SLOT(_childDirtyChanged(bool)));
}
}
int j = i;
for (QObject* object: objects) {
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
_objectList.insert(i, object);
insertRows(i, 1);
// Look for a dirtyChanged signal on the object
if (object->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || j != 0) {
QObject::connect(object, SIGNAL(dirtyChanged(bool)), this, SLOT(_childDirtyChanged(bool)));
}
}
j++;
setDirty(true);
}
void QmlObjectListModel::insert(int i, QList<QObject *> objects) {
if (i < 0 || i > _objectList.count()) {
qWarning() << "Invalid index index:count" << i << _objectList.count();
}
_objectList.insert(j, object);
int j = i;
for (QObject *object : objects) {
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
// Look for a dirtyChanged signal on the object
if (object->metaObject()->indexOfSignal(
QMetaObject::normalizedSignature("dirtyChanged(bool)")) != -1) {
if (!_skipDirtyFirstItem || j != 0) {
QObject::connect(object, SIGNAL(dirtyChanged(bool)), this,
SLOT(_childDirtyChanged(bool)));
}
}
j++;
insertRows(i, objects.count());
_objectList.insert(j, object);
}
setDirty(true);
}
insertRows(i, objects.count());
void QmlObjectListModel::append(QObject* object)
{
insert(_objectList.count(), object);
setDirty(true);
}
void QmlObjectListModel::append(QList<QObject*> objects)
{
insert(_objectList.count(), objects);
void QmlObjectListModel::append(QObject *object) {
insert(_objectList.count(), object);
}
QObjectList QmlObjectListModel::swapObjectList(const QObjectList& newlist)
{
QObjectList oldlist(_objectList);
beginResetModel();
_objectList = newlist;
endResetModel();
emit countChanged(count());
return oldlist;
void QmlObjectListModel::append(QList<QObject *> objects) {
insert(_objectList.count(), objects);
}
int QmlObjectListModel::count() const
{
return rowCount();
QObjectList QmlObjectListModel::swapObjectList(const QObjectList &newlist) {
QObjectList oldlist(_objectList);
beginResetModel();
_objectList = newlist;
endResetModel();
emit countChanged(count());
return oldlist;
}
void QmlObjectListModel::setDirty(bool dirty)
{
if (_dirty != dirty) {
_dirty = dirty;
if (!dirty) {
// Need to clear dirty from all children
for(QObject* object: _objectList) {
if (object->property("dirty").isValid()) {
object->setProperty("dirty", false);
}
}
int QmlObjectListModel::count() const { return rowCount(); }
void QmlObjectListModel::setDirty(bool dirty) {
if (_dirty != dirty) {
_dirty = dirty;
if (!dirty) {
// Need to clear dirty from all children
for (QObject *object : _objectList) {
if (object->property("dirty").isValid()) {
object->setProperty("dirty", false);
}
emit dirtyChanged(_dirty);
}
}
emit dirtyChanged(_dirty);
}
}
void QmlObjectListModel::_childDirtyChanged(bool dirty)
{
_dirty |= dirty;
// We want to emit dirtyChanged even if the actual value of _dirty didn't change. It can be a useful
// signal to know when a child has changed dirty state
emit dirtyChanged(_dirty);
void QmlObjectListModel::_childDirtyChanged(bool dirty) {
_dirty |= dirty;
// We want to emit dirtyChanged even if the actual value of _dirty didn't
// change. It can be a useful signal to know when a child has changed dirty
// state
emit dirtyChanged(_dirty);
}
void QmlObjectListModel::deleteListAndContents()
{
for (int i=0; i<_objectList.count(); i++) {
_objectList[i]->deleteLater();
}
deleteLater();
void QmlObjectListModel::deleteListAndContents() {
for (int i = 0; i < _objectList.count(); i++) {
_objectList[i]->deleteLater();
}
deleteLater();
}
void QmlObjectListModel::clearAndDeleteContents()
{
beginResetModel();
for (int i=0; i<_objectList.count(); i++) {
_objectList[i]->deleteLater();
}
clear();
endResetModel();
void QmlObjectListModel::clearAndDeleteContents() {
beginResetModel();
for (int i = 0; i < _objectList.count(); i++) {
_objectList[i]->deleteLater();
}
clear();
endResetModel();
}
void swap(QmlObjectListModel &list1, QmlObjectListModel &list2)
{
using std::swap;
void swap(QmlObjectListModel &list1, QmlObjectListModel &list2) {
using std::swap;
swap(list1._objectList, list2._objectList);
swap(list1._dirty, list2._dirty);
swap(list1._skipDirtyFirstItem, list2._skipDirtyFirstItem);
swap(list1._objectList, list2._objectList);
swap(list1._dirty, list2._dirty);
swap(list1._skipDirtyFirstItem, list2._skipDirtyFirstItem);
}
......@@ -51,6 +51,9 @@ public:
}
QList<QObject *> *objectList() { return &_objectList; }
bool operator==(const QmlObjectListModel &other);
bool operator!=(const QmlObjectListModel &other);
/// Calls deleteLater on all items and this itself.
void deleteListAndContents();
......
......@@ -9,6 +9,38 @@
#define SNAKE_MAX_TILES 1000
#endif
TileData::TileData() : tiles(this) {}
TileData::~TileData() { tiles.clearAndDeleteContents(); }
TileData &TileData::operator=(const TileData &other) {
this->tiles.clearAndDeleteContents();
for (std::size_t i = 0; i < std::size_t(other.tiles.count()); ++i) {
const auto *obj = other.tiles.get(i);
const auto *tile = qobject_cast<const SnakeTile *>(obj);
if (tile != nullptr) {
this->tiles.append(new SnakeTile(*tile, this));
} else {
qWarning("TileData::operator=: nullptr");
}
}
this->tileCenterPoints = other.tileCenterPoints;
return *this;
}
void TileData::clear() {
this->tiles.clearAndDeleteContents();
this->tileCenterPoints.clear();
}
size_t TileData::size() const {
if (tiles.count() == tileCenterPoints.size()) {
return tiles.count();
} else {
return 0;
}
}
const char *WimaMeasurementArea::settingsGroup = "MeasurementArea";
const char *WimaMeasurementArea::tileHeightName = "TileHeight";
const char *WimaMeasurementArea::tileWidthName = "TileWidth";
......@@ -39,8 +71,7 @@ WimaMeasurementArea::WimaMeasurementArea(QObject *parent)
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
this /* QObject parent */)),
_pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false),
_polygonValid(false) {
_calculating(false) {
init();
}
......@@ -64,8 +95,7 @@ WimaMeasurementArea::WimaMeasurementArea(const WimaMeasurementArea &other,
this /* QObject parent */)),
_showTiles(SettingsFact(settingsGroup, _metaDataMap[showTilesName],
this /* QObject parent */)),
_pTiles(new QmlObjectListModel(), &tileDeleter), _calculating(false),
_polygonValid(false) {
_calculating(false) {
init();
}
......@@ -81,9 +111,7 @@ operator=(const WimaMeasurementArea &other) {
return *this;
}
WimaMeasurementArea::~WimaMeasurementArea() {
this->_pTiles->clearAndDeleteContents();
}
WimaMeasurementArea::~WimaMeasurementArea() {}
QString WimaMeasurementArea::mapVisualQML() const {
return "WimaMeasurementAreaMapVisual.qml";
......@@ -105,10 +133,20 @@ Fact *WimaMeasurementArea::minTransectLength() { return &_minTransectLength; }
Fact *WimaMeasurementArea::showTiles() { return &_showTiles; }
QmlObjectListModel *WimaMeasurementArea::tiles() { return this->_pTiles.get(); }
QmlObjectListModel *WimaMeasurementArea::tiles() {
return &this->_tileData.tiles;
}
const QmlObjectListModel *WimaMeasurementArea::tiles() const {
return this->_pTiles.get();
return &this->_tileData.tiles;
}
const QVariantList &WimaMeasurementArea::tileCenterPoints() const {
return this->_tileData.tileCenterPoints;
}
const TileData &WimaMeasurementArea::tileData() const {
return this->_tileData;
}
int WimaMeasurementArea::maxTiles() const { return SNAKE_MAX_TILES; }
......@@ -181,8 +219,8 @@ bool WimaMeasurementArea::loadFromJson(const QJsonObject &json,
}
//!
//! \brief WimaMeasurementArea::doUpdate
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call this
//! function directly!
//! \pre WimaMeasurementArea::deferUpdate must be called first, don't call
//! this function directly!
void WimaMeasurementArea::doUpdate() {
using namespace snake;
using namespace boost::units;
......@@ -198,14 +236,10 @@ void WimaMeasurementArea::doUpdate() {
long(std::ceil(estNumTiles.value())) <= SNAKE_MAX_TILES &&
this->count() >= 3 && this->isSimplePolygon()) {
this->_calculating = true;
if (!this->_polygonValid) {
this->_polygon = this->coordinateList();
for (auto &v : this->_polygon) {
v.setAltitude(0);
}
this->_polygonValid = true;
auto polygon = this->coordinateList();
for (auto &v : polygon) {
v.setAltitude(0);
}
const auto &polygon = this->_polygon;
const auto minArea =
this->_minTileArea.rawValue().toDouble() * si::meter * si::meter;
auto *th = this->thread();
......@@ -213,35 +247,44 @@ void WimaMeasurementArea::doUpdate() {
#ifdef SNAKE_SHOW_TIME
auto start = std::chrono::high_resolution_clock::now();
#endif
TilesPtr pTiles(new QmlObjectListModel(), &tileDeleter);
DataPtr pData(new TileData());
// Convert to ENU system.
QGeoCoordinate origin = polygon.first();
BoostPolygon polygonENU;
areaToEnu(origin, polygon, polygonENU);
std::vector<BoostPolygon> tilesENU;
BoundingBox bbox;
std::string errorString;
// Generate tiles.
if (snake::tiles(polygonENU, height, width, minArea, tilesENU, bbox,
errorString)) {
// Convert to geo system.
for (const auto &t : tilesENU) {
auto geoTile = new SnakeTile(pTiles.get());
auto geoTile = new SnakeTile(pData.get());
for (const auto &v : t.outer()) {
QGeoCoordinate geoVertex;
fromENU(origin, v, geoVertex);
geoTile->push_back(geoVertex);
}
pTiles->append(geoTile);
pData->tiles.append(geoTile);
// Calculate center.
snake::BoostPoint center;
snake::polygonCenter(t, center);
QGeoCoordinate geoCenter;
fromENU(origin, center, geoCenter);
pData->tileCenterPoints.append(QVariant::fromValue(geoCenter));
}
}
pTiles->moveToThread(th);
pData->moveToThread(th);
#ifdef SNAKE_SHOW_TIME
qDebug()
<< "WimaMeasurementArea::doUpdate concurrent update execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count()
<< " ms";
qDebug() << "WimaMeasurementArea::doUpdate concurrent update execution "
"time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count()
<< " ms";
#endif
return pTiles;
return pData;
}); // QtConcurrent::run()
this->_watcher.setFuture(future);
......@@ -259,8 +302,8 @@ void WimaMeasurementArea::deferUpdate() {
if (this->_timer.isActive()) {
this->_timer.stop();
}
if (this->_pTiles->count() > 0) {
this->_pTiles->clearAndDeleteContents();
if (this->_tileData.size() > 0) {
this->_tileData.clear();
emit this->tilesChanged();
}
this->_timer.start(100);
......@@ -270,10 +313,10 @@ void WimaMeasurementArea::storeTiles() {
#ifdef SNAKE_SHOW_TIME
auto start = std::chrono::high_resolution_clock::now();
#endif
this->_pTiles = this->_watcher.result();
this->_tileData = *this->_watcher.result();
this->_calculating = false;
emit this
->tilesChanged(); // This is expensive. Drawing tiles is expensive too.
// This is expensive. Drawing tiles is expensive too.
emit this->tilesChanged();
#ifdef SNAKE_SHOW_TIME
qDebug() << "WimaMeasurementArea::storeTiles() execution time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
......@@ -293,8 +336,6 @@ void WimaMeasurementArea::init() {
&WimaMeasurementArea::deferUpdate);
connect(this, &WimaArea::pathChanged, this,
&WimaMeasurementArea::deferUpdate);
connect(this, &WimaArea::pathChanged,
[this] { this->_polygonValid = false; });
this->_timer.setSingleShot(true);
connect(&this->_timer, &QTimer::timeout, this,
&WimaMeasurementArea::doUpdate);
......
......@@ -8,6 +8,20 @@
#include "SettingsFact.h"
class TileData : public QObject {
public:
QmlObjectListModel tiles;
QVariantList tileCenterPoints;
TileData();
~TileData();
TileData &operator=(const TileData &other);
void clear();
std::size_t size() const;