1 /*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include <KOSMIndoorMap/FloorLevelModel>
8 #include <KOSMIndoorMap/HitDetector>
9 #include <KOSMIndoorMap/MapCSSParser>
10 #include <KOSMIndoorMap/MapCSSStyle>
11 #include <KOSMIndoorMap/MapData>
12 #include <KOSMIndoorMap/MapLoader>
13 #include <KOSMIndoorMap/PainterRenderer>
14 #include <KOSMIndoorMap/SceneController>
15 #include <KOSMIndoorMap/SceneGraph>
16 #include <KOSMIndoorMap/View>
17
18 #include <QApplication>
19 #include <QCommandLineParser>
20 #include <QMouseEvent>
21 #include <QComboBox>
22 #include <QHBoxLayout>
23 #include <QPainter>
24 #include <QRegularExpression>
25
26 using namespace KOSMIndoorMap;
27
cssPath(const QString & styleName)28 static QString cssPath(const QString &styleName)
29 {
30 return QLatin1String(SOURCE_DIR "/../src/map/assets/css/") + styleName + QLatin1String(".mapcss");
31 // return QLatin1String(":/org.kde.kosmindoormap/assets/css/") + styleName + QLatin1String(".mapcss");
32 }
33
34 class MapWidget : public QWidget
35 {
36 public:
37 explicit MapWidget(QWidget *parent = nullptr);
38 void paintEvent(QPaintEvent *event) override;
39 void resizeEvent(QResizeEvent *event) override;
40 void mousePressEvent(QMouseEvent *event) override;
41 void mouseMoveEvent(QMouseEvent *event) override;
42 void mouseReleaseEvent(QMouseEvent *event) override;
43 void wheelEvent(QWheelEvent *event) override;
44 void setMapData(MapData &&data);
45 void setStyleSheet(const QString &styleName);
46
47 MapData m_data;
48 SceneGraph m_sg;
49 MapCSSStyle m_style;
50 SceneController m_controller;
51 PainterRenderer m_renderer;
52 View m_view;
53 QPoint m_lastPanPoint;
54 };
55
MapWidget(QWidget * parent)56 MapWidget::MapWidget(QWidget* parent)
57 : QWidget(parent)
58 {
59 m_view.setScreenSize(size());
60 m_controller.setView(&m_view);
61 }
62
paintEvent(QPaintEvent * event)63 void MapWidget::paintEvent(QPaintEvent *event)
64 {
65 m_controller.updateScene(m_sg);
66 QPainter p(this);
67 m_renderer.setPainter(&p);
68 m_renderer.render(m_sg, &m_view);
69 return QWidget::paintEvent(event);
70 }
71
resizeEvent(QResizeEvent * event)72 void MapWidget::resizeEvent(QResizeEvent *event)
73 {
74 QWidget::resizeEvent(event);
75 m_view.setScreenSize(size());
76 }
77
mousePressEvent(QMouseEvent * event)78 void MapWidget::mousePressEvent(QMouseEvent *event)
79 {
80 m_lastPanPoint = event->pos();
81 QWidget::mousePressEvent(event);
82 }
83
mouseMoveEvent(QMouseEvent * event)84 void MapWidget::mouseMoveEvent(QMouseEvent *event)
85 {
86 m_view.panScreenSpace(m_lastPanPoint - event->pos());
87 m_lastPanPoint = event->pos();
88 QWidget::mouseMoveEvent(event);
89 update();
90 }
91
mouseReleaseEvent(QMouseEvent * event)92 void MapWidget::mouseReleaseEvent(QMouseEvent *event)
93 {
94 if (event->button() == Qt::RightButton) {
95 HitDetector detector;
96 const auto items = detector.itemsAt(event->pos(), m_sg, &m_view);
97 for (const auto item : items) {
98 qDebug() << item->element.url();
99 for (auto it = item->element.tagsBegin(); it != item->element.tagsEnd(); ++it) {
100 qDebug() << " " << (*it).key.name() << (*it).value;
101 }
102 switch (item->element.type()) {
103 case OSM::Type::Null:
104 case OSM::Type::Node:
105 break;
106 case OSM::Type::Way:
107 for (const auto &node : item->element.way()->nodes) {
108 qDebug() << " " << node;
109 }
110 break;
111 case OSM::Type::Relation:
112 for (const auto &mem : item->element.relation()->members) {
113 qDebug() << " " << mem.role().name() << (int)mem.type() << mem.id;
114 }
115 break;
116 }
117 }
118 }
119 }
120
wheelEvent(QWheelEvent * event)121 void MapWidget::wheelEvent(QWheelEvent *event)
122 {
123 if (event->angleDelta().y() > 0) {
124 m_view.zoomIn(event->position());
125 } else {
126 m_view.zoomOut(event->position());
127 }
128 QWidget::wheelEvent(event);
129 update();
130 }
131
setMapData(MapData && data)132 void MapWidget::setMapData(MapData &&data)
133 {
134 m_data = std::move(data);
135 m_controller.setMapData(m_data);
136 m_view.setSceneBoundingBox(m_data.boundingBox());
137 m_style.compile(m_data.dataSet());
138 m_controller.setStyleSheet(&m_style);
139 update();
140 }
141
setStyleSheet(const QString & styleName)142 void MapWidget::setStyleSheet(const QString &styleName)
143 {
144 MapCSSParser cssParser;
145 m_style = cssParser.parse(cssPath(styleName));
146 m_style.compile(m_data.dataSet());
147 m_controller.setStyleSheet(&m_style);
148 }
149
150
main(int argc,char ** argv)151 int main(int argc, char **argv)
152 {
153 QApplication app(argc, argv);
154 QCommandLineParser parser;
155 QCommandLineOption coordOpt({QStringLiteral("coordinate"), QStringLiteral("c")}, QStringLiteral("coordinate of the location to load"), QStringLiteral("lat,lon"));
156 parser.addOption(coordOpt);
157 QCommandLineOption fileOpt({QStringLiteral("file"), QStringLiteral("f")}, QStringLiteral("O5M or OSM PBF file to load"), QStringLiteral("file"));
158 parser.addOption(fileOpt);
159 parser.addHelpOption();
160 parser.addVersionOption();
161 parser.process(app);
162
163 MapWidget widget;
164 widget.resize(480, 720);
165 widget.setStyleSheet(QStringLiteral("breeze-light"));
166
167 auto layout = new QHBoxLayout(&widget);
168 layout->setAlignment(Qt::AlignTop);
169
170 FloorLevelModel floorModel;
171 auto levelBox = new QComboBox;
172 levelBox->setModel(&floorModel);
173 layout->addWidget(levelBox);
174 QObject::connect(levelBox, &QComboBox::currentTextChanged, &app, [&]() {
175 widget.m_view.setLevel(levelBox->currentData().value<MapLevel>().numericLevel());
176 widget.update();
177 });
178
179 auto styleBox = new QComboBox;
180 layout->addWidget(styleBox);
181 styleBox->addItems({QStringLiteral("breeze-light"), QStringLiteral("breeze-dark"), QStringLiteral("diagnostic")});
182 QObject::connect(styleBox, &QComboBox::currentTextChanged, &app, [&](const QString &styleName) {
183 widget.setStyleSheet(styleName);
184 widget.update();
185 });
186
187 widget.show();
188
189 MapLoader loader;
190 QObject::connect(&loader, &MapLoader::done, &app, [&]() {
191 widget.setMapData(loader.takeData());
192 floorModel.setMapData(&widget.m_data);
193 levelBox->setCurrentText(QLatin1String("0"));
194 });
195
196 if (parser.isSet(fileOpt)) {
197 loader.loadFromFile(parser.value(fileOpt));
198 } else if (parser.isSet(coordOpt)) {
199 const auto s = parser.value(coordOpt).split(QRegularExpression(QStringLiteral("[,/;]")));
200 loader.loadForCoordinate(s.at(0).toDouble(), s.at(1).toDouble());
201 }
202
203 return app.exec();
204 }
205