Commit 4b04b829 authored by Volker Krause's avatar Volker Krause
Browse files

Expand the bike rental integration

The model now properly updates, and merges its content with the OSM base
data where possible.
parent daa6195f
......@@ -10,6 +10,7 @@
#include <KPublicTransport/LocationQueryModel>
#include <osm/element.h>
#include <osm/geomath.h>
using namespace KOSMIndoorMap;
using namespace KPublicTransport;
......@@ -31,6 +32,14 @@ void LocationQueryOverlayProxyModel::setMapData(MapData* data)
// ### do not check for m_data != data, this does not actually change!
beginResetModel();
m_data = data;
if (m_data) {
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");
}
initialize();
endResetModel();
emit mapDataChanged();
......@@ -49,8 +58,40 @@ void LocationQueryOverlayProxyModel::setSourceModel(QAbstractItemModel *sourceMo
beginResetModel();
m_sourceModel = sourceModel;
initialize();
// TODO watch for changes
endResetModel();
connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() {
beginResetModel();
initialize();
endResetModel();
});
connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
if (parent.isValid() || !m_data) {
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) {
if (parent.isValid() || !m_data) {
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) {
if (first.parent().isValid() || last.parent().isValid() || !m_data) {
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));
});
}
int LocationQueryOverlayProxyModel::rowCount(const QModelIndex &parent) const
......@@ -87,29 +128,41 @@ QHash<int, QByteArray> LocationQueryOverlayProxyModel::roleNames() const
void LocationQueryOverlayProxyModel::initialize()
{
static OSM::Id nextId = -1;
if (!m_data || !m_sourceModel) {
return;
}
const auto overlayTag = m_data->dataSet().makeTagKey("mx:overlay");
const auto capacityTag = m_data->dataSet().makeTagKey("capacity");
const auto availableTag = m_data->dataSet().makeTagKey("available");
m_nodes.clear();
const auto rows = m_sourceModel->rowCount();
m_nodes.reserve(rows);
for (int i = 0; i < rows; ++i) {
const auto idx = m_sourceModel->index(i, 0);
const auto loc = idx.data(LocationQueryModel::LocationRole).value<Location>();
m_nodes.push_back(nodeForRow(i));
}
}
OSM::Node node;
node.id = --nextId;
node.coordinate = OSM::Coordinate(loc.latitude(), loc.longitude());
OSM::setTagValue(node, overlayTag, "bike_rental");
OSM::setTagValue(node, capacityTag, QByteArray::number(loc.rentalVehicleStation().capacity()));
OSM::setTagValue(node, availableTag, QByteArray::number(loc.rentalVehicleStation().availableVehicles()));
OSM::Node LocationQueryOverlayProxyModel::nodeForRow(int row) const
{
const auto idx = m_sourceModel->index(row, 0);
const auto loc = idx.data(LocationQueryModel::LocationRole).value<Location>();
OSM::Node node;
node.coordinate = OSM::Coordinate(loc.latitude(), loc.longitude());
// try to find a matching node in the base OSM data
for (const auto &n : m_data->dataSet().nodes) {
if (OSM::distance(n.coordinate, node.coordinate) < 10 && OSM::tagValue(n, m_tagKeys.amenity) == "bicycle_rental") {
qDebug() << "found matching node, cloning that!" << n.url();
node = n;
break;
}
}
m_nodes.push_back(std::move(node));
node.id = m_data->dataSet().nextInternalId();
OSM::setTagValue(node, m_tagKeys.amenity, "bicycle_rental");
OSM::setTagValue(node, m_tagKeys.capacity, QByteArray::number(loc.rentalVehicleStation().capacity()));
OSM::setTagValue(node, m_tagKeys.realtimeAvailable, QByteArray::number(loc.rentalVehicleStation().availableVehicles()));
if (OSM::tagValue(node, m_tagKeys.network).isEmpty()) {
OSM::setTagValue(node, m_tagKeys.network, loc.rentalVehicleStation().network().name().toUtf8());
}
return node;
}
......@@ -52,6 +52,14 @@ Q_SIGNALS:
private:
void initialize();
OSM::Node nodeForRow(int row) const;
struct {
OSM::TagKey amenity;
OSM::TagKey capacity;
OSM::TagKey realtimeAvailable;
OSM::TagKey network;
} m_tagKeys;
std::vector<OSM::Node> m_nodes;
MapData *m_data = nullptr;
......
......@@ -201,6 +201,7 @@ void MapItem::setOverlaySources(const QVariant &overlays)
m_controller.setOverlaySources(std::move(sources));
emit overlaySourcesChanged();
update();
}
void MapItem::addOverlaySource(std::vector<OverlaySource> &overlaySources, const QVariant &source)
......
......@@ -62,7 +62,24 @@ area|z17-[amenity=parking][access=private]
*|z18-[amenity=bicycle_parking]
{
icon-image: "bicycle_parking";
text-color: #3daee9;
icon-color: #3daee9;
}
// bicycle rental
node|z18-[amenity=bicycle_rental] {
icon-color: #3daee9;
icon-image: "bicycle_rental";
}
node|z18-[amenity=bicycle_rental][mx:realtime_available=0] {
icon-color: #da4453;
text: mx:realtime_available;
text-color: #da4453;
text-offset: 9;
}
node|z18-[amenity=bicycle_rental][mx:realtime_available>0] {
icon-color: #27ae60;
text: mx:realtime_available;
text-color: #27ae60;
text-offset: 9;
}
/** Roads */
......@@ -339,7 +356,7 @@ node|z18-[public_transport=platform_section_sign]
/** Shops/etc */
*|z20- [shop],
*|z19- [shop=ticket],
*|z21- [amenity],
*|z21- [amenity][name],
*|z20- [amenity=cafe],
*|z20- [amenity=car_rental],
*|z20- [amenity=fast_food],
......@@ -380,14 +397,3 @@ node|z18-[amenity=atm] { text: "🏧"; }
// hide information boards as their names clutter the vie
// showing an icon instead would be better, but we don't have one yet
node[information=board] { text: ""; }
// bicycle rental live data (WIP, this should be matched against regular bicycle_rental nodes)
node|z18-[mx:overlay=bike_rental] {
text: available;
text-color: #27ae60;
icon-image: "bicycle_rental";
}
node|z18-[mx:overlay=bike_rental][available=0] {
text-color: #da4453;
}
......@@ -109,6 +109,12 @@ void DataSet::addRelation(Relation &&rel)
relations.insert(it, std::move(rel));
}
OSM::Id DataSet::nextInternalId() const
{
static OSM::Id nextId = 0;
return --nextId;
}
// resolve ids for elements split in Marble vector tiles
template <typename T>
static QString actualIdString(const T &elem)
......
......@@ -346,6 +346,9 @@ public:
*/
Role makeRole(const char *roleName, StringMemory memOpt = StringIsTransient);
/** Create a unique id for internal use (ie. one that will not clash with official OSM ids). */
Id nextInternalId() const;
std::vector<Node> nodes;
std::vector<Way> ways;
std::vector<Relation> relations;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment