Commit d1b884b2 authored by Volker Krause's avatar Volker Krause
Browse files

Allow overlay elements to hide source elements

This avoids drawing things twice, and having to work around overlapping
issues when things change in size or position.
parent 1214d9a4
......@@ -38,6 +38,7 @@ void LocationQueryOverlayProxyModel::setMapData(MapData* data)
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");
}
initialize();
......@@ -110,9 +111,11 @@ QVariant LocationQueryOverlayProxyModel::data(const QModelIndex &index, int role
switch (role) {
case ElementRole:
return QVariant::fromValue(OSM::Element(&m_nodes[index.row()]));
return QVariant::fromValue(OSM::Element(&m_nodes[index.row()].overlayNode));
case LevelRole:
return 0;
case HiddenElementRole:
return QVariant::fromValue(m_nodes[index.row()].sourceElement);
}
return {};
......@@ -123,6 +126,7 @@ QHash<int, QByteArray> LocationQueryOverlayProxyModel::roleNames() const
auto n = QAbstractListModel::roleNames();
n.insert(ElementRole, "osmElement");
n.insert(LevelRole, "level");
n.insert(HiddenElementRole, "hiddenElement");
return n;
}
......@@ -140,31 +144,33 @@ void LocationQueryOverlayProxyModel::initialize()
}
}
OSM::Node LocationQueryOverlayProxyModel::nodeForRow(int row) const
LocationQueryOverlayProxyModel::Info 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());
Info info;
info.overlayNode.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") {
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();
node = n;
info.sourceElement = OSM::Element(&n);
info.overlayNode = n;
OSM::setTagValue(info.overlayNode, m_tagKeys.mxoid, QByteArray::number(qlonglong(n.id)));
break;
}
}
node.id = m_data->dataSet().nextInternalId();
OSM::setTagValue(node, m_tagKeys.amenity, "bicycle_rental");
info.overlayNode.id = m_data->dataSet().nextInternalId();
OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
if (loc.rentalVehicleStation().capacity() >= 0) {
OSM::setTagValue(node, m_tagKeys.capacity, QByteArray::number(loc.rentalVehicleStation().capacity()));
OSM::setTagValue(info.overlayNode, 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());
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());
}
return node;
return info;
}
......@@ -39,6 +39,7 @@ public:
enum Role {
ElementRole = Qt::UserRole,
LevelRole,
HiddenElementRole,
};
Q_ENUM(Role)
......@@ -51,17 +52,23 @@ Q_SIGNALS:
void sourceModelChanged();
private:
struct Info {
OSM::Node overlayNode;
OSM::Element sourceElement;
};
void initialize();
OSM::Node nodeForRow(int row) const;
Info nodeForRow(int row) const;
struct {
OSM::TagKey amenity;
OSM::TagKey capacity;
OSM::TagKey realtimeAvailable;
OSM::TagKey network;
OSM::TagKey mxoid;
} m_tagKeys;
std::vector<OSM::Node> m_nodes;
std::vector<Info> m_nodes;
MapData *m_data = nullptr;
QAbstractItemModel *m_sourceModel = nullptr;
};
......
......@@ -387,13 +387,11 @@ 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;
}
// charging stations
......
......@@ -19,6 +19,8 @@ OverlaySource::OverlaySource(QAbstractItemModel *model)
m_elementRole = it.key();
} else if (it.value() == "level") {
m_floorRole = it.key();
} else if (it.value() == "hiddenElement") {
m_hiddenElementRole = it.key();
}
}
if (m_elementRole < 0 || m_floorRole < 0) {
......@@ -66,3 +68,19 @@ void OverlaySource::forEach(int floorLevel, const std::function<void (OSM::Eleme
func(elem, floor);
}
}
void OverlaySource::hiddenElements(std::vector<OSM::Element> &elems) const
{
if (!m_model || m_hiddenElementRole < 0) {
return;
}
const auto rows = m_model->rowCount();
for (int i = 0; i < rows; ++i) {
const auto idx = m_model->index(i, 0);
const auto elem = idx.data(m_hiddenElementRole).value<OSM::Element>();
if (elem.type() != OSM::Type::Null) {
elems.push_back(elem);
}
}
}
......@@ -39,10 +39,14 @@ public:
/** Iteration interface with floor level filtering. */
void forEach(int floorLevel, const std::function<void(OSM::Element, int)> &func) const;
/** Adds hidden elements to @param elems. */
void hiddenElements(std::vector<OSM::Element> &elems) const;
private:
QPointer<QAbstractItemModel> m_model;
int m_elementRole = -1;
int m_floorRole = -1;
int m_hiddenElementRole = -1;
};
}
......
......@@ -115,11 +115,18 @@ void SceneController::updateScene(SceneGraph &sg) const
}
}
// collect elements that the overlay want to hide
m_hiddenElements.clear();
for (const auto &overlaySource : m_overlaySources) {
overlaySource.hiddenElements(m_hiddenElements);
}
std::sort(m_hiddenElements.begin(), m_hiddenElements.end());
// for each level, update or create scene graph elements, after a some basic bounding box check
const auto geoBbox = m_view->mapSceneToGeo(m_view->sceneBoundingBox());
for (auto it = beginIt; it != endIt; ++it) {
for (auto e : (*it).second) {
if (OSM::intersects(geoBbox, e.boundingBox())) {
if (OSM::intersects(geoBbox, e.boundingBox()) && !std::binary_search(m_hiddenElements.begin(), m_hiddenElements.end(), e)) {
updateElement(e, (*it).first.numericLevel(), sg);
}
}
......
......@@ -72,6 +72,7 @@ private:
const MapCSSStyle *m_styleSheet = nullptr;
const View *m_view = nullptr;
std::vector<OverlaySource> m_overlaySources;
mutable std::vector<OSM::Element> m_hiddenElements;
mutable MapCSSResult m_styleResult;
mutable QColor m_defaultTextColor;
......
......@@ -26,6 +26,7 @@ public:
inline Element(const Relation *relation) : m_elem(relation, static_cast<uint8_t>(Type::Relation)) {}
inline bool operator==(Element other) const { return m_elem == other.m_elem; }
inline bool operator<(Element other) const { return m_elem < other.m_elem; }
inline Type type() const { return static_cast<Type>(m_elem.tag()); }
inline const Node* node() const { return static_cast<const Node*>(m_elem.get()); }
......
......@@ -27,6 +27,7 @@ public:
constexpr inline void setTag(uint8_t tag) { m_data = (tag & TagMask) | (m_data & ~TagMask); }
constexpr inline operator bool() const { return (m_data & ~TagMask); }
constexpr inline bool operator==(TaggedPointer<T> other) const { return m_data == other.m_data; }
constexpr inline bool operator<(TaggedPointer<T> other) const { return m_data < other.m_data; }
private:
enum { TagMask = 0x3 };
......
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