Marble

GeoPolyline.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2019 Torsten Rahn <rahn@kde.org>
4//
5
6#include "GeoPolyline.h"
7#include "MarbleQuickItem.h"
8
9#include <QPolygonF>
10#include <QSGFlatColorMaterial>
11#include <QSGGeometryNode>
12#include <QSGSimpleTextureNode>
13#include <QSGTexture>
14#include <QtMath>
15
17
18namespace Marble
19{
20GeoPolyline::GeoPolyline(QQuickItem *parent)
21 : QQuickItem(parent)
22 , m_map(nullptr)
23 , m_observable(false)
24 , m_lineColor(Qt::black)
25 , m_lineWidth(1)
26 , m_tessellate(true)
27 , m_clipScreenCoordinates(true)
28{
29 setFlag(ItemHasContents, true);
30}
31
32MarbleQuickItem *GeoPolyline::map() const
33{
34 return m_map;
35}
36
37void GeoPolyline::setMap(MarbleQuickItem *map)
38{
39 if (m_map == map)
40 return;
41
42 m_map = map;
43
44 connect(m_map, &MarbleQuickItem::visibleLatLonAltBoxChanged, this, &GeoPolyline::updateScreenPositions);
45 Q_EMIT mapChanged(m_map);
46}
47
48void GeoPolyline::updateScreenPositions()
49{
50 GeoDataLineString lineString(m_lineString);
51
52 if (m_map) {
53 QPolygonF displayPolygon;
54 displayPolygon << QPointF(-10, -10) << QPointF(m_map->mapWidth() + 10, -10) << QPointF(m_map->mapWidth() + 10, m_map->mapHeight() + 10)
55 << QPointF(-10, m_map->mapHeight() + 10);
56 m_screenPolygons.clear();
57 QList<QPolygonF *> fullScreenPolygons;
58 bool success = m_map->screenCoordinatesFromGeoDataLineString(lineString, fullScreenPolygons);
59 if (m_clipScreenCoordinates) {
60 for (auto reducedPolygon : std::as_const(fullScreenPolygons)) {
61 m_screenPolygons << reducedPolygon->intersected(displayPolygon);
62 }
63 } else {
64 for (auto eachPolygon : std::as_const(fullScreenPolygons)) {
65 m_screenPolygons << *eachPolygon;
66 }
67 }
68
69 qDeleteAll(fullScreenPolygons);
70
71 QVariantList previousScreenCoordinates;
72 previousScreenCoordinates = m_screenCoordinates;
73 m_screenCoordinates.clear();
74 if (success) {
75 int i = 0;
76 for (const auto &screenPolygon : std::as_const(m_screenPolygons)) {
77 QVariantList polyline;
78 for (auto node : screenPolygon) {
79 QVariantMap vmap;
80 vmap[QStringLiteral("x")] = node.x();
81 vmap[QStringLiteral("y")] = node.y();
82 polyline.append(vmap);
83 }
84 m_screenCoordinates.insert(i, polyline);
85 ++i;
86 }
87 }
88
89 QRectF polygonBoundingRect;
90 if (m_screenPolygons.length() == 1) {
91 polygonBoundingRect = m_screenPolygons[0].boundingRect();
92 } else {
93 QPolygonF polygons;
94 for (const auto &polygon : std::as_const(m_screenPolygons)) {
95 polygons << polygon;
96 }
97 polygonBoundingRect = polygons.boundingRect();
98 }
99 setX(polygonBoundingRect.x());
100 setY(polygonBoundingRect.y());
101 setWidth(polygonBoundingRect.width());
102 setHeight(polygonBoundingRect.height());
103
104 if (m_screenCoordinates != previousScreenCoordinates) {
105 Q_EMIT screenCoordinatesChanged();
106 }
107 Q_EMIT readonlyXChanged();
108 Q_EMIT readonlyYChanged();
109 Q_EMIT readonlyWidthChanged();
110 Q_EMIT readonlyHeightChanged();
111 update();
112 }
113}
114
115bool GeoPolyline::observable() const
116{
117 return m_observable;
118}
119
120QVariantList GeoPolyline::geoCoordinates() const
121{
122 return m_geoCoordinates;
123}
124
125void GeoPolyline::setGeoCoordinates(const QVariantList &coordinates)
126{
127 m_lineString.clear();
128 m_lineString.setTessellate(m_tessellate);
129 for (auto &item : coordinates) {
130 QVariantMap map = item.toMap();
131 m_lineString << GeoDataCoordinates(map[QStringLiteral("lon")].toReal(),
132 map[QStringLiteral("lat")].toReal(),
133 map[QStringLiteral("alt")].toReal(),
134 GeoDataCoordinates::Degree);
135 }
136
137 m_geoCoordinates = coordinates;
138 Q_EMIT geoCoordinatesChanged();
139 updateScreenPositions();
140}
141
142QVariantList GeoPolyline::screenCoordinates() const
143{
144 return m_screenCoordinates;
145}
146
147QColor GeoPolyline::lineColor() const
148{
149 return m_lineColor;
150}
151
152qreal GeoPolyline::lineWidth() const
153{
154 return m_lineWidth;
155}
156
157void GeoPolyline::setLineColor(const QColor &lineColor)
158{
159 if (m_lineColor == lineColor)
160 return;
161
162 m_lineColor = lineColor;
163 Q_EMIT lineColorChanged(m_lineColor);
164}
165
166void GeoPolyline::setLineWidth(const qreal lineWidth)
167{
168 if (m_lineWidth == lineWidth)
169 return;
170
171 m_lineWidth = lineWidth;
172 Q_EMIT lineWidthChanged(m_lineWidth);
173}
174
175bool GeoPolyline::tessellate() const
176{
177 return m_tessellate;
178}
179
180bool GeoPolyline::clipScreenCoordinates() const
181{
182 return m_clipScreenCoordinates;
183}
184
185void GeoPolyline::setTessellate(bool tessellate)
186{
187 if (m_tessellate == tessellate)
188 return;
189
190 m_tessellate = tessellate;
191 Q_EMIT tessellateChanged(m_tessellate);
192}
193
194void GeoPolyline::setClipScreenCoordinates(bool clipped)
195{
196 if (m_clipScreenCoordinates == clipped)
197 return;
198
199 m_clipScreenCoordinates = clipped;
200 Q_EMIT clipScreenCoordinatesChanged(m_clipScreenCoordinates);
201}
202
203qreal GeoPolyline::readonlyX() const
204{
205 return x();
206}
207
208qreal GeoPolyline::readonlyY() const
209{
210 return y();
211}
212
213qreal GeoPolyline::readonlyWidth() const
214{
215 return width();
216}
217
218qreal GeoPolyline::readonlyHeight() const
219{
220 return height();
221}
222
223QSGNode *GeoPolyline::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
224{
225 qreal const halfWidth = m_lineWidth;
226
227 delete oldNode;
228 oldNode = new QSGNode;
229
230 if (m_screenPolygons.isEmpty())
231 return oldNode;
232
233 for (int i = 0; i < m_screenPolygons.length(); ++i) {
234 QPolygonF polygon = m_screenPolygons[i];
235 QList<QVector2D> normals;
236 int segmentCount = polygon.size() - 1;
237 normals.reserve(segmentCount);
238 for (int i = 0; i < segmentCount; ++i) {
239 normals << QVector2D(polygon.at(i + 1) - polygon.at(i)).normalized();
240 }
241 auto lineNode = new QSGGeometryNode;
242
243 auto lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount * 2);
244 lineNodeGeo->setDrawingMode(0x0005);
245 lineNodeGeo->allocate((segmentCount + 1) * 2);
246
247 auto material = new QSGFlatColorMaterial;
248 material->setColor(m_lineColor);
249
250 lineNode->setGeometry(lineNodeGeo);
251 lineNode->setFlag(QSGNode::OwnsGeometry);
252 lineNode->setMaterial(material);
253 lineNode->setFlag(QSGNode::OwnsMaterial);
254
255 auto points = lineNodeGeo->vertexDataAsPoint2D();
256 int k = -1;
257 for (int i = 0; i < segmentCount + 1; ++i) {
258 auto const &a = mapFromItem(m_map, polygon.at(i));
259 auto const &n = normals[qMin(i, segmentCount - 1)].toPointF();
260 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x());
261 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x());
262 }
263 oldNode->appendChildNode(lineNode);
264 }
265
266 return oldNode;
267}
268}
269
270#include "moc_GeoPolyline.cpp"
A 3d point representation.
void update(Part *part, const QByteArray &data, qint64 dataSize)
Binds a QML item to a specific geodetic location in screen coordinates.
const_reference at(qsizetype i) const const
void reserve(qsizetype size)
qsizetype size() const const
QRectF boundingRect() const const
qreal height() const const
qreal width() const const
qreal x() const const
qreal y() const const
void setColor(const QColor &color)
const AttributeSet & defaultAttributes_Point2D()
void appendChildNode(QSGNode *node)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVector2D normalized() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.