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