9#include "GeometryLayer.h"
12#include "GeoDataLatLonAltBox.h"
13#include "GeoDataDocument.h"
14#include "GeoDataFolder.h"
15#include "GeoDataLineStyle.h"
16#include "GeoDataMultiTrack.h"
17#include "GeoDataObject.h"
18#include "GeoDataPlacemark.h"
19#include "GeoDataLinearRing.h"
20#include "GeoDataMultiGeometry.h"
21#include "GeoDataPolygon.h"
22#include "GeoDataBuilding.h"
23#include "GeoDataPolyStyle.h"
24#include "GeoDataStyle.h"
25#include "GeoDataIconStyle.h"
26#include "GeoDataStyleMap.h"
27#include "GeoDataTrack.h"
28#include "GeoDataFeature.h"
29#include "MarbleDebug.h"
30#include "GeoPainter.h"
32#include "RenderState.h"
33#include "GeoGraphicsScene.h"
34#include "GeoGraphicsItem.h"
35#include "GeoLineStringGraphicsItem.h"
36#include "GeoPolygonGraphicsItem.h"
37#include "GeoTrackGraphicsItem.h"
38#include "GeoDataPhotoOverlay.h"
39#include "GeoDataScreenOverlay.h"
40#include "GeoPhotoGraphicsItem.h"
41#include "ScreenOverlayGraphicsItem.h"
43#include "MarbleGraphicsItem.h"
44#include "MarblePlacemarkModel.h"
45#include "GeoDataTreeModel.h"
46#include <OsmPlacemarkData.h>
47#include "StyleBuilder.h"
48#include "AbstractGeoPolygonGraphicsItem.h"
52#include <QAbstractItemModel>
57class GeometryLayerPrivate
65 struct PaintFragments {
74 explicit GeometryLayerPrivate(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder);
76 void createGraphicsItems(
const GeoDataObject *
object);
77 void createGraphicsItems(
const GeoDataObject *
object, FeatureRelationHash &relations);
78 void createGraphicsItemFromGeometry(
const GeoDataGeometry *
object,
const GeoDataPlacemark *placemark,
const Relations &relations);
79 void createGraphicsItemFromOverlay(
const GeoDataOverlay *overlay);
80 void removeGraphicsItems(
const GeoDataFeature *feature);
81 void updateTiledLineStrings(
const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem);
82 static void updateTiledLineStrings(OsmLineStringItems &lineStringItems);
84 bool showRelation(
const GeoDataRelation* relation)
const;
85 void updateRelationVisibility();
88 const StyleBuilder *
const m_styleBuilder;
89 GeoGraphicsScene m_scene;
95 GeoGraphicsItem* m_lastFeatureAt;
98 int m_cachedItemCount;
100 typedef QPair<QString, GeoGraphicsItem*> LayerItem;
103 GeoDataLatLonBox m_cachedLatLonBox;
105 GeoDataRelation::RelationTypes m_visibleRelationTypes;
106 bool m_levelTagDebugModeEnabled;
110GeometryLayerPrivate::GeometryLayerPrivate(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder) :
112 m_styleBuilder(styleBuilder),
114 m_lastFeatureAt(nullptr),
116 m_cachedItemCount(0),
117 m_visibleRelationTypes(GeoDataRelation::RouteFerry),
118 m_levelTagDebugModeEnabled(false),
123void GeometryLayerPrivate::createGraphicsItems(
const GeoDataObject *
object)
125 FeatureRelationHash noRelations;
126 createGraphicsItems(
object, noRelations);
129GeometryLayer::GeometryLayer(
const QAbstractItemModel *model,
const StyleBuilder *styleBuilder) :
130 d(new GeometryLayerPrivate(model, styleBuilder))
132 const GeoDataObject *
object =
static_cast<GeoDataObject*
>(d->m_model->index(0, 0,
QModelIndex()).internalPointer());
133 if (
object && object->parent()) {
134 d->createGraphicsItems(object->parent());
138 this, SLOT(resetCacheData()));
142 this, SLOT(removePlacemarks(
QModelIndex,
int,
int)));
143 connect(model, SIGNAL(modelReset()),
144 this, SLOT(resetCacheData()));
147 connect(&d->m_scene, SIGNAL(repaintNeeded()),
148 this, SIGNAL(repaintNeeded()));
151GeometryLayer::~GeometryLayer()
158 return QStringList(QStringLiteral(
"HOVERS_ABOVE_SURFACE"));
162bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport,
163 const QString& renderPos, GeoSceneLayer * layer)
170 auto const & box = viewport->viewLatLonAltBox();
171 bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05);
173 if (d->m_cachedLatLonBox.isEmpty() || !isEqual) {
179 if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) {
186 const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel());
187 auto const items = d->m_scene.items(box, maxZoomLevel);
188 d->m_cachedLatLonBox = box;
189 d->m_cachedDateTime = now;
191 d->m_cachedItemCount = items.size();
192 d->m_cachedDefaultLayer.clear();
193 d->m_cachedPaintFragments.clear();
195 const QStringList &renderOrder = d->m_styleBuilder->renderOrder();
197 for (GeoGraphicsItem* item: items) {
200 mDebug() << item <<
" provides no paint layers, so I force one onto it.";
203 for (
const auto &layer: paintLayers) {
204 if (knownLayers.contains(layer)) {
205 GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer];
206 double const zValue = item->zValue();
209 fragments.null << item;
211 }
else if (zValue < 0.0) {
212 fragments.negative << item;
215 fragments.positive << item;
219 d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item);
221 if (!missingLayers.
contains(layer)) {
222 mDebug() <<
"Missing layer " << layer <<
", in render order, will render it on top";
223 missingLayers << layer;
229 for (
const QString &layer: d->m_styleBuilder->renderOrder()) {
230 GeometryLayerPrivate::PaintFragments & layerItems = paintFragments[layer];
231 std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan);
234 std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan);
235 std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan);
236 auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size();
237 d->m_cachedPaintFragments[layer].reserve(count);
238 d->m_cachedPaintFragments[layer] << layerItems.negative;
239 d->m_cachedPaintFragments[layer] << layerItems.null;
240 d->m_cachedPaintFragments[layer] << layerItems.positive;
244 for (
const QString &layer: d->m_styleBuilder->renderOrder()) {
245 auto & layerItems = d->m_cachedPaintFragments[layer];
246 AbstractGeoPolygonGraphicsItem::s_previousStyle =
nullptr;
247 GeoLineStringGraphicsItem::s_previousStyle =
nullptr;
248 for (
auto item: layerItems) {
249 if (d->m_levelTagDebugModeEnabled) {
250 if (
const auto placemark = geodata_cast<GeoDataPlacemark>(item->feature())) {
251 if (placemark->hasOsmData()) {
253 if (tagIter != placemark->osmData().tagsEnd()) {
254 const int val = tagIter.value().toInt();
255 if (val != d->m_debugLevelTag) {
262 item->paint(painter, viewport, layer, d->m_tileLevel);
266 for (
const auto & item: d->m_cachedDefaultLayer) {
267 item.second->paint(painter, viewport, item.first, d->m_tileLevel);
270 for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) {
271 item->paintEvent(painter, viewport);
275 d->m_runtimeTrace = QStringLiteral(
"Geometries: %1 Zoom: %2")
276 .arg(d->m_cachedItemCount)
277 .arg(d->m_tileLevel);
281RenderState GeometryLayer::renderState()
const
283 return RenderState(QStringLiteral(
"GeoGraphicsScene"));
286QString GeometryLayer::runtimeTrace()
const
288 return d->m_runtimeTrace;
291bool GeometryLayer::hasFeatureAt(
const QPoint &curpos,
const ViewportParams *viewport)
293 if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) {
297 auto const renderOrder = d->m_styleBuilder->renderOrder();
298 for (
int i = renderOrder.
size() - 1; i >= 0; --i) {
299 auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
300 for (
auto item : layerItems) {
301 if (item->contains(curpos, viewport)) {
302 d->m_lastFeatureAt = item;
311void GeometryLayerPrivate::createGraphicsItems(
const GeoDataObject *
object, FeatureRelationHash &relations)
314 if (
auto document = geodata_cast<GeoDataDocument>(
object)) {
315 for (
auto feature: document->featureList()) {
316 if (
auto relation = geodata_cast<GeoDataRelation>(feature)) {
317 relation->setVisible(showRelation(relation));
318 for (
auto member: relation->members()) {
319 relations[member] << relation;
324 if (
auto placemark = geodata_cast<GeoDataPlacemark>(
object)) {
325 createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark));
326 }
else if (
const GeoDataOverlay* overlay =
dynamic_cast<const GeoDataOverlay*
>(
object)) {
327 createGraphicsItemFromOverlay(overlay);
331 if (
const GeoDataContainer *container =
dynamic_cast<const GeoDataContainer*
>(
object)) {
332 int rowCount = container->size();
333 for (
int row = 0; row < rowCount; ++row) {
334 createGraphicsItems(container->child(row), relations);
339void GeometryLayerPrivate::updateTiledLineStrings(
const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem)
341 if (!placemark->hasOsmData()) {
344 qint64
const osmId = placemark->osmData().oid();
348 auto & lineStringItems = m_osmLineStringItems[osmId];
349 lineStringItems << lineStringItem;
350 updateTiledLineStrings(lineStringItems);
353void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems)
355 GeoDataLineString merged;
356 if (lineStringItems.size() > 1) {
358 for (
auto item : lineStringItems) {
359 lineStrings << item->lineString();
361 merged = GeoLineStringGraphicsItem::merge(lineStrings);
367 for (
auto item : lineStringItems) {
368 item->setVisible(visible);
370 item->setMergedLineString(merged);
371 visible = merged.isEmpty();
376void GeometryLayerPrivate::clearCache()
378 m_lastFeatureAt =
nullptr;
381 m_cachedItemCount = 0;
382 m_cachedPaintFragments.clear();
383 m_cachedDefaultLayer.clear();
384 m_cachedLatLonBox = GeoDataLatLonBox();
387inline bool GeometryLayerPrivate::showRelation(
const GeoDataRelation *relation)
const
389 return (m_visibleRelationTypes.testFlag(relation->relationType())
390 || m_highlightedRouteRelations.contains(relation->osmData().oid()));
393void GeometryLayerPrivate::updateRelationVisibility()
395 for (
int i = 0; i < m_model->rowCount(); ++i) {
396 QVariant const data = m_model->
data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
397 GeoDataObject *
object = qvariant_cast<GeoDataObject*> (data);
398 if (
auto doc = geodata_cast<GeoDataDocument>(
object)) {
399 for (
auto feature: doc->featureList()) {
400 if (
auto relation = geodata_cast<GeoDataRelation>(feature)) {
401 relation->setVisible(showRelation(relation));
406 m_scene.resetStyle();
409void GeometryLayerPrivate::createGraphicsItemFromGeometry(
const GeoDataGeometry*
object,
const GeoDataPlacemark *placemark,
const Relations &relations)
411 if (!placemark->isGloballyVisible()) {
415 GeoGraphicsItem *item =
nullptr;
416 if (
const auto line = geodata_cast<GeoDataLineString>(
object)) {
417 auto lineStringItem =
new GeoLineStringGraphicsItem(placemark, line);
418 item = lineStringItem;
419 updateTiledLineStrings(placemark, lineStringItem);
420 }
else if (
const auto ring = geodata_cast<GeoDataLinearRing>(
object)) {
421 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring);
422 }
else if (
const auto poly = geodata_cast<GeoDataPolygon>(
object)) {
423 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly);
424 if (item->zValue() == 0) {
425 item->setZValue(poly->renderOrder());
427 }
else if (
const auto building = geodata_cast<GeoDataBuilding>(
object)) {
428 item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building);
429 }
else if (
const auto multigeo = geodata_cast<GeoDataMultiGeometry>(
object)) {
430 int rowCount = multigeo->size();
431 for (
int row = 0; row < rowCount; ++row) {
432 createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations);
434 }
else if (
const auto multitrack = geodata_cast<GeoDataMultiTrack>(
object)) {
435 int rowCount = multitrack->size();
436 for (
int row = 0; row < rowCount; ++row) {
437 createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations);
439 }
else if (
const auto track = geodata_cast<GeoDataTrack>(
object)) {
440 item =
new GeoTrackGraphicsItem(placemark, track);
445 item->setRelations(relations);
446 item->setStyleBuilder(m_styleBuilder);
447 item->setVisible(item->visible() && placemark->isGloballyVisible());
448 item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark));
449 m_scene.addItem(item);
452void GeometryLayerPrivate::createGraphicsItemFromOverlay(
const GeoDataOverlay *overlay)
454 if (!overlay->isGloballyVisible()) {
458 GeoGraphicsItem* item =
nullptr;
459 if (
const auto photoOverlay = geodata_cast<GeoDataPhotoOverlay>(overlay)) {
460 GeoPhotoGraphicsItem *photoItem =
new GeoPhotoGraphicsItem(overlay);
461 photoItem->setPoint(photoOverlay->point());
463 }
else if (
const auto screenOverlay = geodata_cast<GeoDataScreenOverlay>(overlay)) {
464 ScreenOverlayGraphicsItem *screenItem =
new ScreenOverlayGraphicsItem(screenOverlay);
465 m_screenOverlays.push_back(screenItem);
469 item->setStyleBuilder(m_styleBuilder);
470 item->setVisible(overlay->isGloballyVisible());
471 m_scene.addItem(item);
475void GeometryLayerPrivate::removeGraphicsItems(
const GeoDataFeature *feature)
478 if (
const auto placemark = geodata_cast<GeoDataPlacemark>(feature)) {
479 if (placemark->isGloballyVisible() &&
480 geodata_cast<GeoDataLineString>(placemark->geometry()) &&
481 placemark->hasOsmData() &&
482 placemark->osmData().oid() > 0) {
483 auto & items = m_osmLineStringItems[placemark->osmData().oid()];
484 bool removed =
false;
485 for (
auto item : items) {
486 if (item->feature() == feature) {
487 items.removeOne(item);
493 updateTiledLineStrings(items);
495 m_scene.removeItem(feature);
496 }
else if (
const auto container =
dynamic_cast<const GeoDataContainer*
>(feature)) {
497 for (
const GeoDataFeature *child: container->featureList()) {
498 removeGraphicsItems(child);
500 }
else if (geodata_cast<GeoDataScreenOverlay>(feature)) {
501 for (ScreenOverlayGraphicsItem *item: m_screenOverlays) {
502 if (item->screenOverlay() == feature) {
503 m_screenOverlays.removeAll(item);
509void GeometryLayer::addPlacemarks(
const QModelIndex& parent,
int first,
int last)
511 Q_ASSERT(first < d->m_model->rowCount(parent));
512 Q_ASSERT(last < d->m_model->rowCount(parent));
513 for (
int i = first; i <= last; ++i) {
514 QModelIndex index = d->m_model->index(i, 0, parent);
516 const GeoDataObject *
object = qvariant_cast<GeoDataObject*>(index.
data(MarblePlacemarkModel::ObjectPointerRole));
518 d->createGraphicsItems(
object);
520 emit repaintNeeded();
524void GeometryLayer::removePlacemarks(
const QModelIndex& parent,
int first,
int last)
526 Q_ASSERT(last < d->m_model->rowCount(parent));
527 bool isRepaintNeeded =
false;
528 for (
int i = first; i <= last; ++i) {
529 QModelIndex index = d->m_model->index(i, 0, parent);
531 const GeoDataObject *
object = qvariant_cast<GeoDataObject*>(index.
data(MarblePlacemarkModel::ObjectPointerRole));
532 const GeoDataFeature *feature =
dynamic_cast<const GeoDataFeature*
>(object);
533 if (feature !=
nullptr) {
534 d->removeGraphicsItems(feature);
535 isRepaintNeeded =
true;
538 if (isRepaintNeeded) {
539 emit repaintNeeded();
544void GeometryLayer::resetCacheData()
548 qDeleteAll(d->m_screenOverlays);
549 d->m_screenOverlays.clear();
550 d->m_osmLineStringItems.clear();
552 const GeoDataObject *
object =
static_cast<GeoDataObject*
>(d->m_model->index(0, 0,
QModelIndex()).internalPointer());
553 if (
object && object->parent()) {
554 d->createGraphicsItems(object->parent());
556 emit repaintNeeded();
559void GeometryLayer::setTileLevel(
int tileLevel)
561 d->m_tileLevel = tileLevel;
567 auto const renderOrder = d->m_styleBuilder->renderOrder();
570 for (
int i = renderOrder.
size()-1; i >= 0; --i) {
571 if (renderOrder[i].endsWith(label)) {
574 auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
575 for (
auto j = layerItems.size()-1; j >= 0; --j) {
576 auto const & layerItem = layerItems[j];
578 if (layerItem->contains(curpos, viewport)) {
579 result << layerItem->feature();
581 checked << layerItem;
589void GeometryLayer::highlightRouteRelation(qint64 osmId,
bool enabled)
592 d->m_highlightedRouteRelations << osmId;
594 d->m_highlightedRouteRelations.remove(osmId);
596 d->updateRelationVisibility();
599void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes)
601 if (relationTypes != d->m_visibleRelationTypes) {
602 d->m_visibleRelationTypes = relationTypes;
603 d->updateRelationVisibility();
607void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
609 GeoDataCoordinates clickedPoint(lon, lat, 0, unit);
612 for (
int i = 0; i < d->m_model->rowCount(); ++i) {
613 QVariant const data = d->m_model->
data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
614 GeoDataObject *
object = qvariant_cast<GeoDataObject*> (data);
616 if (
const auto doc = geodata_cast<GeoDataDocument>(
object)) {
617 bool isHighlight =
false;
619 for (
const GeoDataStyleMap &styleMap: doc->styleMaps()) {
620 if (styleMap.contains(QStringLiteral(
"highlight"))) {
637 for (; iter !=
end; ++iter) {
638 if (
auto placemark = geodata_cast<GeoDataPlacemark>(*iter)) {
639 GeoDataPolygon *polygon =
dynamic_cast<GeoDataPolygon*
>(placemark->geometry());
641 polygon->contains(clickedPoint)) {
645 if (
auto linearRing = geodata_cast<GeoDataLinearRing>(placemark->geometry())) {
646 if (linearRing->contains(clickedPoint)) {
651 if (
auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) {
655 for (; multiIter != multiEnd; ++multiIter) {
656 GeoDataPolygon *poly =
dynamic_cast<GeoDataPolygon*
>(*multiIter);
659 poly->contains(clickedPoint)) {
664 if (
auto linearRing = geodata_cast<GeoDataLinearRing>(*multiIter)) {
665 if (linearRing->contains(clickedPoint)) {
678 emit highlightedPlacemarksChanged(selectedPlacemarks);
681void GeometryLayer::setLevelTagDebugModeEnabled(
bool enabled)
683 if (d->m_levelTagDebugModeEnabled != enabled) {
684 d->m_levelTagDebugModeEnabled = enabled;
685 emit repaintNeeded();
689bool GeometryLayer::levelTagDebugModeEnabled()
const
691 return d->m_levelTagDebugModeEnabled;
694void GeometryLayer::setDebugLevelTag(
int level)
696 if (d->m_debugLevelTag != level) {
697 d->m_debugLevelTag =
level;
698 emit repaintNeeded();
702int GeometryLayer::debugLevelTag()
const
704 return d->m_debugLevelTag;
709#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)
QString label(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
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)