| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2015 The Qt Company Ltd. | 
| 4 | ** Contact: http://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtLocation module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL3$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at http://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or later as published by the Free | 
| 28 | ** Software Foundation and appearing in the file LICENSE.GPL included in | 
| 29 | ** the packaging of this file. Please review the following information to | 
| 30 | ** ensure the GNU General Public License version 2.0 requirements will be | 
| 31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. | 
| 32 | ** | 
| 33 | ** $QT_END_LICENSE$ | 
| 34 | ** | 
| 35 | ****************************************************************************/ | 
| 36 |  | 
| 37 | #include "qdeclarativerectanglemapitem_p.h" | 
| 38 | #include "qdeclarativerectanglemapitem_p_p.h" | 
| 39 | #include "qdeclarativepolygonmapitem_p.h" | 
| 40 | #include "qlocationutils_p.h" | 
| 41 | #include <QPainterPath> | 
| 42 | #include <qnumeric.h> | 
| 43 | #include <QRectF> | 
| 44 | #include <QPointF> | 
| 45 | #include <QtPositioning/private/qwebmercator_p.h> | 
| 46 | #include <QtLocation/private/qgeomap_p.h> | 
| 47 | #include <QtPositioning/private/qdoublevector2d_p.h> | 
| 48 | #include <QtCore/QScopedValueRollback> | 
| 49 |  | 
| 50 | QT_BEGIN_NAMESPACE | 
| 51 |  | 
| 52 | /*! | 
| 53 |     \qmltype MapRectangle | 
| 54 |     \instantiates QDeclarativeRectangleMapItem | 
| 55 |     \inqmlmodule QtLocation | 
| 56 |     \ingroup qml-QtLocation5-maps | 
| 57 |     \since QtLocation 5.5 | 
| 58 |  | 
| 59 |     \brief The MapRectangle type displays a rectangle on a Map. | 
| 60 |  | 
| 61 |     The MapRectangle type displays a rectangle on a Map. Rectangles are a | 
| 62 |     special case of Polygon with exactly 4 vertices and 4 "straight" edges. In | 
| 63 |     this case, "straight" means that the top-left point has the same latitude | 
| 64 |     as the top-right point (the top edge), and the bottom-left point has the | 
| 65 |     same latitude as the bottom-right point (the bottom edge). Similarly, the | 
| 66 |     points on the left side have the same longitude, and the points on the | 
| 67 |     right side have the same longitude. | 
| 68 |  | 
| 69 |     To specify the rectangle, it requires a \l topLeft and \l bottomRight point, | 
| 70 |     both given by a \l {coordinate}. | 
| 71 |  | 
| 72 |     By default, the rectangle is displayed with transparent fill and a 1-pixel | 
| 73 |     thick black border. This can be changed using the \l color, \l border.color | 
| 74 |     and \l border.width properties. | 
| 75 |  | 
| 76 |     \note Similar to the \l MapPolygon type, MapRectangles are geographic | 
| 77 |     items, thus dragging a MapRectangle causes its vertices to be recalculated | 
| 78 |     in the geographic coordinate space. Apparent stretching of the item | 
| 79 |     occurs when dragged to the a different latitude, however, its edges | 
| 80 |     remain straight. | 
| 81 |  | 
| 82 |     \section2 Performance | 
| 83 |  | 
| 84 |     MapRectangles have a rendering cost identical to a MapPolygon with 4 | 
| 85 |     vertices. | 
| 86 |  | 
| 87 |     Like the other map objects, MapRectangle is normally drawn without a smooth | 
| 88 |     appearance. Setting the \l opacity property will force the object to be | 
| 89 |     blended, which decreases performance considerably depending on the hardware | 
| 90 |     in use. | 
| 91 |  | 
| 92 |     \section2 Example Usage | 
| 93 |  | 
| 94 |     The following snippet shows a map containing a MapRectangle, spanning | 
| 95 |     from (-27, 153) to (-28, 153.5), near Brisbane, Australia. The rectangle | 
| 96 |     is filled in green, with a 2 pixel black border. | 
| 97 |  | 
| 98 |     \code | 
| 99 |     Map { | 
| 100 |         MapRectangle { | 
| 101 |             color: 'green' | 
| 102 |             border.width: 2 | 
| 103 |             topLeft { | 
| 104 |                 latitude: -27 | 
| 105 |                 longitude: 153 | 
| 106 |             } | 
| 107 |             bottomRight { | 
| 108 |                 latitude: -28 | 
| 109 |                 longitude: 153.5 | 
| 110 |             } | 
| 111 |         } | 
| 112 |     } | 
| 113 |     \endcode | 
| 114 |  | 
| 115 |     \image api-maprectangle.png | 
| 116 | */ | 
| 117 |  | 
| 118 | /*! | 
| 119 |     \qmlproperty bool QtLocation::MapRectangle::autoFadeIn | 
| 120 |  | 
| 121 |     This property holds whether the item automatically fades in when zooming into the map | 
| 122 |     starting from very low zoom levels. By default this is \c true. | 
| 123 |     Setting this property to \c false causes the map item to always have the opacity specified | 
| 124 |     with the \l QtQuick::Item::opacity property, which is 1.0 by default. | 
| 125 |  | 
| 126 |     \since 5.14 | 
| 127 | */ | 
| 128 |  | 
| 129 | struct RectangleBackendSelector | 
| 130 | { | 
| 131 |     RectangleBackendSelector() | 
| 132 |     { | 
| 133 |         backend = (qgetenv(varName: "QTLOCATION_OPENGL_ITEMS" ).toInt()) ? QDeclarativeRectangleMapItem::OpenGL : QDeclarativeRectangleMapItem::Software; | 
| 134 |     } | 
| 135 |     QDeclarativeRectangleMapItem::Backend backend = QDeclarativeRectangleMapItem::Software; | 
| 136 | }; | 
| 137 |  | 
| 138 | Q_GLOBAL_STATIC(RectangleBackendSelector, mapRectangleBackendSelector) | 
| 139 |  | 
| 140 | QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) | 
| 141 | :   QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), | 
| 142 |     m_updatingGeometry(false) | 
| 143 |   , m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this)) | 
| 144 | { | 
| 145 |     // ToDo: handle envvar, and switch implementation. | 
| 146 |     m_itemType = QGeoMap::MapRectangle; | 
| 147 |     setFlag(flag: ItemHasContents, enabled: true); | 
| 148 |     QObject::connect(sender: &m_border, SIGNAL(colorChanged(QColor)), | 
| 149 |                      receiver: this, SLOT(onLinePropertiesChanged())); | 
| 150 |     QObject::connect(sender: &m_border, SIGNAL(widthChanged(qreal)), | 
| 151 |                      receiver: this, SLOT(onLinePropertiesChanged())); | 
| 152 |     setBackend(mapRectangleBackendSelector->backend); | 
| 153 | } | 
| 154 |  | 
| 155 | QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() | 
| 156 | { | 
| 157 | } | 
| 158 |  | 
| 159 | /*! | 
| 160 |     \qmlproperty MapRectangle.Backend QtLocation::MapRectangle::backend | 
| 161 |  | 
| 162 |     This property holds which backend is in use to render the map item. | 
| 163 |     Valid values are \b MapRectangle.Software and \b{MapRectangle.OpenGL}. | 
| 164 |     The default value is \b{MapRectangle.Software}. | 
| 165 |  | 
| 166 |     \note \b{The release of this API with Qt 5.15 is a Technology Preview}. | 
| 167 |     Ideally, as the OpenGL backends for map items mature, there will be | 
| 168 |     no more need to also offer the legacy software-projection backend. | 
| 169 |     So this property will likely disappear at some later point. | 
| 170 |     To select OpenGL-accelerated item backends without using this property, | 
| 171 |     it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS | 
| 172 |     to \b{1}. | 
| 173 |     Also note that all current OpenGL backends won't work as expected when enabling | 
| 174 |     layers on the individual item, or when running on OpenGL core profiles greater than 2.x. | 
| 175 |  | 
| 176 |     \since 5.15 | 
| 177 | */ | 
| 178 | QDeclarativeRectangleMapItem::Backend QDeclarativeRectangleMapItem::backend() const | 
| 179 | { | 
| 180 |     return m_backend; | 
| 181 | } | 
| 182 |  | 
| 183 | void QDeclarativeRectangleMapItem::setBackend(QDeclarativeRectangleMapItem::Backend b) | 
| 184 | { | 
| 185 |     if (b == m_backend) | 
| 186 |         return; | 
| 187 |     m_backend = b; | 
| 188 |     QScopedPointer<QDeclarativeRectangleMapItemPrivate> d( | 
| 189 |             (m_backend == Software) ? static_cast<QDeclarativeRectangleMapItemPrivate *>( | 
| 190 |                     new QDeclarativeRectangleMapItemPrivateCPU(*this)) | 
| 191 | #if QT_CONFIG(opengl) | 
| 192 |                                     : static_cast<QDeclarativeRectangleMapItemPrivate *>( | 
| 193 |                                             new QDeclarativeRectangleMapItemPrivateOpenGL(*this))); | 
| 194 | #else | 
| 195 |                                     : nullptr); | 
| 196 |     qFatal("Requested non software rendering backend, but source code is compiled wihtout opengl "  | 
| 197 |            "support" ); | 
| 198 | #endif | 
| 199 |  | 
| 200 |     m_d.swap(other&: d); | 
| 201 |     m_d->onGeoGeometryChanged(); | 
| 202 |     emit backendChanged(); | 
| 203 | } | 
| 204 |  | 
| 205 | /*! | 
| 206 |     \internal | 
| 207 | */ | 
| 208 | void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) | 
| 209 | { | 
| 210 |     QDeclarativeGeoMapItemBase::setMap(quickMap,map); | 
| 211 |     if (!map) | 
| 212 |         return; | 
| 213 |     m_d->onMapSet(); | 
| 214 | } | 
| 215 |  | 
| 216 | /*! | 
| 217 |     \qmlpropertygroup Location::MapRectangle::border | 
| 218 |     \qmlproperty int MapRectangle::border.width | 
| 219 |     \qmlproperty color MapRectangle::border.color | 
| 220 |  | 
| 221 |     This property is part of the border property group. The border property group | 
| 222 |     holds the width and color used to draw the border of the rectangle. | 
| 223 |     The width is in pixels and is independent of the zoom level of the map. | 
| 224 |  | 
| 225 |     The default values correspond to a black border with a width of 1 pixel. | 
| 226 |     For no line, use a width of 0 or a transparent color. | 
| 227 | */ | 
| 228 | QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() | 
| 229 | { | 
| 230 |     return &m_border; | 
| 231 | } | 
| 232 |  | 
| 233 | /*! | 
| 234 |     \qmlproperty coordinate MapRectangle::topLeft | 
| 235 |  | 
| 236 |     This property holds the top-left coordinate of the MapRectangle which | 
| 237 |     can be used to retrieve its longitude, latitude and altitude. | 
| 238 | */ | 
| 239 | void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) | 
| 240 | { | 
| 241 |     if (m_rectangle.topLeft() == topLeft) | 
| 242 |         return; | 
| 243 |  | 
| 244 |     m_rectangle.setTopLeft(topLeft); | 
| 245 |     m_d->onGeoGeometryChanged(); | 
| 246 |     emit topLeftChanged(topLeft); | 
| 247 | } | 
| 248 |  | 
| 249 | QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() | 
| 250 | { | 
| 251 |     return m_rectangle.topLeft(); | 
| 252 | } | 
| 253 |  | 
| 254 | /*! | 
| 255 |     \internal | 
| 256 | */ | 
| 257 | void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() | 
| 258 | { | 
| 259 |     m_d->markSourceDirtyAndUpdate(); | 
| 260 | } | 
| 261 |  | 
| 262 | void QDeclarativeRectangleMapItem::onLinePropertiesChanged() | 
| 263 | { | 
| 264 |     m_d->onLinePropertiesChanged(); | 
| 265 | } | 
| 266 |  | 
| 267 | /*! | 
| 268 |     \qmlproperty coordinate MapRectangle::bottomRight | 
| 269 |  | 
| 270 |     This property holds the bottom-right coordinate of the MapRectangle which | 
| 271 |     can be used to retrieve its longitude, latitude and altitude. | 
| 272 | */ | 
| 273 | void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) | 
| 274 | { | 
| 275 |     if (m_rectangle.bottomRight() == bottomRight) | 
| 276 |         return; | 
| 277 |  | 
| 278 |     m_rectangle.setBottomRight(bottomRight); | 
| 279 |     m_d->onGeoGeometryChanged(); | 
| 280 |     emit bottomRightChanged(bottomRight); | 
| 281 | } | 
| 282 |  | 
| 283 | QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() | 
| 284 | { | 
| 285 |     return m_rectangle.bottomRight(); | 
| 286 | } | 
| 287 |  | 
| 288 | /*! | 
| 289 |     \qmlproperty color MapRectangle::color | 
| 290 |  | 
| 291 |     This property holds the fill color of the rectangle. For no fill, use | 
| 292 |     a transparent color. | 
| 293 | */ | 
| 294 | QColor QDeclarativeRectangleMapItem::color() const | 
| 295 | { | 
| 296 |     return m_color; | 
| 297 | } | 
| 298 |  | 
| 299 | void QDeclarativeRectangleMapItem::setColor(const QColor &color) | 
| 300 | { | 
| 301 |     if (m_color == color) | 
| 302 |         return; | 
| 303 |     m_color = color; | 
| 304 |     m_dirtyMaterial = true; | 
| 305 |     polishAndUpdate(); | 
| 306 |     emit colorChanged(color: m_color); | 
| 307 | } | 
| 308 |  | 
| 309 | /*! | 
| 310 |   \qmlproperty real MapRectangle::opacity | 
| 311 |  | 
| 312 |   This property holds the opacity of the item.  Opacity is specified as a | 
| 313 |   number between 0 (fully transparent) and 1 (fully opaque).  The default is 1. | 
| 314 |  | 
| 315 |   An item with 0 opacity will still receive mouse events. To stop mouse events, set the | 
| 316 |   visible property of the item to false. | 
| 317 | */ | 
| 318 |  | 
| 319 | /*! | 
| 320 |     \internal | 
| 321 | */ | 
| 322 | QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) | 
| 323 | { | 
| 324 |     return m_d->updateMapItemPaintNode(oldNode, data); | 
| 325 | } | 
| 326 |  | 
| 327 | /*! | 
| 328 |     \internal | 
| 329 | */ | 
| 330 | void QDeclarativeRectangleMapItem::updatePolish() | 
| 331 | { | 
| 332 |     if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) | 
| 333 |         return; | 
| 334 |     m_d->updatePolish(); | 
| 335 | } | 
| 336 |  | 
| 337 | /*! | 
| 338 |     \internal | 
| 339 | */ | 
| 340 | void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) | 
| 341 | { | 
| 342 |     if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) | 
| 343 |         return; | 
| 344 |     m_d->afterViewportChanged(); | 
| 345 | } | 
| 346 |  | 
| 347 | /*! | 
| 348 |     \internal | 
| 349 | */ | 
| 350 | bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const | 
| 351 | { | 
| 352 |     return m_d->contains(point); | 
| 353 | } | 
| 354 |  | 
| 355 | const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const | 
| 356 | { | 
| 357 |     return m_rectangle; | 
| 358 | } | 
| 359 |  | 
| 360 | void QDeclarativeRectangleMapItem::setGeoShape(const QGeoShape &shape) | 
| 361 | { | 
| 362 |     if (shape == m_rectangle) | 
| 363 |         return; | 
| 364 |  | 
| 365 |     const QGeoRectangle rectangle = m_rectangle.boundingGeoRectangle(); | 
| 366 |     const bool tlHasChanged = rectangle.topLeft() != m_rectangle.topLeft(); | 
| 367 |     const bool brHasChanged = rectangle.bottomRight() != m_rectangle.bottomRight(); | 
| 368 |     m_rectangle = rectangle; | 
| 369 |  | 
| 370 |     m_d->onGeoGeometryChanged(); | 
| 371 |     if (tlHasChanged) | 
| 372 |         emit topLeftChanged(topLeft: m_rectangle.topLeft()); | 
| 373 |     if (brHasChanged) | 
| 374 |         emit bottomRightChanged(bottomRight: m_rectangle.bottomRight()); | 
| 375 | } | 
| 376 |  | 
| 377 | /*! | 
| 378 |     \internal | 
| 379 | */ | 
| 380 | void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | 
| 381 | { | 
| 382 |     if (!map() || !m_rectangle.isValid() || m_updatingGeometry || newGeometry.topLeft() == oldGeometry.topLeft()) { | 
| 383 |         QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); | 
| 384 |         return; | 
| 385 |     } | 
| 386 |     // TODO: change the algorithm to preserve the distances and size | 
| 387 |     QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(newGeometry.center()), clipToViewport: false); | 
| 388 |     QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(pos: QDoubleVector2D(oldGeometry.center()), clipToViewport: false); | 
| 389 |     if (!newCenter.isValid() || !oldCenter.isValid()) | 
| 390 |         return; | 
| 391 |     double offsetLongi = newCenter.longitude() - oldCenter.longitude(); | 
| 392 |     double offsetLati = newCenter.latitude() - oldCenter.latitude(); | 
| 393 |     if (offsetLati == 0.0 && offsetLongi == 0.0) | 
| 394 |         return; | 
| 395 |  | 
| 396 |     m_rectangle.translate(degreesLatitude: offsetLati, degreesLongitude: offsetLongi); | 
| 397 |     m_d->onItemGeometryChanged(); | 
| 398 |     emit topLeftChanged(topLeft: m_rectangle.topLeft()); | 
| 399 |     emit bottomRightChanged(bottomRight: m_rectangle.bottomRight()); | 
| 400 |  | 
| 401 |     // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested | 
| 402 |     // call to this function. | 
| 403 | } | 
| 404 |  | 
| 405 | QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {} | 
| 406 |  | 
| 407 | QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {} | 
| 408 |  | 
| 409 | #if QT_CONFIG(opengl) | 
| 410 | QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {} | 
| 411 | #endif | 
| 412 |  | 
| 413 | QT_END_NAMESPACE | 
| 414 |  |