locationqueryoverlayproxymodel.cpp 5.57 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/*
    SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "locationqueryoverlayproxymodel.h"

#include <KPublicTransport/Location>
#include <KPublicTransport/LocationQueryModel>

#include <osm/element.h>
13
#include <osm/geomath.h>
14
15
16
17
18
19
20
21
22
23
24

using namespace KOSMIndoorMap;
using namespace KPublicTransport;

LocationQueryOverlayProxyModel::LocationQueryOverlayProxyModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

LocationQueryOverlayProxyModel::~LocationQueryOverlayProxyModel() = default;

25
MapData LocationQueryOverlayProxyModel::mapData() const
26
27
28
29
{
    return m_data;
}

30
void LocationQueryOverlayProxyModel::setMapData(const MapData &data)
31
{
32
33
34
35
    if (m_data == data) {
        return;
    }

36
37
    beginResetModel();
    m_data = data;
38

39
40
41
42
43
44
    if (!m_data.isEmpty()) {
        m_tagKeys.amenity = m_data.dataSet().makeTagKey("amenity");
        m_tagKeys.capacity = m_data.dataSet().makeTagKey("capacity");
        m_tagKeys.realtimeAvailable = m_data.dataSet().makeTagKey("mx:realtime_available");
        m_tagKeys.network = m_data.dataSet().makeTagKey("network");
        m_tagKeys.mxoid = m_data.dataSet().makeTagKey("mx:oid");
45
46
    }

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
    initialize();
    endResetModel();
    emit mapDataChanged();
}

QAbstractItemModel* LocationQueryOverlayProxyModel::sourceModel() const
{
    return m_sourceModel;
}

void LocationQueryOverlayProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
    if (m_sourceModel == sourceModel) {
        return;
    }
    beginResetModel();
    m_sourceModel = sourceModel;
    initialize();
    endResetModel();
66
67
68
69
70
71
72

    connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() {
        beginResetModel();
        initialize();
        endResetModel();
    });
    connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
73
        if (parent.isValid() || m_data.isEmpty()) {
74
75
76
77
78
79
80
81
82
            return;
        }
        beginInsertRows({}, first, last);
        for (int i = first; i <= last; ++i) {
            m_nodes.insert(m_nodes.begin() + i, nodeForRow(i));
        }
        endInsertRows();
    });
    connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
83
        if (parent.isValid() || m_data.isEmpty()) {
84
85
86
87
88
89
90
            return;
        }
        beginRemoveRows({}, first, last);
        m_nodes.erase(m_nodes.begin() + first, m_nodes.begin() + last);
        endRemoveRows();
    });
    connect(m_sourceModel, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &first, const QModelIndex &last) {
91
        if (first.parent().isValid() || last.parent().isValid() || m_data.isEmpty()) {
92
93
94
95
96
97
98
            return;
        }
        for (int i = first.row(); i <= last.row(); ++i) {
            m_nodes[i] = nodeForRow(i);
        }
        emit dataChanged(index(first.row(), 0), index(last.row(), 0));
    });
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
}

int LocationQueryOverlayProxyModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return m_nodes.size();
}

QVariant LocationQueryOverlayProxyModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()) {
        return {};
    }

    switch (role) {
        case ElementRole:
117
            return QVariant::fromValue(OSM::Element(&m_nodes[index.row()].overlayNode));
118
119
        case LevelRole:
            return 0;
120
121
        case HiddenElementRole:
            return QVariant::fromValue(m_nodes[index.row()].sourceElement);
122
123
124
125
126
127
128
129
130
131
    }

    return {};
}

QHash<int, QByteArray> LocationQueryOverlayProxyModel::roleNames() const
{
    auto n = QAbstractListModel::roleNames();
    n.insert(ElementRole, "osmElement");
    n.insert(LevelRole, "level");
132
    n.insert(HiddenElementRole, "hiddenElement");
133
134
135
136
137
    return n;
}

void LocationQueryOverlayProxyModel::initialize()
{
138
    if (m_data.isEmpty() || !m_sourceModel) {
139
140
141
142
143
144
145
        return;
    }

    m_nodes.clear();
    const auto rows = m_sourceModel->rowCount();
    m_nodes.reserve(rows);
    for (int i = 0; i < rows; ++i) {
146
147
148
        m_nodes.push_back(nodeForRow(i));
    }
}
149

150
LocationQueryOverlayProxyModel::Info LocationQueryOverlayProxyModel::nodeForRow(int row) const
151
152
153
154
{
    const auto idx = m_sourceModel->index(row, 0);
    const auto loc = idx.data(LocationQueryModel::LocationRole).value<Location>();

155
156
    Info info;
    info.overlayNode.coordinate = OSM::Coordinate(loc.latitude(), loc.longitude());
157
158

    // try to find a matching node in the base OSM data
159
    for (const auto &n : m_data.dataSet().nodes) {
160
        if (OSM::distance(n.coordinate, info.overlayNode.coordinate) < 10 && OSM::tagValue(n, m_tagKeys.amenity) == "bicycle_rental") {
161
            qDebug() << "found matching node, cloning that!" << n.url();
162
163
164
            info.sourceElement = OSM::Element(&n);
            info.overlayNode = n;
            OSM::setTagValue(info.overlayNode, m_tagKeys.mxoid, QByteArray::number(qlonglong(n.id)));
165
166
167
            break;
        }
    }
168

169
    info.overlayNode.id = m_data.dataSet().nextInternalId();
170
    OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
171
    if (loc.rentalVehicleStation().capacity() >= 0) {
172
        OSM::setTagValue(info.overlayNode, m_tagKeys.capacity, QByteArray::number(loc.rentalVehicleStation().capacity()));
173
    }
174
175
176
    OSM::setTagValue(info.overlayNode, m_tagKeys.realtimeAvailable, QByteArray::number(loc.rentalVehicleStation().availableVehicles()));
    if (OSM::tagValue(info.overlayNode, m_tagKeys.network).isEmpty()) {
        OSM::setTagValue(info.overlayNode, m_tagKeys.network, loc.rentalVehicleStation().network().name().toUtf8());
177
    }
178
    return info;
179
}