KOSMIndoorMap

scenecontroller.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "scenecontroller.h"
8 #include "logging.h"
9 #include "render-logging.h"
10 
11 #include "iconloader_p.h"
12 #include "penwidthutil_p.h"
13 #include "poleofinaccessibilityfinder_p.h"
14 #include "scenegeometry_p.h"
15 #include "openinghourscache_p.h"
16 #include "../style/mapcssdeclaration_p.h"
17 #include "../style/mapcssstate_p.h"
18 #include "../style/mapcssresult_p.h"
19 
20 #include <KOSMIndoorMap/MapData>
21 #include <KOSMIndoorMap/MapCSSStyle>
22 #include <KOSMIndoorMap/OverlaySource>
23 #include <KOSMIndoorMap/SceneGraph>
24 #include <KOSMIndoorMap/View>
25 
26 #include <osm/element.h>
27 #include <osm/datatypes.h>
28 
29 #include <QDebug>
30 #include <QElapsedTimer>
31 #include <QGuiApplication>
32 #include <QPalette>
33 
34 namespace KOSMIndoorMap {
35 class SceneControllerPrivate
36 {
37 public:
38  MapData m_data;
39  const MapCSSStyle *m_styleSheet = nullptr;
40  const View *m_view = nullptr;
41  std::vector<QPointer<AbstractOverlaySource>> m_overlaySources;
42  mutable std::vector<OSM::Element> m_hiddenElements;
43 
44  MapCSSResult m_styleResult;
45  QColor m_defaultTextColor;
46  QFont m_defaultFont;
47  QPolygonF m_labelPlacementPath;
48  IconLoader m_iconLoader;
49  OpeningHoursCache m_openingHours;
50  PoleOfInaccessibilityFinder m_piaFinder;
51 
52  OSM::TagKey m_layerTag;
53  OSM::TagKey m_typeTag;
54 
55  bool m_dirty = true;
56  bool m_overlay = false;
57 };
58 }
59 
60 using namespace KOSMIndoorMap;
61 
62 SceneController::SceneController() : d(new SceneControllerPrivate) {}
63 SceneController::~SceneController() = default;
64 
65 void SceneController::setMapData(const MapData &data)
66 {
67  d->m_data = data;
68  if (!d->m_data.isEmpty()) {
69  d->m_layerTag = data.dataSet().tagKey("layer");
70  d->m_typeTag = data.dataSet().tagKey("type");
71  d->m_openingHours.setMapData(data);
72  } else {
73  d->m_layerTag = {};
74  d->m_typeTag = {};
75  d->m_openingHours.setMapData(MapData());
76  }
77  d->m_dirty = true;
78 }
79 
80 void SceneController::setStyleSheet(const MapCSSStyle *styleSheet)
81 {
82  d->m_styleSheet = styleSheet;
83  d->m_dirty = true;
84 }
85 
86 void SceneController::setView(const View *view)
87 {
88  d->m_view = view;
89  QObject::connect(view, &View::timeChanged, [this]() { d->m_dirty = true; });
90  d->m_dirty = true;
91 }
92 
93 void SceneController::setOverlaySources(std::vector<QPointer<AbstractOverlaySource>> &&overlays)
94 {
95  d->m_overlaySources = std::move(overlays);
96  d->m_dirty = true;
97 }
98 
100 {
101  // TODO we could potentially do this more fine-grained?
102  d->m_dirty = true;
103 }
104 
106 {
107  QElapsedTimer sgUpdateTimer;
108  sgUpdateTimer.start();
109 
110  // check if we are set up completely yet (we can't rely on a defined order with QML)
111  if (!d->m_view || !d->m_styleSheet) {
112  return;
113  }
114 
115  // check if the scene is dirty at all
116  if (sg.zoomLevel() == (int)d->m_view->zoomLevel() && sg.currentFloorLevel() == d->m_view->level() && !d->m_dirty) {
117  return;
118  }
119  sg.setZoomLevel(d->m_view->zoomLevel());
120  sg.setCurrentFloorLevel(d->m_view->level());
121  d->m_openingHours.setTimeRange(d->m_view->beginTime(), d->m_view->endTime());
122  d->m_dirty = false;
123 
124  sg.beginSwap();
125  updateCanvas(sg);
126 
127  if (d->m_data.isEmpty()) { // if we don't have map data yet, we just need to get canvas styling here
128  sg.endSwap();
129  return;
130  }
131 
132  // find all intermediate levels below or above the currently selected "full" level
133  auto it = d->m_data.levelMap().find(MapLevel(d->m_view->level()));
134  if (it == d->m_data.levelMap().end()) {
135  return;
136  }
137 
138  auto beginIt = it;
139  if (beginIt != d->m_data.levelMap().begin()) {
140  do {
141  --beginIt;
142  } while (!(*beginIt).first.isFullLevel() && beginIt != d->m_data.levelMap().begin());
143  ++beginIt;
144  }
145 
146  auto endIt = it;
147  for (++endIt; endIt != d->m_data.levelMap().end(); ++endIt) {
148  if ((*endIt).first.isFullLevel()) {
149  break;
150  }
151  }
152 
153  // collect elements that the overlay want to hide
154  d->m_hiddenElements.clear();
155  for (const auto &overlaySource : d->m_overlaySources) {
156  overlaySource->hiddenElements(d->m_hiddenElements);
157  }
158  std::sort(d->m_hiddenElements.begin(), d->m_hiddenElements.end());
159 
160  // for each level, update or create scene graph elements, after a some basic bounding box check
161  const auto geoBbox = d->m_view->mapSceneToGeo(d->m_view->sceneBoundingBox());
162  for (auto it = beginIt; it != endIt; ++it) {
163  for (auto e : (*it).second) {
164  if (OSM::intersects(geoBbox, e.boundingBox()) && !std::binary_search(d->m_hiddenElements.begin(), d->m_hiddenElements.end(), e)) {
165  updateElement(e, (*it).first.numericLevel(), sg);
166  }
167  }
168  }
169 
170  // update overlay elements
171  d->m_overlay = true;
172  for (const auto &overlaySource : d->m_overlaySources) {
173  overlaySource->forEach(d->m_view->level(), [this, &geoBbox, &sg](OSM::Element e, int floorLevel) {
174  if (OSM::intersects(geoBbox, e.boundingBox()) && e.type() != OSM::Type::Null) {
175  updateElement(e, floorLevel, sg);
176  }
177  });
178  }
179  d->m_overlay = false;
180 
181  sg.zSort();
182  sg.endSwap();
183 
184  qCDebug(RenderLog) << "updated scenegraph took" << sgUpdateTimer.elapsed() << "ms";
185 }
186 
187 void SceneController::updateCanvas(SceneGraph &sg) const
188 {
189  sg.setBackgroundColor(QGuiApplication::palette().color(QPalette::Base));
190  d->m_defaultTextColor = QGuiApplication::palette().color(QPalette::Text);
191  d->m_defaultFont = QGuiApplication::font();
192 
193  MapCSSState state;
194  state.zoomLevel = d->m_view->zoomLevel();
195  state.floorLevel = d->m_view->level();
196  d->m_styleSheet->evaluateCanvas(state, d->m_styleResult);
197  for (auto decl : d->m_styleResult[{}].declarations()) {
198  switch (decl->property()) {
199  case MapCSSDeclaration::FillColor:
200  sg.setBackgroundColor(decl->colorValue());
201  break;
202  case MapCSSDeclaration::TextColor:
203  d->m_defaultTextColor = decl->colorValue();
204  break;
205  default:
206  break;
207  }
208  }
209 }
210 
211 void SceneController::updateElement(OSM::Element e, int level, SceneGraph &sg) const
212 {
213  MapCSSState state;
214  state.element = e;
215  state.zoomLevel = d->m_view->zoomLevel();
216  state.floorLevel = d->m_view->level();
217  state.openingHours = &d->m_openingHours;
218  d->m_styleSheet->evaluate(state, d->m_styleResult);
219  for (const auto &result : d->m_styleResult.results()) {
220  updateElement(e, level, sg, result);
221  }
222 }
223 
224 void SceneController::updateElement(OSM::Element e, int level, SceneGraph &sg, const MapCSSResultItem &result) const
225 {
226  if (result.hasAreaProperties()) {
227  PolygonBaseItem *item = nullptr;
228  std::unique_ptr<SceneGraphItemPayload> baseItem;
229  if (e.type() == OSM::Type::Relation && e.tagValue(d->m_typeTag) == "multipolygon") {
230  baseItem = sg.findOrCreatePayload<MultiPolygonItem>(e, level, result.layerSelector());
231  auto i = static_cast<MultiPolygonItem*>(baseItem.get());
232  if (i->path.isEmpty()) {
233  i->path = createPath(e, d->m_labelPlacementPath);
234  } else if (result.hasLabelProperties()) {
235  SceneGeometry::outerPolygonFromPath(i->path, d->m_labelPlacementPath);
236  }
237  item = i;
238  } else {
239  baseItem = sg.findOrCreatePayload<PolygonItem>(e, level, result.layerSelector());
240  auto i = static_cast<PolygonItem*>(baseItem.get());
241  if (i->polygon.isEmpty()) {
242  i->polygon = createPolygon(e);
243  }
244  d->m_labelPlacementPath = i->polygon;
245  item = i;
246  }
247 
248  double lineOpacity = 1.0;
249  double fillOpacity = 1.0;
250  initializePen(item->pen);
251  for (auto decl : result.declarations()) {
252  applyGenericStyle(decl, item);
253  applyPenStyle(e, decl, item->pen, lineOpacity, item->penWidthUnit);
254  switch (decl->property()) {
255  case MapCSSDeclaration::FillColor:
256  item->brush.setColor(decl->colorValue());
257  item->brush.setStyle(Qt::SolidPattern);
258  break;
259  case MapCSSDeclaration::FillOpacity:
260  fillOpacity = decl->doubleValue();
261  break;
262  default:
263  break;
264  }
265  }
266  finalizePen(item->pen, lineOpacity);
267  if (item->brush.style() == Qt::SolidPattern && fillOpacity < 1.0) {
268  auto c = item->brush.color();
269  c.setAlphaF(c.alphaF() * fillOpacity);
270  item->brush.setColor(c);
271  }
272  if (item->brush.color().alphaF() == 0.0) {
273  item->brush.setStyle(Qt::NoBrush);
274  }
275 
276  addItem(sg, e, level, result.layerSelector(), std::move(baseItem));
277  } else if (result.hasLineProperties()) {
278  auto baseItem = sg.findOrCreatePayload<PolylineItem>(e, level, result.layerSelector());
279  auto item = static_cast<PolylineItem*>(baseItem.get());
280  if (item->path.isEmpty()) {
281  item->path = createPolygon(e);
282  }
283 
284  double lineOpacity = 1.0;
285  double casingOpacity = 1.0;
286  initializePen(item->pen);
287  initializePen(item->casingPen);
288  for (auto decl : result.declarations()) {
289  applyGenericStyle(decl, item);
290  applyPenStyle(e, decl, item->pen, lineOpacity, item->penWidthUnit);
291  applyCasingPenStyle(e, decl, item->casingPen, casingOpacity, item->casingPenWidthUnit);
292  }
293  finalizePen(item->pen, lineOpacity);
294  finalizePen(item->casingPen, casingOpacity);
295 
296  d->m_labelPlacementPath = item->path;
297  addItem(sg, e, level, result.layerSelector(), std::move(baseItem));
298  }
299 
300  if (result.hasLabelProperties()) {
301  QString text;
302  auto textDecl = result.declaration(MapCSSDeclaration::Text);
303  if (!textDecl) {
304  textDecl = result.declaration(MapCSSDeclaration::ShieldText);
305  }
306 
307  if (textDecl) {
308  if (!textDecl->keyValue().isEmpty()) {
309  text = QString::fromUtf8(e.tagValue(textDecl->keyValue().constData(), QLocale()));
310  } else {
311  text = textDecl->stringValue();
312  }
313  }
314 
315  const auto iconDecl = result.declaration(MapCSSDeclaration::IconImage);
316 
317  if (!text.isEmpty() || iconDecl) {
318  auto baseItem = sg.findOrCreatePayload<LabelItem>(e, level, result.layerSelector());
319  auto item = static_cast<LabelItem*>(baseItem.get());
320  item->text.setText(text);
321  item->font = d->m_defaultFont;
322  item->color = d->m_defaultTextColor;
323 
324  if (item->pos.isNull()) {
325  if (result.hasAreaProperties()) {
326  // for simple enough shapes we can use the faster centroid rather than the expensive PIA
327  if (d->m_labelPlacementPath.size() > 6) {
328  item->pos = d->m_piaFinder.find(d->m_labelPlacementPath);
329  } else {
330  item->pos = SceneGeometry::polygonCentroid(d->m_labelPlacementPath);
331  }
332  } else if (result.hasLineProperties()) {
333  item->pos = SceneGeometry::polylineMidPoint(d->m_labelPlacementPath);
334  }
335  if (item->pos.isNull()) {
336  item->pos = d->m_view->mapGeoToScene(e.center()); // node or something failed above
337  }
338  }
339 
340  double textOpacity = 1.0;
341  double shieldOpacity = 1.0;
342  IconData iconData;
343  for (auto decl : result.declarations()) {
344  applyGenericStyle(decl, item);
345  applyFontStyle(decl, item->font);
346  switch (decl->property()) {
347  case MapCSSDeclaration::TextColor:
348  item->color = decl->colorValue();
349  break;
350  case MapCSSDeclaration::TextOpacity:
351  textOpacity = decl->doubleValue();
352  break;
353  case MapCSSDeclaration::ShieldCasingColor:
354  item->casingColor = decl->colorValue();
355  break;
356  case MapCSSDeclaration::ShieldCasingWidth:
357  item->casingWidth = decl->doubleValue();
358  break;
359  case MapCSSDeclaration::ShieldColor:
360  item->shieldColor = decl->colorValue();
361  break;
362  case MapCSSDeclaration::ShieldOpacity:
363  shieldOpacity = decl->doubleValue();
364  break;
365  case MapCSSDeclaration::ShieldFrameColor:
366  item->frameColor = decl->colorValue();
367  break;
368  case MapCSSDeclaration::ShieldFrameWidth:
369  item->frameWidth = decl->doubleValue();
370  break;
371  case MapCSSDeclaration::TextPosition:
372  if (decl->textFollowsLine() && d->m_labelPlacementPath.size() > 1) {
373  item->angle = SceneGeometry::polylineMidPointAngle(d->m_labelPlacementPath);
374  }
375  break;
376  case MapCSSDeclaration::TextOffset:
377  item->offset = decl->doubleValue();
378  break;
379  case MapCSSDeclaration::MaxWidth:
380  item->text.setTextWidth(decl->intValue());
381  break;
382  case MapCSSDeclaration::IconImage:
383  if (!decl->keyValue().isEmpty()) {
384  iconData.name = QString::fromUtf8(e.tagValue(decl->keyValue().constData()));
385  } else {
386  iconData.name = decl->stringValue();
387  }
388  break;
389  case MapCSSDeclaration::IconHeight:
390  item->iconSize.setHeight(decl->doubleValue()); // TODO percent sizes
391  break;
392  case MapCSSDeclaration::IconWidth:
393  item->iconSize.setWidth(decl->doubleValue()); // TODO percent sizes
394  break;
395  case MapCSSDeclaration::IconColor:
396  {
397  const auto alpha = iconData.color.alphaF();
398  iconData.color = decl->colorValue().rgb();
399  iconData.color.setAlphaF(alpha);
400  break;
401  }
402  case MapCSSDeclaration::IconOpacity:
403  iconData.color.setAlphaF(decl->doubleValue());
404  break;
405  case MapCSSDeclaration::TextHaloColor:
406  item->haloColor = decl->colorValue();
407  break;
408  case MapCSSDeclaration::TextHaloRadius:
409  item->haloRadius = decl->doubleValue();
410  break;
411  default:
412  break;
413  }
414  }
415  if (item->color.isValid() && textOpacity < 1.0) {
416  auto c = item->color;
417  c.setAlphaF(c.alphaF() * textOpacity);
418  item->color = c;
419  }
420  if (item->shieldColor.isValid() && shieldOpacity < 1.0) {
421  auto c = item->shieldColor;
422  c.setAlphaF(c.alphaF() * shieldOpacity);
423  item->shieldColor = c;
424  }
425  if (!iconData.name.isEmpty() && iconData.color.alphaF() > 0.0) {
426  if (!iconData.color.isValid()) {
427  iconData.color = d->m_defaultTextColor;
428  }
429  item->icon = d->m_iconLoader.loadIcon(iconData);
430  }
431  if (!item->icon.isNull()) {
432  const auto iconSourceSize = item->icon.availableSizes().at(0);
433  const auto aspectRatio = (double)iconSourceSize.width() / (double)iconSourceSize.height();
434  if (item->iconSize.width() <= 0.0 && item->iconSize.height() <= 0.0) {
435  item->iconSize = iconSourceSize;
436  } else if (item->iconSize.width() <= 0.0) {
437  item->iconSize.setWidth(item->iconSize.height() * aspectRatio);
438  } else if (item->iconSize.height() <= 0.0) {
439  item->iconSize.setHeight(item->iconSize.width() / aspectRatio);
440  }
441  }
442 
443  if (!item->text.text().isEmpty()) {
444  QTextOption opt;
447  item->text.setTextOption(opt);
448  item->text.prepare({}, item->font);
449 
450  // discard labels that are longer than the line they are aligned with
451  if (result.hasLineProperties() && d->m_labelPlacementPath.size() > 1 && item->angle != 0.0) {
452  const auto sceneLen = SceneGeometry::polylineLength(d->m_labelPlacementPath);
453  const auto sceneP1 = d->m_view->viewport().topLeft();
454  const auto sceneP2 = QPointF(sceneP1.x() + sceneLen, sceneP1.y());
455  const auto screenP1 = d->m_view->mapSceneToScreen(sceneP1);
456  const auto screenP2 = d->m_view->mapSceneToScreen(sceneP2);
457  const auto screenLen = screenP2.x() - screenP1.x();
458  if (screenLen < item->text.size().width()) {
459  item->text = {};
460  }
461  }
462  }
463 
464  if (!item->icon.isNull() || !item->text.text().isEmpty()) {
465  addItem(sg, e, level, result.layerSelector(), std::move(baseItem));
466  }
467  }
468  }
469 }
470 
471 QPolygonF SceneController::createPolygon(OSM::Element e) const
472 {
473  const auto path = e.outerPath(d->m_data.dataSet());
474  if (path.empty()) {
475  return {};
476  }
477 
478  QPolygonF poly;
479  // Element::outerPath takes care of re-assembling broken up line segments
480  // the below takes care of properly merging broken up polygons
481  for (auto it = path.begin(); it != path.end();) {
482  QPolygonF subPoly;
483  subPoly.reserve(path.size());
484  OSM::Id pathBegin = (*it)->id;
485 
486  auto subIt = it;
487  for (; subIt != path.end(); ++subIt) {
488  subPoly.push_back(d->m_view->mapGeoToScene((*subIt)->coordinate));
489  if ((*subIt)->id == pathBegin && subIt != it && subIt != std::prev(path.end())) {
490  ++subIt;
491  break;
492  }
493  }
494  it = subIt;
495  poly = poly.isEmpty() ? std::move(subPoly) : poly.united(subPoly);
496  }
497  return poly;
498 }
499 
500 // @see https://wiki.openstreetmap.org/wiki/Relation:multipolygon
501 QPainterPath SceneController::createPath(const OSM::Element e, QPolygonF &outerPath) const
502 {
503  assert(e.type() == OSM::Type::Relation);
504  outerPath = createPolygon(e);
505  QPainterPath path;
506  path.addPolygon(outerPath); // assemble the outer polygon, which can be represented as a set of unsorted lines here even
507 
508  for (const auto &mem : e.relation()->members) {
509  const bool isInner = std::strcmp(mem.role().name(), "inner") == 0;
510  if (mem.type() != OSM::Type::Way || !isInner) {
511  continue;
512  }
513  if (auto way = d->m_data.dataSet().way(mem.id)) {
514  const auto subPoly = createPolygon(OSM::Element(way));
515  QPainterPath subPath;
516  subPath.addPolygon(subPoly);
517  path = path.subtracted(subPath);
518  }
519  }
520 
521  return path;
522 }
523 
524 void SceneController::applyGenericStyle(const MapCSSDeclaration *decl, SceneGraphItemPayload *item) const
525 {
526  if (decl->property() == MapCSSDeclaration::ZIndex) {
527  item->z = decl->intValue();
528  }
529 }
530 
531 void SceneController::applyPenStyle(OSM::Element e, const MapCSSDeclaration *decl, QPen &pen, double &opacity, Unit &unit) const
532 {
533  switch (decl->property()) {
534  case MapCSSDeclaration::Color:
535  pen.setColor(decl->colorValue());
536  break;
537  case MapCSSDeclaration::Width:
538  pen.setWidthF(PenWidthUtil::penWidth(e, decl, unit));
539  break;
540  case MapCSSDeclaration::Dashes:
541  pen.setDashPattern(decl->dashesValue());
542  break;
543  case MapCSSDeclaration::LineCap:
544  pen.setCapStyle(decl->capStyle());
545  break;
546  case MapCSSDeclaration::LineJoin:
547  pen.setJoinStyle(decl->joinStyle());
548  break;
549  case MapCSSDeclaration::Opacity:
550  opacity = decl->doubleValue();
551  break;
552  default:
553  break;
554  }
555 }
556 
557 void SceneController::applyCasingPenStyle(OSM::Element e, const MapCSSDeclaration *decl, QPen &pen, double &opacity, Unit &unit) const
558 {
559  switch (decl->property()) {
560  case MapCSSDeclaration::CasingColor:
561  pen.setColor(decl->colorValue());
562  break;
563  case MapCSSDeclaration::CasingWidth:
564  pen.setWidthF(PenWidthUtil::penWidth(e, decl, unit));
565  break;
566  case MapCSSDeclaration::CasingDashes:
567  pen.setDashPattern(decl->dashesValue());
568  break;
569  case MapCSSDeclaration::CasingLineCap:
570  pen.setCapStyle(decl->capStyle());
571  break;
572  case MapCSSDeclaration::CasingLineJoin:
573  pen.setJoinStyle(decl->joinStyle());
574  break;
575  case MapCSSDeclaration::CasingOpacity:
576  opacity = decl->doubleValue();
577  break;
578  default:
579  break;
580  }
581 }
582 
583 void SceneController::applyFontStyle(const MapCSSDeclaration *decl, QFont &font) const
584 {
585  switch (decl->property()) {
586  case MapCSSDeclaration::FontFamily:
587  font.setFamily(decl->stringValue());
588  break;
589  case MapCSSDeclaration::FontSize:
590  if (decl->unit() == MapCSSDeclaration::Pixels) {
591  font.setPixelSize(decl->doubleValue());
592  } else {
593  font.setPointSizeF(decl->doubleValue());
594  }
595  break;
596  case MapCSSDeclaration::FontWeight:
597  font.setBold(decl->isBoldStyle());
598  break;
599  case MapCSSDeclaration::FontStyle:
600  font.setItalic(decl->isItalicStyle());
601  break;
602  case MapCSSDeclaration::FontVariant:
603  font.setCapitalization(decl->capitalizationStyle());
604  break;
605  case MapCSSDeclaration::TextDecoration:
606  font.setUnderline(decl->isUnderlineStyle());
607  break;
608  case MapCSSDeclaration::TextTransform:
609  font.setCapitalization(decl->capitalizationStyle());
610  break;
611  default:
612  break;
613  }
614 }
615 
616 void SceneController::initializePen(QPen &pen) const
617 {
619 
620  // default according to spec
623  pen.setStyle(Qt::SolidLine);
624 }
625 
626 void SceneController::finalizePen(QPen &pen, double opacity) const
627 {
628  if (pen.color().isValid() && opacity < 1.0) {
629  auto c = pen.color();
630  c.setAlphaF(c.alphaF() * opacity);
631  pen.setColor(c);
632  }
633 
634  if (pen.color().alphaF() == 0.0) {
635  pen.setStyle(Qt::NoPen); // so the renderer can skip this entirely
636  }
637 
638  // normalize dash pattern, as QPainter scales that with the line width
639  if (pen.widthF() > 0.0 && !pen.dashPattern().isEmpty()) {
640  auto dashes = pen.dashPattern();
641  std::for_each(dashes.begin(), dashes.end(), [pen](double &d) { d /= pen.widthF(); });
642  pen.setDashPattern(std::move(dashes));
643  }
644 }
645 
646 void SceneController::addItem(SceneGraph &sg, OSM::Element e, int level, LayerSelectorKey layerSelector, std::unique_ptr<SceneGraphItemPayload> &&payload) const
647 {
648  SceneGraphItem item;
649  item.element = e;
650  item.layerSelector = layerSelector;
651  item.level = level;
652  item.payload = std::move(payload);
653 
654  // get the OSM layer, if set
655  if (!d->m_overlay) {
656  const auto layerStr = e.tagValue(d->m_layerTag);
657  if (!layerStr.isEmpty()) {
658  bool success = false;
659  const auto layer = layerStr.toInt(&success);
660  if (success) {
661 
662  // ### Ignore layer information when it matches the level
663  // This is very wrong according to the specification, however it looks that in many places
664  // layer and level tags aren't correctly filled, possibly a side-effect of layer pre-dating
665  // level and layers not having been properly updated when retrofitting level information
666  // Strictly following the MapCSS rendering order yields sub-optimal results in that case, with
667  // relevant elements being hidden.
668  //
669  // Ideally we find a way to detect the presence of that problem, and only then enabling this
670  // workaround, but until we have this, this seems to produce better results in all tests.
671  if (level != layer * 10) {
672  item.layer = layer;
673  }
674  } else {
675  qCWarning(Log) << "Invalid layer:" << e.url() << layerStr;
676  }
677  }
678  } else {
679  item.layer = std::numeric_limits<int>::max();
680  }
681 
682  sg.addItem(std::move(item));
683 }
OSM-based multi-floor indoor maps for buildings.
A path/way/line item in the scenegraph.
QPolygonF united(const QPolygonF &r) const const
void setStyle(Qt::PenStyle style)
SolidPattern
qreal textWidth() const const
int toInt(bool *ok, int base) const const
qreal alphaF() const const
SolidLine
Scene graph of the currently displayed level.
Definition: scenegraph.h:28
Payload base class for scene graph items.
Scene graph item description and handle for its content.
Qt::BrushStyle style() const const
A parsed MapCSS style sheet.
Definition: mapcssstyle.h:29
View transformations and transformation manipulation.
Definition: view.h:39
A text or item label.
const QColor & color(QPalette::ColorGroup group, QPalette::ColorRole role) const const
int size() const const
void setUnderline(bool enable)
Multi-polygon item, used for polygons with "holes" in them.
void setJoinStyle(Qt::PenJoinStyle style)
A floor level.
Definition: mapdata.h:27
void setText(const QString &text)
void setStyle(Qt::BrushStyle style)
AlignHCenter
const QColor & color() const const
void setBold(bool enable)
void setCapStyle(Qt::PenCapStyle style)
void setPixelSize(int pixelSize)
void setWrapMode(QTextOption::WrapMode mode)
QColor color() const const
QList< QSize > availableSizes(QIcon::Mode mode, QIcon::State state) const const
A reference to any of OSM::Node/OSMWay/OSMRelation.
Definition: element.h:22
qreal x() const const
void setDashPattern(const QVector< qreal > &pattern)
QString fromUtf8(const char *str, int size)
void addPolygon(const QPolygonF &polygon)
A key of an OSM tag.
Definition: datatypes.h:179
void setTextWidth(qreal textWidth)
QPalette palette()
void setWidth(qreal width)
bool isEmpty() const const
Raw OSM map data, separated by levels.
Definition: mapdata.h:59
void updateScene(SceneGraph &sg) const
Creates or updates sg based on the currently set style and view settings.
void setWidthF(qreal width)
Base item for filled polygons.
void overlaySourceUpdated()
Overlay dirty state tracking.
void setColor(const QColor &color)
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition: datatypes.cpp:27
int64_t Id
OSM element identifier.
Definition: datatypes.h:27
void reserve(int size)
RoundJoin
void prepare(const QTransform &matrix, const QFont &font)
void setItalic(bool enable)
void setPointSizeF(qreal pointSize)
std::vector< const Node * > outerPath(const DataSet &dataSet) const
Returns all nodes belonging to the outer path of this element.
Definition: element.cpp:166
bool isEmpty() const const
bool isNull() const const
void setFamily(const QString &family)
QPainterPath subtracted(const QPainterPath &p) const const
QString text() const const
bool isEmpty() const const
A single filled polygon.
Unit
Unit for geometry sizes.
qreal widthF() const const
void setCapitalization(QFont::Capitalization caps)
void push_back(const T &value)
void setAlphaF(qreal alpha)
qreal height() const const
transparent
QVector< qreal > dashPattern() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
OSM::Element element
The OSM::Element this item refers to.
void setHeight(qreal height)
qint64 elapsed() const const
void setColor(const QColor &color)
void setAlignment(Qt::Alignment alignment)
qreal width() const const
void setTextOption(const QTextOption &textOption)
bool isValid() const const
bool isNull() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 23:04:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.