File indexing completed on 2024-05-05 03:49:16

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2019 Torsten Rahn <rahn@kde.org>
0004 //
0005 
0006 #include "MarbleQuickItem.h"
0007 #include "GeoPolyline.h"
0008 #include "Coordinate.h"
0009 
0010 #include <QSGGeometryNode>
0011 #include <QSGFlatColorMaterial>
0012 #include <QSGSimpleTextureNode>
0013 #include <QSGTexture>
0014 #include <QPolygonF>
0015 #include <QtMath>
0016 
0017 #include "MarbleGlobal.h"
0018 
0019 using Marble::GeoDataCoordinates;
0020 using Marble::EARTH_RADIUS;
0021 using Marble::DEG2RAD;
0022 
0023 namespace Marble
0024 {
0025     GeoPolyline::GeoPolyline(QQuickItem *parent ) :
0026         QQuickItem( parent ),
0027         m_map(nullptr),
0028         m_observable(false),
0029         m_lineColor(Qt::black),
0030         m_lineWidth(1),
0031         m_tessellate(true),
0032         m_clipScreenCoordinates(true)
0033     {
0034         setFlag(ItemHasContents, true);
0035     }
0036 
0037     MarbleQuickItem * GeoPolyline::map() const
0038     {
0039         return m_map;
0040     }
0041 
0042     void GeoPolyline::setMap(MarbleQuickItem *map)
0043     {
0044         if (m_map == map)
0045             return;
0046 
0047         m_map = map;
0048 
0049         connect(m_map, &MarbleQuickItem::visibleLatLonAltBoxChanged, this, &GeoPolyline::updateScreenPositions);
0050         emit mapChanged(m_map);
0051     }
0052 
0053     void GeoPolyline::updateScreenPositions() {
0054         GeoDataLineString lineString(m_lineString);
0055 
0056         if (m_map) {
0057             QPolygonF displayPolygon;
0058             displayPolygon << QPointF(-10,-10) << QPointF(m_map->mapWidth() + 10, -10)
0059                            << QPointF(m_map->mapWidth() + 10, m_map->mapHeight() + 10) << QPointF(-10, m_map->mapHeight() + 10);
0060             m_screenPolygons.clear();
0061             QVector<QPolygonF*> fullScreenPolygons;
0062             bool success = m_map->screenCoordinatesFromGeoDataLineString(lineString, fullScreenPolygons);
0063             if (m_clipScreenCoordinates) {
0064                 for (auto reducedPolygon : qAsConst(fullScreenPolygons)) {
0065                     m_screenPolygons << reducedPolygon->intersected(displayPolygon);
0066                 }
0067             }
0068             else {
0069                 for (auto eachPolygon : qAsConst(fullScreenPolygons)) {
0070                     m_screenPolygons << *eachPolygon;
0071                 }
0072             }
0073 
0074             qDeleteAll(fullScreenPolygons);
0075 
0076             QVariantList previousScreenCoordinates;
0077             previousScreenCoordinates = m_screenCoordinates;
0078             m_screenCoordinates.clear();
0079             if (success) {
0080                 int i = 0;
0081                 for (auto screenPolygon : qAsConst(m_screenPolygons)) {
0082                     QVariantList polyline;
0083                     for (auto node : screenPolygon) {
0084                         QVariantMap vmap;
0085                         vmap["x"] = node.x();
0086                         vmap["y"] = node.y();
0087                         polyline.append(vmap);
0088                     }
0089                     m_screenCoordinates.insert(i, polyline);
0090                     ++i;
0091                 }
0092             }
0093 
0094             QRectF polygonBoundingRect;
0095             if (m_screenPolygons.length() == 1) {
0096                 polygonBoundingRect = m_screenPolygons[0].boundingRect();
0097             }
0098             else {
0099                 QPolygonF polygons;
0100                 for (auto polygon : qAsConst(m_screenPolygons)) {
0101                     polygons << polygon;
0102                 }
0103                 polygonBoundingRect = polygons.boundingRect();
0104             }
0105             setX(polygonBoundingRect.x());
0106             setY(polygonBoundingRect.y());
0107             setWidth(polygonBoundingRect.width());
0108             setHeight(polygonBoundingRect.height());
0109 
0110             if (m_screenCoordinates != previousScreenCoordinates) {
0111                 emit screenCoordinatesChanged();
0112             }
0113             emit readonlyXChanged();
0114             emit readonlyYChanged();
0115             emit readonlyWidthChanged();
0116             emit readonlyHeightChanged();
0117             update();
0118         }
0119     }
0120 
0121     bool GeoPolyline::observable() const
0122     {
0123         return m_observable;
0124     }
0125 
0126     QVariantList GeoPolyline::geoCoordinates() const
0127     {
0128         return m_geoCoordinates;
0129     }
0130 
0131     void GeoPolyline::setGeoCoordinates(const QVariantList & coordinates)
0132     {
0133         m_lineString.clear();
0134         m_lineString.setTessellate(m_tessellate);
0135         for(auto & item : coordinates) {
0136             QVariantMap map = item.toMap();
0137             m_lineString << GeoDataCoordinates(
0138                                 map["lon"].toReal(),
0139                                 map["lat"].toReal(),
0140                                 map["alt"].toReal(),
0141                                 GeoDataCoordinates::Degree
0142                             );
0143         }
0144 
0145         m_geoCoordinates = coordinates;
0146         emit geoCoordinatesChanged();
0147         updateScreenPositions();
0148     }
0149 
0150     QVariantList GeoPolyline::screenCoordinates() const
0151     {
0152         return m_screenCoordinates;
0153     }
0154 
0155     QColor GeoPolyline::lineColor() const
0156     {
0157         return m_lineColor;
0158     }
0159 
0160     qreal GeoPolyline::lineWidth() const
0161     {
0162         return m_lineWidth;
0163     }
0164 
0165     void GeoPolyline::setLineColor(const QColor& lineColor)
0166     {
0167         if (m_lineColor == lineColor)
0168             return;
0169 
0170         m_lineColor = lineColor;
0171         emit lineColorChanged(m_lineColor);
0172     }
0173 
0174     void GeoPolyline::setLineWidth(const qreal lineWidth)
0175     {
0176         if (m_lineWidth == lineWidth)
0177             return;
0178 
0179         m_lineWidth = lineWidth;
0180         emit lineWidthChanged(m_lineWidth);
0181     }
0182 
0183     bool GeoPolyline::tessellate() const
0184     {
0185         return m_tessellate;
0186     }
0187 
0188     bool GeoPolyline::clipScreenCoordinates() const
0189     {
0190         return m_clipScreenCoordinates;
0191     }
0192 
0193     void GeoPolyline::setTessellate(bool tessellate)
0194     {
0195         if (m_tessellate == tessellate)
0196             return;
0197 
0198         m_tessellate = tessellate;
0199         emit tessellateChanged(m_tessellate);
0200     }
0201 
0202     void GeoPolyline::setClipScreenCoordinates(bool clipped)
0203     {
0204         if (m_clipScreenCoordinates == clipped)
0205             return;
0206 
0207         m_clipScreenCoordinates = clipped;
0208         emit clipScreenCoordinatesChanged(m_clipScreenCoordinates);
0209     }
0210 
0211     qreal GeoPolyline::readonlyX() const
0212     {
0213         return x();
0214     }
0215 
0216     qreal GeoPolyline::readonlyY() const
0217     {
0218         return y();
0219     }
0220 
0221     qreal GeoPolyline::readonlyWidth() const
0222     {
0223         return width();
0224     }
0225 
0226     qreal GeoPolyline::readonlyHeight() const
0227     {
0228         return height();
0229     }
0230 
0231     QSGNode *GeoPolyline::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
0232     {
0233         qreal const halfWidth = m_lineWidth;
0234 
0235         delete oldNode;
0236         oldNode = new QSGNode;
0237 
0238         if (m_screenPolygons.isEmpty()) return oldNode;
0239 
0240         for(int i = 0; i < m_screenPolygons.length(); ++i) {
0241             QPolygonF polygon = m_screenPolygons[i];
0242             QVector<QVector2D> normals;
0243             int segmentCount = polygon.size() - 1;
0244             normals.reserve(segmentCount);
0245             for(int i = 0; i < segmentCount; ++i) {
0246                 normals << QVector2D(polygon.at(i+1) - polygon.at(i)).normalized();
0247             }
0248             QSGGeometryNode* lineNode = new QSGGeometryNode;
0249 
0250             QSGGeometry * lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount*2);
0251             lineNodeGeo->setDrawingMode(0x0005);
0252             lineNodeGeo->allocate((segmentCount + 1)*2);
0253 
0254 
0255             QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
0256             material->setColor(m_lineColor);
0257 
0258             lineNode->setGeometry(lineNodeGeo);
0259             lineNode->setFlag(QSGNode::OwnsGeometry);
0260             lineNode->setMaterial(material);
0261             lineNode->setFlag(QSGNode::OwnsMaterial);
0262 
0263             auto points = lineNodeGeo->vertexDataAsPoint2D();
0264             int k = -1;
0265             for(int i = 0; i < segmentCount + 1; ++i) {
0266                 auto const & a = mapFromItem(m_map, polygon.at(i));
0267                 auto const & n = normals[qMin(i, segmentCount - 1)].toPointF();
0268                 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x());
0269                 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x());
0270             }
0271             oldNode->appendChildNode(lineNode);
0272         }
0273 
0274         return oldNode;
0275     }
0276 }
0277 
0278 #include "moc_GeoPolyline.cpp"