1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> 4 ** Copyright (C) 2020 The Qt Company Ltd. 5 ** Contact: http://www.qt.io/licensing/ 6 ** 7 ** This file is part of the QtLocation module of the Qt Toolkit. 8 ** 9 ** $QT_BEGIN_LICENSE:LGPL3$ 10 ** Commercial License Usage 11 ** Licensees holding valid commercial Qt licenses may use this file in 12 ** accordance with the commercial license agreement provided with the 13 ** Software or, alternatively, in accordance with the terms contained in 14 ** a written agreement between you and The Qt Company. For licensing terms 15 ** and conditions see http://www.qt.io/terms-conditions. For further 16 ** information use the contact form at http://www.qt.io/contact-us. 17 ** 18 ** GNU Lesser General Public License Usage 19 ** Alternatively, this file may be used under the terms of the GNU Lesser 20 ** General Public License version 3 as published by the Free Software 21 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the 22 ** packaging of this file. Please review the following information to 23 ** ensure the GNU Lesser General Public License version 3 requirements 24 ** will be met: https://www.gnu.org/licenses/lgpl.html. 25 ** 26 ** GNU General Public License Usage 27 ** Alternatively, this file may be used under the terms of the GNU 28 ** General Public License version 2.0 or later as published by the Free 29 ** Software Foundation and appearing in the file LICENSE.GPL included in 30 ** the packaging of this file. Please review the following information to 31 ** ensure the GNU General Public License version 2.0 requirements will be 32 ** met: http://www.gnu.org/licenses/gpl-2.0.html. 33 ** 34 ** $QT_END_LICENSE$ 35 ** 36 ****************************************************************************/ 37 38 #ifndef QDECLARATIVERECTANGLEMAPITEM_P_P_H 39 #define QDECLARATIVERECTANGLEMAPITEM_P_P_H 40 41 // 42 // W A R N I N G 43 // ------------- 44 // 45 // This file is not part of the Qt API. It exists purely as an 46 // implementation detail. This header file may change from version to 47 // version without notice, or even be removed. 48 // 49 // We mean it. 50 // 51 52 #include <QtLocation/private/qlocationglobal_p.h> 53 #include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> 54 #include <QtLocation/private/qdeclarativerectanglemapitem_p.h> 55 #include <QtPositioning/private/qwebmercator_p.h> 56 57 QT_BEGIN_NAMESPACE 58 59 class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivate 60 { 61 public: QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem & rect)62 QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect) : m_rect(rect) 63 { 64 65 } QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItemPrivate & other)66 QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItemPrivate &other) : m_rect(other.m_rect) 67 { 68 } 69 70 virtual ~QDeclarativeRectangleMapItemPrivate(); 71 virtual void onLinePropertiesChanged() = 0; 72 virtual void markSourceDirtyAndUpdate() = 0; 73 virtual void onMapSet() = 0; 74 virtual void onGeoGeometryChanged() = 0; 75 virtual void onItemGeometryChanged() = 0; 76 virtual void updatePolish() = 0; 77 virtual void afterViewportChanged() = 0; 78 virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; 79 virtual bool contains(const QPointF &point) const = 0; 80 81 QDeclarativeRectangleMapItem &m_rect; 82 }; 83 84 class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateCPU: public QDeclarativeRectangleMapItemPrivate 85 { 86 public: QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem & rect)87 QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) 88 { 89 } 90 QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItemPrivate & other)91 QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItemPrivate &other) 92 : QDeclarativeRectangleMapItemPrivate(other) 93 { 94 } 95 96 ~QDeclarativeRectangleMapItemPrivateCPU() override; 97 onLinePropertiesChanged()98 void onLinePropertiesChanged() override 99 { 100 // mark dirty just in case we're a width change 101 markSourceDirtyAndUpdate(); 102 } markSourceDirtyAndUpdate()103 virtual void markSourceDirtyAndUpdate() override 104 { 105 m_geometry.markSourceDirty(); 106 m_borderGeometry.markSourceDirty(); 107 m_rect.polishAndUpdate(); 108 } onMapSet()109 virtual void onMapSet() override 110 { 111 markSourceDirtyAndUpdate(); 112 } onGeoGeometryChanged()113 virtual void onGeoGeometryChanged() override 114 { 115 markSourceDirtyAndUpdate(); 116 } onItemGeometryChanged()117 virtual void onItemGeometryChanged() override 118 { 119 m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 120 m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 121 markSourceDirtyAndUpdate(); 122 } afterViewportChanged()123 virtual void afterViewportChanged() override 124 { 125 m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 126 m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 127 markSourceDirtyAndUpdate(); 128 } updatePolish()129 virtual void updatePolish() override 130 { 131 if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { 132 m_geometry.clear(); 133 m_borderGeometry.clear(); 134 m_rect.setWidth(0); 135 m_rect.setHeight(0); 136 return; 137 } 138 139 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection()); 140 141 QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry); 142 m_rect.m_updatingGeometry = true; 143 144 const QList<QGeoCoordinate> perimeter = path(m_rect.m_rectangle); 145 const QList<QDoubleVector2D> pathMercator_ = pathMercator(perimeter); 146 m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 147 m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_); 148 m_geometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); 149 150 QList<QGeoMapItemGeometry *> geoms; 151 geoms << &m_geometry; 152 m_borderGeometry.clear(); 153 154 if (m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0) { 155 QList<QDoubleVector2D> closedPath = pathMercator_; 156 closedPath << closedPath.first(); 157 158 m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 159 const QGeoCoordinate &geometryOrigin = m_geometry.origin(); 160 161 m_borderGeometry.srcPoints_.clear(); 162 m_borderGeometry.srcPointTypes_.clear(); 163 164 QDoubleVector2D borderLeftBoundWrapped; 165 QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*m_rect.map(), closedPath, borderLeftBoundWrapped); 166 if (clippedPaths.size()) { 167 borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); 168 m_borderGeometry.pathToScreen(*m_rect.map(), clippedPaths, borderLeftBoundWrapped); 169 m_borderGeometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); 170 171 geoms << &m_borderGeometry; 172 } else { 173 m_borderGeometry.clear(); 174 } 175 } 176 177 QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); 178 m_rect.setWidth(combined.width() + 2 * m_rect.m_border.width()); // ToDo: fix this! 2 is incorrect 179 m_rect.setHeight(combined.height() + 2 * m_rect.m_border.width()); 180 181 m_rect.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset()); 182 } 183 updateMapItemPaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData * data)184 virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override 185 { 186 Q_UNUSED(data); 187 if (!m_node || !oldNode) { 188 m_node = new MapPolygonNode(); 189 if (oldNode) { 190 delete oldNode; 191 oldNode = nullptr; 192 } 193 } else { 194 m_node = static_cast<MapPolygonNode *>(oldNode); 195 } 196 197 //TODO: update only material 198 if (m_geometry.isScreenDirty() || m_borderGeometry.isScreenDirty() || m_rect.m_dirtyMaterial) { 199 m_node->update(m_rect.m_color, m_rect.m_border.color(), &m_geometry, &m_borderGeometry); 200 m_geometry.setPreserveGeometry(false); 201 m_borderGeometry.setPreserveGeometry(false); 202 m_geometry.markClean(); 203 m_borderGeometry.markClean(); 204 m_rect.m_dirtyMaterial = false; 205 } 206 return m_node; 207 } contains(const QPointF & point)208 virtual bool contains(const QPointF &point) const override 209 { 210 return (m_geometry.contains(point) || m_borderGeometry.contains(point)); 211 } 212 path(const QGeoRectangle & rect)213 static QList<QGeoCoordinate> path(const QGeoRectangle &rect) 214 { 215 QList<QGeoCoordinate> res; 216 res << rect.topLeft(); 217 res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude()); 218 res << rect.bottomRight(); 219 res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude()); 220 return res; 221 } 222 perimeter(const QGeoRectangle & rect)223 static QList<QGeoCoordinate> perimeter(const QGeoRectangle &rect) 224 { 225 QList<QGeoCoordinate> res; 226 res << rect.topLeft(); 227 res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude()); 228 res << rect.bottomRight(); 229 res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude()); 230 res << res.first(); 231 return res; 232 } 233 pathMercator(const QList<QGeoCoordinate> & p)234 static QList<QDoubleVector2D> pathMercator(const QList<QGeoCoordinate> &p) 235 { 236 QList<QDoubleVector2D> res; 237 for (const auto &c: p) 238 res << QWebMercator::coordToMercator(c); 239 return res; 240 } 241 242 QGeoMapPolygonGeometry m_geometry; 243 QGeoMapPolylineGeometry m_borderGeometry; 244 MapPolygonNode *m_node = nullptr; 245 }; 246 247 #if QT_CONFIG(opengl) 248 class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateOpenGL: public QDeclarativeRectangleMapItemPrivate 249 { 250 public: QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem & rect)251 QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) 252 { 253 } 254 QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate & other)255 QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate &other) 256 : QDeclarativeRectangleMapItemPrivate(other) 257 { 258 } 259 260 ~QDeclarativeRectangleMapItemPrivateOpenGL() override; 261 markScreenDirtyAndUpdate()262 void markScreenDirtyAndUpdate() 263 { 264 // preserveGeometry is cleared in updateMapItemPaintNode 265 m_geometry.markScreenDirty(); 266 m_borderGeometry.markScreenDirty(); 267 m_rect.polishAndUpdate(); 268 } onLinePropertiesChanged()269 void onLinePropertiesChanged() override 270 { 271 m_rect.m_dirtyMaterial = true; 272 afterViewportChanged(); 273 } markSourceDirtyAndUpdate()274 virtual void markSourceDirtyAndUpdate() override 275 { 276 m_geometry.markSourceDirty(); 277 m_borderGeometry.markSourceDirty(); 278 m_rect.polishAndUpdate(); 279 } preserveGeometry()280 void preserveGeometry() 281 { 282 m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 283 m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); 284 } onMapSet()285 virtual void onMapSet() override 286 { 287 markSourceDirtyAndUpdate(); 288 } onGeoGeometryChanged()289 virtual void onGeoGeometryChanged() override 290 { 291 preserveGeometry(); 292 markSourceDirtyAndUpdate(); 293 } onItemGeometryChanged()294 virtual void onItemGeometryChanged() override 295 { 296 onGeoGeometryChanged(); 297 } afterViewportChanged()298 virtual void afterViewportChanged() override 299 { 300 preserveGeometry(); 301 markScreenDirtyAndUpdate(); 302 } updatePolish()303 virtual void updatePolish() override 304 { 305 if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { 306 m_geometry.clear(); 307 m_borderGeometry.clear(); 308 m_rect.setWidth(0); 309 m_rect.setHeight(0); 310 return; 311 } 312 313 QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry); 314 m_rect.m_updatingGeometry = true; 315 const qreal lineWidth = m_rect.m_border.width(); 316 const QColor &lineColor = m_rect.m_border.color(); 317 const QColor &fillColor = m_rect.color(); 318 if (fillColor.alpha() != 0) { 319 m_geometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); 320 m_geometry.markScreenDirty(); 321 m_geometry.updateScreenPoints(*m_rect.map(), lineWidth, lineColor); 322 } else { 323 m_geometry.clearBounds(); 324 } 325 326 QGeoMapItemGeometry * geom = &m_geometry; 327 m_borderGeometry.clearScreen(); 328 if (lineColor.alpha() != 0 && lineWidth > 0) { 329 m_borderGeometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); 330 m_borderGeometry.markScreenDirty(); 331 m_borderGeometry.updateScreenPoints(*m_rect.map(), lineWidth); 332 geom = &m_borderGeometry; 333 } 334 m_rect.setWidth(geom->sourceBoundingBox().width()); 335 m_rect.setHeight(geom->sourceBoundingBox().height()); 336 m_rect.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); 337 } 338 updateMapItemPaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData * data)339 virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override 340 { 341 Q_UNUSED(data); 342 343 if (!m_rootNode || !oldNode) { 344 m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); 345 m_node = new MapPolygonNodeGL(); 346 m_rootNode->appendChildNode(m_node); 347 m_polylinenode = new MapPolylineNodeOpenGLExtruded(); 348 m_rootNode->appendChildNode(m_polylinenode); 349 m_rootNode->markDirty(QSGNode::DirtyNodeAdded); 350 if (oldNode) 351 delete oldNode; 352 } else { 353 m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode); 354 } 355 356 const QGeoMap *map = m_rect.map(); 357 const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); 358 const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); 359 360 if (m_borderGeometry.isScreenDirty()) { 361 /* Do the border update first */ 362 m_polylinenode->update(m_rect.m_border.color(), 363 float(m_rect.m_border.width()), 364 &m_borderGeometry, 365 combinedMatrix, 366 cameraCenter, 367 Qt::SquareCap, 368 true, 369 30); // No LOD for rectangles 370 m_borderGeometry.setPreserveGeometry(false); 371 m_borderGeometry.markClean(); 372 } else { 373 m_polylinenode->setSubtreeBlocked(true); 374 } 375 if (m_geometry.isScreenDirty()) { 376 m_node->update(m_rect.m_color, 377 &m_geometry, 378 combinedMatrix, 379 cameraCenter); 380 m_geometry.setPreserveGeometry(false); 381 m_geometry.markClean(); 382 } else { 383 m_node->setSubtreeBlocked(true); 384 } 385 386 m_rootNode->setSubtreeBlocked(false); 387 return m_rootNode; 388 } contains(const QPointF & point)389 virtual bool contains(const QPointF &point) const override 390 { 391 const qreal lineWidth = m_rect.m_border.width(); 392 const QColor &lineColor = m_rect.m_border.color(); 393 const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); 394 if (bounds.contains(point)) { 395 QDeclarativeGeoMap *m = m_rect.quickMap(); 396 if (m) { 397 const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_rect, point)); 398 return m_rect.m_rectangle.contains(crd) || m_borderGeometry.contains(m_rect.mapToItem(m_rect.quickMap(), point), 399 m_rect.border()->width(), 400 static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection())); 401 } else { 402 return true; 403 } 404 } 405 return false; 406 } 407 408 QGeoMapPolygonGeometryOpenGL m_geometry; 409 QGeoMapPolylineGeometryOpenGL m_borderGeometry; 410 QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; 411 MapPolygonNodeGL *m_node = nullptr; 412 MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; 413 }; 414 #endif // QT_CONFIG(opengl) 415 416 QT_END_NAMESPACE 417 418 #endif // QDECLARATIVERECTANGLEMAPITEM_P_P_H 419 420