Marble

GeoGraphicsScene.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Konstantin Oblaukhov <oblaukhov.konstantin@gmail.com>
4//
5
6#include "GeoGraphicsScene.h"
7
8#include "GeoDataFeature.h"
9#include "GeoDataLatLonAltBox.h"
10#include "GeoDataStyle.h"
11#include "GeoDataStyleMap.h"
12#include "GeoDataPlacemark.h"
13#include "GeoDataDocument.h"
14#include "GeoGraphicsItem.h"
15#include "TileId.h"
16#include "TileCoordsPyramid.h"
17#include "MarbleDebug.h"
18
19#include <QMap>
20#include <QRect>
21
22namespace Marble
23{
24
25class GeoGraphicsScenePrivate
26{
27public:
28 GeoGraphicsScene *q;
29 explicit GeoGraphicsScenePrivate(GeoGraphicsScene *parent) :
30 q(parent)
31 {
32 }
33
34 ~GeoGraphicsScenePrivate()
35 {
36 q->clear();
37 }
38
41 QMultiHash<const GeoDataFeature*, TileId> m_features; // multi hash because multi track and multi geometry insert multiple items
42
43 // Stores the items which have been clicked;
44 QList<GeoGraphicsItem*> m_selectedItems;
45
46 static GeoDataStyle::Ptr highlightStyle(const GeoDataDocument *document, const GeoDataStyleMap &styleMap);
47
48 void selectItem( GeoGraphicsItem *item );
49 static void applyHighlightStyle(GeoGraphicsItem *item, const GeoDataStyle::Ptr &style);
50};
51
52GeoDataStyle::Ptr GeoGraphicsScenePrivate::highlightStyle( const GeoDataDocument *document,
53 const GeoDataStyleMap &styleMap )
54{
55 // @todo Consider QUrl parsing when external styles are suppported
56 QString highlightStyleId = styleMap.value(QStringLiteral("highlight"));
57 highlightStyleId.remove(QLatin1Char('#'));
58 if ( !highlightStyleId.isEmpty() ) {
59 GeoDataStyle::Ptr highlightStyle(new GeoDataStyle( *document->style(highlightStyleId) ));
60 return highlightStyle;
61 }
62 else {
63 return GeoDataStyle::Ptr();
64 }
65}
66
67void GeoGraphicsScenePrivate::selectItem( GeoGraphicsItem* item )
68{
69 m_selectedItems.append( item );
70}
71
72void GeoGraphicsScenePrivate::applyHighlightStyle(GeoGraphicsItem* item, const GeoDataStyle::Ptr &highlightStyle )
73{
74 item->setHighlightStyle( highlightStyle );
75 item->setHighlighted( true );
76}
77
79 QObject( parent ),
80 d( new GeoGraphicsScenePrivate(this) )
81{
82
83}
84
85GeoGraphicsScene::~GeoGraphicsScene()
86{
87 delete d;
88}
89
91{
92 if ( box.west() > box.east() ) {
93 // Handle boxes crossing the IDL by splitting it into two separate boxes
95 left.setWest( -M_PI );
96 left.setEast( box.east() );
97 left.setNorth( box.north() );
98 left.setSouth( box.south() );
99
100 GeoDataLatLonBox right;
101 right.setWest( box.west() );
102 right.setEast( M_PI );
103 right.setNorth( box.north() );
104 right.setSouth( box.south() );
105
106 return items(left, zoomLevel) + items(right, zoomLevel);
107 }
108
110 QRect rect;
111 qreal north, south, east, west;
112 box.boundaries( north, south, east, west );
113 TileId key;
114
115 key = TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel );
116 rect.setLeft( key.x() );
117 rect.setTop( key.y() );
118
119 key = TileId::fromCoordinates( GeoDataCoordinates(east, south, 0), zoomLevel );
120 rect.setRight( key.x() );
121 rect.setBottom( key.y() );
122
123 TileCoordsPyramid pyramid( 0, zoomLevel );
124 pyramid.setBottomLevelCoords( rect );
125
126 for ( int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) {
127 QRect const coords = pyramid.coords( level );
128 int x1, y1, x2, y2;
129 coords.getCoords( &x1, &y1, &x2, &y2 );
130 for ( int x = x1; x <= x2; ++x ) {
131 bool const isBorderX = x == x1 || x == x2;
132 for ( int y = y1; y <= y2; ++y ) {
133 bool const isBorder = isBorderX || y == y1 || y == y2;
134 const TileId tileId = TileId( 0, level, x, y );
135 for(GeoGraphicsItem *object: d->m_tiledItems.value( tileId )) {
136 if (object->minZoomLevel() <= zoomLevel && object->visible()) {
137 if (!isBorder || object->latLonAltBox().intersects(box)) {
138 result.push_back(object);
139 }
140 }
141 }
142 }
143 }
144 }
145
146 return result;
147}
148
150{
151 return d->m_selectedItems;
152}
153
154void GeoGraphicsScene::resetStyle()
155{
156 for (auto const & items: d->m_tiledItems) {
157 for (auto item: items) {
158 item->resetStyle();
159 }
160 }
161 emit repaintNeeded();
162}
163
165{
166 /**
167 * First set the items, which were selected previously, to
168 * use normal style
169 */
170 for ( GeoGraphicsItem *item: d->m_selectedItems ) {
171 item->setHighlighted( false );
172 }
173
174 // Also clear the list to store the new selected items
175 d->m_selectedItems.clear();
176
177 /**
178 * Process the placemark. which were under mouse
179 * while clicking, and update corresponding graphics
180 * items to use highlight style
181 */
182 for( const GeoDataPlacemark *placemark: selectedPlacemarks ) {
183 for (auto tileIter = d->m_features.find(placemark); tileIter != d->m_features.end() && tileIter.key() == placemark; ++tileIter) {
184 auto const & clickedItemsList = d->m_tiledItems.values(*tileIter);
185 for (auto const & clickedItems: clickedItemsList) { //iterate through FeatureItemMap clickedItems (QHash)
186 for (auto iter = clickedItems.find(placemark); iter != clickedItems.end(); ++iter) {
187 if ( iter.key() == placemark ) {
188 const GeoDataObject *parent = placemark->parent();
189 if ( parent ) {
190 auto item = *iter;
192 QString styleUrl = placemark->styleUrl();
193 styleUrl.remove(QLatin1Char('#'));
194 if ( !styleUrl.isEmpty() ) {
195 GeoDataStyleMap const &styleMap = doc->styleMap( styleUrl );
196 GeoDataStyle::Ptr style = d->highlightStyle( doc, styleMap );
197 if ( style ) {
198 d->selectItem( item );
199 d->applyHighlightStyle( item, style );
200 }
201 }
202
203 /**
204 * If a placemark is using an inline style instead of a shared
205 * style ( e.g in case when theme file specifies the colorMap
206 * attribute ) then highlight it if any of the style maps have a
207 * highlight styleId
208 */
209 else {
210 for ( const GeoDataStyleMap &styleMap: doc->styleMaps() ) {
211 GeoDataStyle::Ptr style = d->highlightStyle( doc, styleMap );
212 if ( style ) {
213 d->selectItem( item );
214 d->applyHighlightStyle( item, style );
215 break;
216 }
217 }
218 }
219 }
220 }
221 }
222 }
223 }
224 }
225 }
226 emit repaintNeeded();
227}
228
230{
231 for (auto tileIter = d->m_features.find(feature), end = d->m_features.end(); tileIter != end && tileIter.key() == feature;) {
232 auto & tileList = d->m_tiledItems[*tileIter];
233 auto iter = tileList.find(feature);
234 if (iter != tileList.end()) {
235 auto item = iter.value();
236 tileIter = d->m_features.erase(tileIter);
237 tileList.erase(iter);
238 delete item;
239 } else {
240 ++tileIter;
241 }
242 }
243}
244
246{
247 for(auto const &list: d->m_tiledItems.values()) {
248 qDeleteAll(list.values());
249 }
250 d->m_tiledItems.clear();
251 d->m_features.clear();
252}
253
254void GeoGraphicsScene::addItem( GeoGraphicsItem* item )
255{
256 // Select zoom level so that the object fit in single tile
257 int zoomLevel;
258 qreal north, south, east, west;
259 item->latLonAltBox().boundaries( north, south, east, west );
260 for(zoomLevel = item->minZoomLevel(); zoomLevel >= 0; zoomLevel--)
261 {
262 if( TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel ) ==
263 TileId::fromCoordinates( GeoDataCoordinates(east, south, 0), zoomLevel ) )
264 break;
265 }
266
267 const TileId key = TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel ); // same as GeoDataCoordinates(east, south, 0), see above
268
269 auto & tileList = d->m_tiledItems[key];
270 auto feature = item->feature();
271 tileList.insertMulti(feature, item);
272 d->m_features.insert(feature, key );
273}
274
275}
276
277#include "moc_GeoGraphicsScene.cpp"
A 3d point representation.
A container for Features, Styles and in the future Schemas.
A base class for all geodata features.
A class that defines a 2D bounding box for geographic data.
A base class for all geodata objects.
a class representing a point of interest on the map
a class to map different styles to one style
void clear()
Remove all items from the GeoGraphicsScene.
QList< GeoGraphicsItem * > items(const GeoDataLatLonBox &box, int maxZoomLevel) const
Get the list of items in the specified Box.
QList< GeoGraphicsItem * > selectedItems() const
Get the list of items which belong to a placemark that has been clicked.
void removeItem(const GeoDataFeature *feature)
Remove all concerned items from the GeoGraphicsScene Removes all items which are associated with obje...
void applyHighlight(const QVector< GeoDataPlacemark * > &)
void addItem(GeoGraphicsItem *item)
Add an item to the GeoGraphicsScene Adds the item item to the GeoGraphicsScene.
GeoGraphicsScene(QObject *parent=nullptr)
Creates a new instance of GeoGraphicsScene.
Binds a QML item to a specific geodetic location in screen coordinates.
void append(QList< T > &&value)
void clear()
void push_back(parameter_type value)
QObject * parent() const const
T qobject_cast(QObject *object)
void getCoords(int *x1, int *y1, int *x2, int *y2) const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
bool isEmpty() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.