Commit 4268a413 authored by Volker Krause's avatar Volker Krause
Browse files

Integrate opening hours based conditions in MapCSS evaluation

This works and performs well already, but we are still missing proper
ways to configure the relevant time span as well as the holiday region.
Also, this has yet to be used in any of the style sheets.
parent 584b3cb8
Pipeline #42114 passed with stage
in 20 seconds
......@@ -36,6 +36,8 @@ ecm_setup_version(PROJECT VARIABLE_PREFIX KOSMINDOORMAP
find_package(Qt5 5.11 REQUIRED COMPONENTS Quick)
find_package(KF5 REQUIRED COMPONENTS I18n)
find_package(KPublicTransport REQUIRED)
find_package(KOpeningHours)
set_package_properties(KOpeningHours PROPERTIES TYPE OPTIONAL PURPOSE "Highlight currently open amenities/shops/etc.")
if (BUILD_TESTING)
find_package(Qt5 5.11 REQUIRED COMPONENTS Test Widgets)
......
# SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
# SPDX-License-Identifier: BSD-3-Clause
if (TARGET KOpeningHours)
set(HAVE_KOPENINGHOURS TRUE)
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kosmindoormap.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-kosmindoormap.h)
flex_target(mapcssscanner
style/mapcsslexer.l
${CMAKE_CURRENT_BINARY_DIR}/mapcsslexer.cpp
......@@ -42,6 +47,7 @@ add_library(KOSMIndoorMap
renderer/view.cpp
scene/iconloader.cpp
scene/openinghourscache.cpp
scene/overlaysource.cpp
scene/penwidthutil.cpp
scene/scenecontroller.cpp
......@@ -74,6 +80,9 @@ target_link_libraries(KOSMIndoorMap
PUBLIC Qt5::Gui KOSM
PRIVATE Qt5::Network Qt5::CorePrivate
)
if (TARGET KOpeningHours)
target_link_libraries(KOSMIndoorMap PRIVATE KOpeningHours)
endif()
ecm_generate_headers(KOSMIndoorMap_Content_FORWARDING_HEADERS
HEADER_NAMES
......
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KOSMINDOORMAP_CONFIG_H
#define KOSMINDOORMAP_CONFIG_H
#cmakedefine HAVE_KOPENINGHOURS
#endif
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "config-kosmindoormap.h"
#include "openinghourscache_p.h"
#include "logging.h"
#ifdef HAVE_KOPENINGHOURS
#include <KOpeningHours/Interval>
#include <KOpeningHours/OpeningHours>
#endif
using namespace KOSMIndoorMap;
void OpeningHoursCache::setTimeRange(const QDateTime &begin, const QDateTime &end)
{
if ((begin == m_begin || begin < QDateTime::currentDateTime()) && end == m_end) {
return;
}
m_begin = std::max(begin, QDateTime::currentDateTime());
m_end = end;
m_cacheEntries.clear();
}
bool OpeningHoursCache::isClosed(OSM::Element elem, const QByteArray &oh)
{
#ifndef HAVE_KOPENINGHOURS
Q_UNUSED(elem);
Q_UNUSED(oh);
return false;
#else
const auto key = elem.id();
const auto it = std::lower_bound(m_cacheEntries.begin(), m_cacheEntries.end(), key, [](auto lhs, auto rhs) {
return lhs.key < rhs;
});
if (it != m_cacheEntries.end() && (*it).key == key) {
return (*it).closed;
}
bool closed = false;
KOpeningHours::OpeningHours expr(oh, KOpeningHours::OpeningHours::IntervalMode);
expr.setLocation(elem.center().latF(), elem.center().lonF());
// TODO holiday region, timezone
if (expr.error() != KOpeningHours::OpeningHours::NoError) {
qCDebug(Log) << "opening hours expression error:" << expr.error() << oh;
} else {
auto i = expr.interval(m_begin);
while (i.state() == KOpeningHours::Interval::Closed && i.end().isValid() && i.end() < m_end) {
i = expr.nextInterval(i);
}
closed = i.state() == KOpeningHours::Interval::Closed;
}
m_cacheEntries.insert(it, {key, closed});
return closed;
#endif
}
/*
SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KOSMINDOORMAP_OPENINGHOURSCACHE_P_H
#define KOSMINDOORMAP_OPENINGHOURSCACHE_P_H
#include <KOSM/Element>
#include <QDateTime>
#include <vector>
namespace KOSMIndoorMap {
/** Opening hours expression evaluation cache for the currently displayed time range. */
class OpeningHoursCache
{
public:
OpeningHoursCache() = default;
~OpeningHoursCache() = default;
OpeningHoursCache(const OpeningHoursCache&) = delete;
OpeningHoursCache& operator=(const OpeningHoursCache&) = delete;
void setTimeRange(const QDateTime &begin, const QDateTime &end);
bool isClosed(OSM::Element elem, const QByteArray &oh);
private:
struct Entry {
OSM::Id key;
bool closed;
};
QDateTime m_begin = QDateTime::currentDateTime();
QDateTime m_end;
std::vector<Entry> m_cacheEntries;
};
}
#endif // KOSMINDOORMAP_OPENINGHOURSCACHE_P_H
......@@ -11,6 +11,7 @@
#include "iconloader_p.h"
#include "penwidthutil_p.h"
#include "scenegeometry_p.h"
#include "openinghourscache_p.h"
#include "../style/mapcssdeclaration_p.h"
#include "../style/mapcssstate_p.h"
#include "../style/mapcssresult_p.h"
......@@ -44,6 +45,7 @@ public:
QFont m_defaultFont;
QPolygonF m_labelPlacementPath;
IconLoader m_iconLoader;
OpeningHoursCache m_openingHours;
OSM::TagKey m_layerTag;
OSM::TagKey m_typeTag;
......@@ -206,6 +208,7 @@ void SceneController::updateElement(OSM::Element e, int level, SceneGraph &sg) c
state.element = e;
state.zoomLevel = d->m_view->zoomLevel();
state.floorLevel = d->m_view->level();
state.openingHours = &d->m_openingHours;
d->m_styleSheet->evaluate(state, d->m_styleResult);
if (d->m_styleResult.hasAreaProperties()) {
......
......@@ -25,7 +25,12 @@ static double toNumber(const QByteArray &val)
void MapCSSCondition::compile(const OSM::DataSet &dataSet)
{
m_tagKey = dataSet.tagKey(m_key.constData());
if (m_key == "mx:closed") {
m_tagKey = dataSet.tagKey("opening_hours");
m_op = (m_op == KeyNotSet ? IsNotClosed : IsClosed);
} else {
m_tagKey = dataSet.tagKey(m_key.constData());
}
}
bool MapCSSCondition::matches(const MapCSSState &state) const
......@@ -44,6 +49,15 @@ bool MapCSSCondition::matches(const MapCSSState &state) const
case GreaterThan: return toNumber(v) > m_numericValue;
case LessOrEqual: return toNumber(v) <= m_numericValue;
case GreaterOrEqual: return toNumber(v) >= m_numericValue;
case IsClosed:
case IsNotClosed:
{
if (v.isEmpty() || !state.openingHours) {
return m_op == IsNotClosed;
}
const auto closed = state.openingHours->isClosed(state.element, v);
return m_op == IsClosed ? closed : !closed;
}
}
return false;
}
......@@ -57,6 +71,8 @@ bool MapCSSCondition::matchesCanvas(const MapCSSState &state) const
switch (m_op) {
case KeySet:
case KeyNotSet:
case IsClosed:
case IsNotClosed:
return false;
case Equal: return (state.floorLevel/10) == m_numericValue;
case NotEqual: return (state.floorLevel/10) != m_numericValue;
......@@ -98,6 +114,8 @@ void MapCSSCondition::write(QIODevice *out) const
switch (m_op) {
case KeySet:
case KeyNotSet:
case IsClosed:
case IsNotClosed:
out->write("]"); return;
case Equal: out->write("="); break;
case NotEqual: out->write("!="); break;
......
......@@ -47,7 +47,9 @@ public:
LessThan,
GreaterThan,
LessOrEqual,
GreaterOrEqual
GreaterOrEqual,
IsClosed,
IsNotClosed,
};
void setKey(const char *key, int len);
......
......@@ -7,6 +7,8 @@
#ifndef KOSMINDOORMAP_MAPCSSSTATE_P_H
#define KOSMINDOORMAP_MAPCSSSTATE_P_H
#include <scene/openinghourscache_p.h>
#include <osm/element.h>
namespace KOSMIndoorMap {
......@@ -18,6 +20,7 @@ public:
OSM::Element element;
double zoomLevel = 0.0;
int floorLevel = 0;
OpeningHoursCache *openingHours = nullptr;
};
}
......
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