1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2019 Torsten Rahn <rahn@kde.org>
4 //
5 
6 #include "MarbleQuickItem.h"
7 #include "GeoPolyline.h"
8 #include "Coordinate.h"
9 
10 #include <QSGGeometryNode>
11 #include <QSGFlatColorMaterial>
12 #include <QSGSimpleTextureNode>
13 #include <QSGTexture>
14 #include <QPolygonF>
15 #include <QtMath>
16 
17 #include "MarbleGlobal.h"
18 
19 using Marble::GeoDataCoordinates;
20 using Marble::EARTH_RADIUS;
21 using Marble::DEG2RAD;
22 
23 namespace Marble
24 {
GeoPolyline(QQuickItem * parent)25     GeoPolyline::GeoPolyline(QQuickItem *parent ) :
26         QQuickItem( parent ),
27         m_map(nullptr),
28         m_observable(false),
29         m_lineColor(Qt::black),
30         m_lineWidth(1),
31         m_tessellate(true)
32     {
33         setFlag(ItemHasContents, true);
34     }
35 
map() const36     MarbleQuickItem * GeoPolyline::map() const
37     {
38         return m_map;
39     }
40 
setMap(MarbleQuickItem * map)41     void GeoPolyline::setMap(MarbleQuickItem *map)
42     {
43         if (m_map == map)
44             return;
45 
46         m_map = map;
47 
48         connect(m_map, &MarbleQuickItem::visibleLatLonAltBoxChanged, this, &GeoPolyline::updateScreenPositions);
49         emit mapChanged(m_map);
50     }
51 
updateScreenPositions()52     void GeoPolyline::updateScreenPositions() {
53         if (m_map) {
54             qDeleteAll(m_screenPolygons);
55             m_screenPolygons.clear();
56             m_map->screenCoordinatesFromGeoDataLineString(m_lineString, m_screenPolygons);
57             m_screenCoordinates.clear();
58             int i = 0;
59             for (auto polygon : m_screenPolygons) {
60                 QVariantList m_polyline;
61                 QPolygonF screenPolygon = *polygon;
62                 for (auto node : screenPolygon) {
63                     QVariantMap vmap;
64                     vmap["x"] = node.x();
65                     vmap["y"] = node.y();
66                     m_polyline.append(vmap);
67                 }
68                 m_screenCoordinates.insert(i, m_polyline);
69                 ++i;
70             }
71 
72             QRectF polygonBoundingRect;
73             if (m_screenPolygons.length() == 1) {
74                 polygonBoundingRect = m_screenPolygons[0]->boundingRect();
75             }
76             else {
77                 QPolygonF polygons;
78                 for (auto polygon : m_screenPolygons) {
79                     polygons << *polygon;
80                 }
81                 polygonBoundingRect = polygons.boundingRect();
82             }
83             setX(polygonBoundingRect.x());
84             setY(polygonBoundingRect.y());
85             setWidth(polygonBoundingRect.width());
86             setHeight(polygonBoundingRect.height());
87 
88             emit screenCoordinatesChanged();
89             emit readonlyXChanged();
90             emit readonlyYChanged();
91             emit readonlyWidthChanged();
92             emit readonlyHeightChanged();
93             update();
94         }
95     }
96 
observable() const97     bool GeoPolyline::observable() const
98     {
99         return m_observable;
100     }
101 
geoCoordinates() const102     QVariantList GeoPolyline::geoCoordinates() const
103     {
104         return m_geoCoordinates;
105     }
106 
setGeoCoordinates(const QVariantList & coordinates)107     void GeoPolyline::setGeoCoordinates(const QVariantList & coordinates)
108     {
109         m_lineString.clear();
110         m_lineString.setTessellate(m_tessellate);
111         for(auto item : coordinates) {
112             QVariantMap map = item.toMap();
113             m_lineString << GeoDataCoordinates(
114                                 map["lon"].toReal(),
115                                 map["lat"].toReal(),
116                                 map["alt"].toReal(),
117                                 GeoDataCoordinates::Degree
118                             );
119         }
120 
121         if (m_geoCoordinates == coordinates)
122             return;
123 
124         m_geoCoordinates = coordinates;
125         emit geoCoordinatesChanged();
126         updateScreenPositions();
127     }
128 
screenCoordinates() const129     QVariantList GeoPolyline::screenCoordinates() const
130     {
131         return m_screenCoordinates;
132     }
133 
lineColor() const134     QColor GeoPolyline::lineColor() const
135     {
136         return m_lineColor;
137     }
138 
lineWidth() const139     qreal GeoPolyline::lineWidth() const
140     {
141         return m_lineWidth;
142     }
143 
setLineColor(const QColor & lineColor)144     void GeoPolyline::setLineColor(const QColor& lineColor)
145     {
146         if (m_lineColor == lineColor)
147             return;
148 
149         m_lineColor = lineColor;
150         emit lineColorChanged(m_lineColor);
151     }
152 
setLineWidth(const qreal lineWidth)153     void GeoPolyline::setLineWidth(const qreal lineWidth)
154     {
155         if (m_lineWidth == lineWidth)
156             return;
157 
158         m_lineWidth = lineWidth;
159         emit lineWidthChanged(m_lineWidth);
160     }
161 
tessellate() const162     bool GeoPolyline::tessellate() const
163     {
164         return m_tessellate;
165     }
166 
setTessellate(bool tessellate)167     void GeoPolyline::setTessellate(bool tessellate)
168     {
169         if (m_tessellate == tessellate)
170             return;
171 
172         m_tessellate = tessellate;
173         emit tessellateChanged(m_tessellate);
174     }
175 
readonlyX() const176     qreal GeoPolyline::readonlyX() const
177     {
178         return x();
179     }
180 
readonlyY() const181     qreal GeoPolyline::readonlyY() const
182     {
183         return y();
184     }
185 
readonlyWidth() const186     qreal GeoPolyline::readonlyWidth() const
187     {
188         return width();
189     }
190 
readonlyHeight() const191     qreal GeoPolyline::readonlyHeight() const
192     {
193         return height();
194     }
195 
updatePaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData *)196     QSGNode *GeoPolyline::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
197     {
198         qreal const halfWidth = m_lineWidth;
199 
200         delete oldNode;
201         oldNode = new QSGNode;
202 
203         if (m_screenPolygons.isEmpty()) return oldNode;
204 
205         for(int i = 0; i < m_screenPolygons.length(); ++i) {
206             QPolygonF * polygon = m_screenPolygons[i];
207             QVector<QVector2D> normals;
208             int segmentCount = polygon->size() - 1;
209             normals.reserve(segmentCount);
210             for(int i = 0; i < segmentCount; ++i) {
211                 normals << QVector2D(polygon->at(i+1) - polygon->at(i)).normalized();
212             }
213             QSGGeometryNode* lineNode = new QSGGeometryNode;
214 
215             QSGGeometry * lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount*2);
216             lineNodeGeo->setDrawingMode(0x0005);
217             lineNodeGeo->allocate((segmentCount + 1)*2);
218 
219 
220             QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
221             material->setColor(m_lineColor);
222 
223             lineNode->setGeometry(lineNodeGeo);
224             lineNode->setFlag(QSGNode::OwnsGeometry);
225             lineNode->setMaterial(material);
226             lineNode->setFlag(QSGNode::OwnsMaterial);
227 
228             auto points = lineNodeGeo->vertexDataAsPoint2D();
229             int k = -1;
230             for(int i = 0; i < segmentCount + 1; ++i) {
231                 auto const & a = mapFromItem(m_map, polygon->at(i));
232                 auto const & n = normals[qMin(i, segmentCount - 1)].toPointF();
233                 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x());
234                 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x());
235             }
236             oldNode->appendChildNode(lineNode);
237         }
238 
239         return oldNode;
240     }
241 }
242 
243 #include "moc_GeoPolyline.cpp"
244