locationqueryoverlayproxymodel.cpp 6.81 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
    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>
11
#include <KPublicTransport/RentalVehicle>
12
13

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

using namespace KOSMIndoorMap;
using namespace KPublicTransport;

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

LocationQueryOverlayProxyModel::~LocationQueryOverlayProxyModel() = default;

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

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

37
38
    beginResetModel();
    m_data = data;
39

40
    if (!m_data.isEmpty()) {
41
        m_tagKeys.name = m_data.dataSet().makeTagKey("name");
42
43
44
45
46
        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");
47
48
    }

49
50
51
52
53
    initialize();
    endResetModel();
    emit mapDataChanged();
}

54
QObject* LocationQueryOverlayProxyModel::sourceModel() const
55
56
57
58
{
    return m_sourceModel;
}

59
void LocationQueryOverlayProxyModel::setSourceModel(QObject *sourceModel)
60
61
62
63
64
{
    if (m_sourceModel == sourceModel) {
        return;
    }
    beginResetModel();
65
    m_sourceModel = qobject_cast<QAbstractItemModel*>(sourceModel);
66
67
    initialize();
    endResetModel();
68
69
70
71
72
73
74

    connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() {
        beginResetModel();
        initialize();
        endResetModel();
    });
    connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
75
        if (parent.isValid() || m_data.isEmpty()) {
76
77
78
79
80
81
82
83
84
            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) {
85
        if (parent.isValid() || m_data.isEmpty()) {
86
87
88
89
90
91
92
            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) {
93
        if (first.parent().isValid() || last.parent().isValid() || m_data.isEmpty()) {
94
95
96
97
98
99
100
            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));
    });
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
}

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:
119
            return QVariant::fromValue(OSM::Element(&m_nodes[index.row()].overlayNode));
120
121
        case LevelRole:
            return 0;
122
123
        case HiddenElementRole:
            return QVariant::fromValue(m_nodes[index.row()].sourceElement);
124
125
126
127
128
129
130
131
132
133
    }

    return {};
}

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

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

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

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

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

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
    switch (loc.type()) {
        case Location::Place:
        case Location::Stop:
            Q_UNREACHABLE();
            break;
        case Location::RentedVehicleStation:
            // try to find a matching node in the base OSM data
            for (const auto &n : m_data.dataSet().nodes) {
                if (OSM::distance(n.coordinate, info.overlayNode.coordinate) < 10 && OSM::tagValue(n, m_tagKeys.amenity) == "bicycle_rental") {
                    qDebug() << "found matching node, cloning that!" << n.url();
                    info.sourceElement = OSM::Element(&n);
                    info.overlayNode = n;
                    OSM::setTagValue(info.overlayNode, m_tagKeys.mxoid, QByteArray::number(qlonglong(n.id)));
                    break;
                }
            }

            info.overlayNode.id = m_data.dataSet().nextInternalId();
            OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
            if (loc.rentalVehicleStation().capacity() >= 0) {
                OSM::setTagValue(info.overlayNode, m_tagKeys.capacity, QByteArray::number(loc.rentalVehicleStation().capacity()));
            }
            OSM::setTagValue(info.overlayNode, m_tagKeys.realtimeAvailable, QByteArray::number(loc.rentalVehicleStation().availableVehicles()));
            if (OSM::tagValue(info.overlayNode, m_tagKeys.network).isEmpty() && !loc.rentalVehicleStation().network().name().isEmpty()) {
                OSM::setTagValue(info.overlayNode, m_tagKeys.network, loc.rentalVehicleStation().network().name().toUtf8());
            }
            break;
        case Location::RentedVehicle:
            // free floating vehicles have no matching OSM element, so no point in searching for one
            info.overlayNode.id = m_data.dataSet().nextInternalId();
            OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
            OSM::setTagValue(info.overlayNode, m_tagKeys.name, loc.name().toUtf8());
            OSM::setTagValue(info.overlayNode, m_tagKeys.realtimeAvailable, "1");
            if (OSM::tagValue(info.overlayNode, m_tagKeys.network).isEmpty() && !loc.rentalVehicle().network().name().isEmpty()) {
                OSM::setTagValue(info.overlayNode, m_tagKeys.network, loc.rentalVehicle().network().name().toUtf8());
            }
196
            break;
197
    }
198
    return info;
199
}