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"