9#include "GeometryLayer.h"
12#include "AbstractGeoPolygonGraphicsItem.h"
13#include "GeoDataBuilding.h"
14#include "GeoDataDocument.h"
15#include "GeoDataFeature.h"
16#include "GeoDataIconStyle.h"
17#include "GeoDataLatLonAltBox.h"
18#include "GeoDataLineStyle.h"
19#include "GeoDataLinearRing.h"
20#include "GeoDataMultiGeometry.h"
21#include "GeoDataMultiTrack.h"
22#include "GeoDataObject.h"
23#include "GeoDataPhotoOverlay.h"
24#include "GeoDataPlacemark.h"
25#include "GeoDataPolyStyle.h"
26#include "GeoDataPolygon.h"
27#include "GeoDataScreenOverlay.h"
28#include "GeoDataStyle.h"
29#include "GeoDataStyleMap.h"
30#include "GeoDataTrack.h"
31#include "GeoDataTreeModel.h"
32#include "GeoGraphicsItem.h"
33#include "GeoGraphicsScene.h"
34#include "GeoLineStringGraphicsItem.h"
35#include "GeoPainter.h"
36#include "GeoPhotoGraphicsItem.h"
37#include "GeoPolygonGraphicsItem.h"
38#include "GeoTrackGraphicsItem.h"
39#include "MarbleDebug.h"
40#include "MarbleGraphicsItem.h"
41#include "MarblePlacemarkModel.h"
42#include "RenderState.h"
43#include "ScreenOverlayGraphicsItem.h"
44#include "StyleBuilder.h"
47#include <OsmPlacemarkData.h>
50#include <QAbstractItemModel>
56class GeometryLayerPrivate
64 struct PaintFragments {
73 explicit GeometryLayerPrivate(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder);
75 void createGraphicsItems(
const GeoDataObject *
object);
76 void createGraphicsItems(
const GeoDataObject *
object, FeatureRelationHash &relations);
77 void createGraphicsItemFromGeometry(
const GeoDataGeometry *
object,
const GeoDataPlacemark *placemark,
const Relations &relations);
78 void createGraphicsItemFromOverlay(
const GeoDataOverlay *overlay);
79 void removeGraphicsItems(
const GeoDataFeature *feature);
80 void updateTiledLineStrings(
const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem *lineStringItem);
81 static void updateTiledLineStrings(OsmLineStringItems &lineStringItems);
83 bool showRelation(
const GeoDataRelation *relation)
const;
84 void updateRelationVisibility();
87 const StyleBuilder *
const m_styleBuilder;
88 GeoGraphicsScene m_scene;
94 GeoGraphicsItem *m_lastFeatureAt;
97 int m_cachedItemCount;
99 using LayerItem = QPair<QString, GeoGraphicsItem *>;
102 GeoDataLatLonBox m_cachedLatLonBox;
104 GeoDataRelation::RelationTypes m_visibleRelationTypes;
105 bool m_levelTagDebugModeEnabled;
109GeometryLayerPrivate::GeometryLayerPrivate(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder)
111 , m_styleBuilder(styleBuilder)
113 , m_lastFeatureAt(nullptr)
115 , m_cachedItemCount(0)
116 , m_visibleRelationTypes(GeoDataRelation::RouteFerry)
117 , m_levelTagDebugModeEnabled(false)
122void GeometryLayerPrivate::createGraphicsItems(
const GeoDataObject *
object)
124 FeatureRelationHash noRelations;
125 createGraphicsItems(
object, noRelations);
128GeometryLayer::GeometryLayer(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder)
129 : d(std::make_unique<GeometryLayerPrivate>(model, styleBuilder))
131 const GeoDataObject *
object =
static_cast<GeoDataObject *
>(d->m_model->index(0, 0,
QModelIndex()).internalPointer());
132 if (
object && object->parent()) {
133 d->createGraphicsItems(object->parent());
140 connect(
this, &GeometryLayer::highlightedPlacemarksChanged, &d->m_scene, &GeoGraphicsScene::applyHighlight);
141 connect(&d->m_scene, &GeoGraphicsScene::repaintNeeded,
this, &GeometryLayer::repaintNeeded);
144GeometryLayer::~GeometryLayer() =
default;
148 return QStringList(QStringLiteral(
"HOVERS_ABOVE_SURFACE"));
151bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport,
const QString &renderPos, GeoSceneLayer *layer)
158 auto const &box = viewport->viewLatLonAltBox();
159 bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05);
161 if (d->m_cachedLatLonBox.isEmpty() || !isEqual) {
167 if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) {
174 const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel());
175 auto const items = d->m_scene.items(box, maxZoomLevel);
176 d->m_cachedLatLonBox = box;
177 d->m_cachedDateTime = now;
179 d->m_cachedItemCount = items.size();
180 d->m_cachedDefaultLayer.clear();
181 d->m_cachedPaintFragments.clear();
183 const QStringList &renderOrder = d->m_styleBuilder->renderOrder();
185 for (GeoGraphicsItem *item : items) {
188 mDebug() << item <<
" provides no paint layers, so I force one onto it.";
191 for (
const auto &layer : std::as_const(paintLayers)) {
192 if (knownLayers.contains(layer)) {
193 GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer];
194 double const zValue = item->zValue();
197 fragments.null << item;
199 }
else if (zValue < 0.0) {
200 fragments.negative << item;
203 fragments.positive << item;
207 d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item);
209 if (!missingLayers.
contains(layer)) {
210 mDebug() <<
"Missing layer " << layer <<
", in render order, will render it on top";
211 missingLayers << layer;
217 const auto layers = d->m_styleBuilder->renderOrder();
218 for (
const QString &layer : layers) {
219 GeometryLayerPrivate::PaintFragments &layerItems = paintFragments[layer];
220 std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan);
223 std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan);
224 std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan);
225 auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size();
226 d->m_cachedPaintFragments[layer].reserve(count);
227 d->m_cachedPaintFragments[layer] << layerItems.negative;
228 d->m_cachedPaintFragments[layer] << layerItems.null;
229 d->m_cachedPaintFragments[layer] << layerItems.positive;
233 const auto layers = d->m_styleBuilder->renderOrder();
234 for (
const QString &layer : layers) {
235 auto &layerItems = d->m_cachedPaintFragments[layer];
236 AbstractGeoPolygonGraphicsItem::s_previousStyle =
nullptr;
237 GeoLineStringGraphicsItem::s_previousStyle =
nullptr;
238 for (
auto item : std::as_const(layerItems)) {
239 if (d->m_levelTagDebugModeEnabled) {
240 if (
const auto placemark = geodata_cast<GeoDataPlacemark>(item->feature())) {
241 if (placemark->hasOsmData()) {
243 if (tagIter != placemark->osmData().tagsEnd()) {
244 const int val = tagIter.value().toInt();
245 if (val != d->m_debugLevelTag) {
252 item->paint(painter, viewport, layer, d->m_tileLevel);
256 for (
const auto &item : std::as_const(d->m_cachedDefaultLayer)) {
257 item.second->paint(painter, viewport, item.first, d->m_tileLevel);
260 for (ScreenOverlayGraphicsItem *item : std::as_const(d->m_screenOverlays)) {
261 item->paintEvent(painter, viewport);
265 d->m_runtimeTrace = QStringLiteral(
"Geometries: %1 Zoom: %2").arg(d->m_cachedItemCount).arg(d->m_tileLevel);
269RenderState GeometryLayer::renderState()
const
271 return RenderState(QStringLiteral(
"GeoGraphicsScene"));
274QString GeometryLayer::runtimeTrace()
const
276 return d->m_runtimeTrace;
279bool GeometryLayer::hasFeatureAt(
const QPoint &curpos,
const ViewportParams *viewport)
281 if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) {
285 auto const renderOrder = d->m_styleBuilder->renderOrder();
286 for (
int i = renderOrder.
size() - 1; i >= 0; --i) {
287 auto &layerItems = d->m_cachedPaintFragments[renderOrder[i]];
288 for (
auto item : layerItems) {
289 if (item->contains(curpos, viewport)) {
290 d->m_lastFeatureAt = item;
299void GeometryLayerPrivate::createGraphicsItems(
const GeoDataObject *
object, FeatureRelationHash &relations)
302 if (
auto document = geodata_cast<GeoDataDocument>(
object)) {
303 const auto features = document->featureList();
304 for (
auto feature : features) {
305 if (
auto relation = geodata_cast<GeoDataRelation>(feature)) {
306 relation->setVisible(showRelation(relation));
307 const auto members = relation->members();
308 for (
const auto &member : members) {
309 relations[member] << relation;
314 if (
auto placemark = geodata_cast<GeoDataPlacemark>(
object)) {
315 createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark));
316 }
else if (
const auto overlay =
dynamic_cast<const GeoDataOverlay *
>(
object)) {
317 createGraphicsItemFromOverlay(overlay);
321 if (
const auto container =
dynamic_cast<const GeoDataContainer *
>(
object)) {
322 int rowCount = container->size();
323 for (
int row = 0; row < rowCount; ++row) {
324 createGraphicsItems(container->child(row), relations);
329void GeometryLayerPrivate::updateTiledLineStrings(
const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem *lineStringItem)
331 if (!placemark->hasOsmData()) {
334 qint64
const osmId = placemark->osmData().oid();
338 auto &lineStringItems = m_osmLineStringItems[osmId];
339 lineStringItems << lineStringItem;
340 updateTiledLineStrings(lineStringItems);
343void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems)
345 GeoDataLineString merged;
346 if (lineStringItems.size() > 1) {
348 for (
auto item : lineStringItems) {
349 lineStrings << item->lineString();
351 merged = GeoLineStringGraphicsItem::merge(lineStrings);
357 for (
auto item : lineStringItems) {
358 item->setVisible(visible);
360 item->setMergedLineString(merged);
361 visible = merged.isEmpty();
366void GeometryLayerPrivate::clearCache()
368 m_lastFeatureAt =
nullptr;
371 m_cachedItemCount = 0;
372 m_cachedPaintFragments.clear();
373 m_cachedDefaultLayer.clear();
374 m_cachedLatLonBox = GeoDataLatLonBox();
377inline bool GeometryLayerPrivate::showRelation(
const GeoDataRelation *relation)
const
379 return (m_visibleRelationTypes.testFlag(relation->relationType()) || m_highlightedRouteRelations.contains(relation->osmData().oid()));
382void GeometryLayerPrivate::updateRelationVisibility()
384 for (
int i = 0; i < m_model->rowCount(); ++i) {
385 QVariant const data = m_model->
data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
386 auto object = qvariant_cast<GeoDataObject *>(data);
387 if (
auto doc = geodata_cast<GeoDataDocument>(
object)) {
388 const auto features = doc->featureList();
389 for (
auto feature : features) {
390 if (
auto relation = geodata_cast<GeoDataRelation>(feature)) {
391 relation->setVisible(showRelation(relation));
396 m_scene.resetStyle();
399void GeometryLayerPrivate::createGraphicsItemFromGeometry(
const GeoDataGeometry *
object,
const GeoDataPlacemark *placemark,
const Relations &relations)
401 if (!placemark->isGloballyVisible()) {
405 GeoGraphicsItem *item =
nullptr;
406 if (
const auto line = geodata_cast<GeoDataLineString>(
object)) {
407 auto lineStringItem =
new GeoLineStringGraphicsItem(placemark, line);
408 item = lineStringItem;
409 updateTiledLineStrings(placemark, lineStringItem);
410 }
else if (
const auto ring = geodata_cast<GeoDataLinearRing>(
object)) {
411 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring);
412 }
else if (
const auto poly = geodata_cast<GeoDataPolygon>(
object)) {
413 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly);
414 if (item->zValue() == 0) {
415 item->setZValue(poly->renderOrder());
417 }
else if (
const auto building = geodata_cast<GeoDataBuilding>(
object)) {
418 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building);
419 }
else if (
const auto multigeo = geodata_cast<GeoDataMultiGeometry>(
object)) {
420 int rowCount = multigeo->size();
421 for (
int row = 0; row < rowCount; ++row) {
422 createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations);
424 }
else if (
const auto multitrack = geodata_cast<GeoDataMultiTrack>(
object)) {
425 int rowCount = multitrack->size();
426 for (
int row = 0; row < rowCount; ++row) {
427 createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations);
429 }
else if (
const auto track = geodata_cast<GeoDataTrack>(
object)) {
430 item =
new GeoTrackGraphicsItem(placemark, track);
435 item->setRelations(relations);
436 item->setStyleBuilder(m_styleBuilder);
437 item->setVisible(item->visible() && placemark->isGloballyVisible());
438 item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark));
439 m_scene.addItem(item);
442void GeometryLayerPrivate::createGraphicsItemFromOverlay(
const GeoDataOverlay *overlay)
444 if (!overlay->isGloballyVisible()) {
448 GeoGraphicsItem *item =
nullptr;
449 if (
const auto photoOverlay = geodata_cast<GeoDataPhotoOverlay>(overlay)) {
450 auto photoItem =
new GeoPhotoGraphicsItem(overlay);
451 photoItem->setPoint(photoOverlay->point());
453 }
else if (
const auto screenOverlay = geodata_cast<GeoDataScreenOverlay>(overlay)) {
454 auto screenItem =
new ScreenOverlayGraphicsItem(screenOverlay);
455 m_screenOverlays.push_back(screenItem);
459 item->setStyleBuilder(m_styleBuilder);
460 item->setVisible(overlay->isGloballyVisible());
461 m_scene.addItem(item);
465void GeometryLayerPrivate::removeGraphicsItems(
const GeoDataFeature *feature)
468 if (
const auto placemark = geodata_cast<GeoDataPlacemark>(feature)) {
469 if (placemark->isGloballyVisible() && geodata_cast<GeoDataLineString>(placemark->geometry()) && placemark->hasOsmData()
470 && placemark->osmData().oid() > 0) {
471 auto &items = m_osmLineStringItems[placemark->osmData().oid()];
472 bool removed =
false;
473 for (
auto item : items) {
474 if (item->feature() == feature) {
475 items.removeOne(item);
481 updateTiledLineStrings(items);
483 m_scene.removeItem(feature);
484 }
else if (
const auto container =
dynamic_cast<const GeoDataContainer *
>(feature)) {
485 const auto features = container->featureList();
486 for (
const GeoDataFeature *child : features) {
487 removeGraphicsItems(child);
489 }
else if (geodata_cast<GeoDataScreenOverlay>(feature)) {
490 for (ScreenOverlayGraphicsItem *item : std::as_const(m_screenOverlays)) {
491 if (item->screenOverlay() == feature) {
492 m_screenOverlays.removeAll(item);
498void GeometryLayer::addPlacemarks(
const QModelIndex &parent,
int first,
int last)
500 Q_ASSERT(first < d->m_model->rowCount(parent));
501 Q_ASSERT(last < d->m_model->rowCount(parent));
502 for (
int i = first; i <= last; ++i) {
503 QModelIndex index = d->m_model->index(i, 0, parent);
505 const GeoDataObject *
object = qvariant_cast<GeoDataObject *>(index.
data(MarblePlacemarkModel::ObjectPointerRole));
507 d->createGraphicsItems(
object);
509 Q_EMIT repaintNeeded();
512void GeometryLayer::removePlacemarks(
const QModelIndex &parent,
int first,
int last)
514 Q_ASSERT(last < d->m_model->rowCount(parent));
515 bool isRepaintNeeded =
false;
516 for (
int i = first; i <= last; ++i) {
517 QModelIndex index = d->m_model->index(i, 0, parent);
519 const GeoDataObject *
object = qvariant_cast<GeoDataObject *>(index.
data(MarblePlacemarkModel::ObjectPointerRole));
520 const auto feature =
dynamic_cast<const GeoDataFeature *
>(object);
521 if (feature !=
nullptr) {
522 d->removeGraphicsItems(feature);
523 isRepaintNeeded =
true;
526 if (isRepaintNeeded) {
527 Q_EMIT repaintNeeded();
531void GeometryLayer::resetCacheData()
535 qDeleteAll(d->m_screenOverlays);
536 d->m_screenOverlays.clear();
537 d->m_osmLineStringItems.clear();
539 const GeoDataObject *
object =
static_cast<GeoDataObject *
>(d->m_model->index(0, 0,
QModelIndex()).internalPointer());
540 if (
object && object->parent()) {
541 d->createGraphicsItems(object->parent());
543 Q_EMIT repaintNeeded();
546void GeometryLayer::setTileLevel(
int tileLevel)
548 d->m_tileLevel = tileLevel;
554 auto const renderOrder = d->m_styleBuilder->renderOrder();
557 for (
int i = renderOrder.
size() - 1; i >= 0; --i) {
558 if (renderOrder[i].endsWith(label)) {
561 auto &layerItems = d->m_cachedPaintFragments[renderOrder[i]];
562 for (
auto j = layerItems.size() - 1; j >= 0; --j) {
563 auto const &layerItem = layerItems[j];
565 if (layerItem->contains(curpos, viewport)) {
566 result << layerItem->feature();
568 checked << layerItem;
576void GeometryLayer::highlightRouteRelation(qint64 osmId,
bool enabled)
579 d->m_highlightedRouteRelations << osmId;
581 d->m_highlightedRouteRelations.remove(osmId);
583 d->updateRelationVisibility();
586void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes)
588 if (relationTypes != d->m_visibleRelationTypes) {
589 d->m_visibleRelationTypes = relationTypes;
590 d->updateRelationVisibility();
594void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
596 GeoDataCoordinates clickedPoint(lon, lat, 0, unit);
599 for (
int i = 0; i < d->m_model->rowCount(); ++i) {
600 QVariant const data = d->m_model->
data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
601 auto object = qvariant_cast<GeoDataObject *>(data);
603 if (
const auto doc = geodata_cast<GeoDataDocument>(
object)) {
604 bool isHighlight =
false;
606 const auto styles = doc->styleMaps();
607 for (
const GeoDataStyleMap &styleMap : styles) {
608 if (styleMap.contains(QStringLiteral(
"highlight"))) {
625 for (; iter !=
end; ++iter) {
626 if (
auto placemark = geodata_cast<GeoDataPlacemark>(*iter)) {
627 auto polygon =
dynamic_cast<GeoDataPolygon *
>(placemark->geometry());
628 if (polygon && polygon->contains(clickedPoint)) {
632 if (
auto linearRing = geodata_cast<GeoDataLinearRing>(placemark->geometry())) {
633 if (linearRing->contains(clickedPoint)) {
638 if (
auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) {
642 for (; multiIter != multiEnd; ++multiIter) {
643 auto poly =
dynamic_cast<GeoDataPolygon *
>(*multiIter);
645 if (poly && poly->contains(clickedPoint)) {
650 if (
auto linearRing = geodata_cast<GeoDataLinearRing>(*multiIter)) {
651 if (linearRing->contains(clickedPoint)) {
664 Q_EMIT highlightedPlacemarksChanged(selectedPlacemarks);
667void GeometryLayer::setLevelTagDebugModeEnabled(
bool enabled)
669 if (d->m_levelTagDebugModeEnabled != enabled) {
670 d->m_levelTagDebugModeEnabled = enabled;
671 Q_EMIT repaintNeeded();
675bool GeometryLayer::levelTagDebugModeEnabled()
const
677 return d->m_levelTagDebugModeEnabled;
680void GeometryLayer::setDebugLevelTag(
int level)
682 if (d->m_debugLevelTag != level) {
683 d->m_debugLevelTag =
level;
684 Q_EMIT repaintNeeded();
688int GeometryLayer::debugLevelTag()
const
690 return d->m_debugLevelTag;
695#include "moc_GeometryLayer.cpp"
This file contains the headers for ViewportParams.
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QStringView level(QStringView ifopt)
std::vector< Feature > features(QStringView coachNumber, QStringView coachClassification)
QString label(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
QDateTime currentDateTime()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool isEmpty() const const
void push_back(parameter_type value)
qsizetype size() const const
QVariant data(int role) const const
bool isValid() const const
bool contains(const QSet< T > &other) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)