Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdeclarativepolylinemapitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <QtCore/QScopedValueRollback>
9#include <qnumeric.h>
10#include <QPainterPath>
11
12#include <QtGui/private/qtriangulatingstroker_p.h>
13#include <QtPositioning/private/qlocationutils_p.h>
14#include <QtPositioning/private/qdoublevector2d_p.h>
15#include <QtPositioning/private/qwebmercator_p.h>
16#include <QtPositioning/private/qclipperutils_p.h>
17#include <QtPositioning/private/qgeopath_p.h>
18#include <QtLocation/private/qgeomap_p.h>
19
20#include <array>
21
23
24
25static bool get_line_intersection(const double p0_x,
26 const double p0_y,
27 const double p1_x,
28 const double p1_y,
29 const double p2_x,
30 const double p2_y,
31 const double p3_x,
32 const double p3_y,
33 double *i_x,
34 double *i_y,
35 double *i_t)
36{
37 const double s10_x = p1_x - p0_x;
38 const double s10_y = p1_y - p0_y;
39 const double s32_x = p3_x - p2_x;
40 const double s32_y = p3_y - p2_y;
41
42 const double denom = s10_x * s32_y - s32_x * s10_y;
43 if (denom == 0.0)
44 return false; // Collinear
45 const bool denomPositive = denom > 0;
46
47 const double s02_x = p0_x - p2_x;
48 const double s02_y = p0_y - p2_y;
49 const double s_numer = s10_x * s02_y - s10_y * s02_x;
50 if ((s_numer < 0.0) == denomPositive)
51 return false; // No collision
52
53 const double t_numer = s32_x * s02_y - s32_y * s02_x;
54 if ((t_numer < 0.0) == denomPositive)
55 return false; // No collision
56
57 if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive))
58 return false; // No collision
59 // Collision detected
60 *i_t = t_numer / denom;
61 *i_x = p0_x + (*i_t * s10_x);
62 *i_y = p0_y + (*i_t * s10_y);
63
64 return true;
65}
66
72
74 const QList<QDoubleVector2D> &l,
75 const QList<QDoubleVector2D> &poly)
76{
77 QList<QList<QDoubleVector2D> > res;
78 if (poly.size() < 2 || l.size() < 2)
79 return res;
80
81 // Step 1: build edges
82 std::vector<std::array<double, 4> > edges;
83 for (qsizetype i = 1; i < poly.size(); i++)
84 edges.push_back({ { poly.at(i-1).x(), poly.at(i-1).y(), poly.at(i).x(), poly.at(i).y() } });
85 edges.push_back({ { poly.at(poly.size()-1).x(), poly.at(poly.size()-1).y(), poly.at(0).x(), poly.at(0).y() } });
86
87 // Step 2: check each segment against each edge
88 QList<QDoubleVector2D> subLine;
89 std::array<double, 4> intersections = { { 0.0, 0.0, 0.0, 0.0 } };
90
91 for (qsizetype i = 0; i < l.size() - 1; ++i) {
93 double t = -1; // valid values are in [0, 1]. Only written if intersects
94 double previousT = t;
95 double i_x, i_y;
96
97 const int firstContained = QClipperUtils::pointInPolygon(l.at(i), poly);
98 const int secondContained = QClipperUtils::pointInPolygon(l.at(i+1), poly);
99
100 if (firstContained && secondContained) { // Second most common condition, test early and skip inner loop if possible
101 if (!subLine.size())
102 subLine.push_back(l.at(i)); // the initial element has to be pushed now.
103 subLine.push_back(l.at(i+1));
104 continue;
105 }
106
107 for (unsigned int j = 0; j < edges.size(); ++j) {
108 const bool intersects = get_line_intersection(l.at(i).x(),
109 l.at(i).y(),
110 l.at(i+1).x(),
111 l.at(i+1).y(),
112 edges.at(j).at(0),
113 edges.at(j).at(1),
114 edges.at(j).at(2),
115 edges.at(j).at(3),
116 &i_x,
117 &i_y,
118 &t);
119 if (intersects) {
120 if (previousT >= 0.0) { //One intersection already hit
121 if (t < previousT) { // Reorder
122 intersections[2] = intersections[0];
123 intersections[3] = intersections[1];
124 intersections[0] = i_x;
125 intersections[1] = i_y;
126 } else {
127 intersections[2] = i_x;
128 intersections[3] = i_y;
129 }
130
131 type = TwoIntersections;
132 break; // no need to check anything else
133 } else { // First intersection
134 intersections[0] = i_x;
135 intersections[1] = i_y;
136 type = OneIntersection;
137 }
138 previousT = t;
139 }
140 }
141
142 if (type == NoIntersection) {
143 if (!firstContained && !secondContained) { // Both outside
144 subLine.clear();
145 } else if (firstContained && secondContained) {
146 // Handled above already.
147 } else { // Mismatch between PointInPolygon and get_line_intersection. Treat it as no intersection
148 if (subLine.size())
149 res.push_back(subLine);
150 subLine.clear();
151 }
152 } else if (type == OneIntersection) { // Need to check the following cases to avoid mismatch with PointInPolygon result.
153 if (firstContained <= 0 && secondContained > 0) { // subLine MUST be empty
154 if (!subLine.size())
155 subLine.push_back(QDoubleVector2D(intersections[0], intersections[1]));
156 subLine.push_back(l.at(i+1));
157 } else if (firstContained > 0 && secondContained <= 0) { // subLine MUST NOT be empty
158 if (!subLine.size())
159 subLine.push_back(l.at(i));
160 subLine.push_back(QDoubleVector2D(intersections[0], intersections[1]));
161 res.push_back(subLine);
162 subLine.clear();
163 } else {
164 if (subLine.size())
165 res.push_back(subLine);
166 subLine.clear();
167 }
168 } else { // Two
169 // restart strip
170 subLine.clear();
171 subLine.push_back(QDoubleVector2D(intersections[0], intersections[1]));
172 subLine.push_back(QDoubleVector2D(intersections[2], intersections[3]));
173 res.push_back(subLine);
174 subLine.clear();
175 }
176 }
177
178 if (subLine.size())
179 res.push_back(subLine);
180 return res;
181}
182
183/*!
184 \qmltype MapPolyline
185 \nativetype QDeclarativePolylineMapItem
186 \inqmlmodule QtLocation
187 \ingroup qml-QtLocation5-maps
188 \since QtLocation 5.0
189
190 \brief The MapPolyline type displays a polyline on a map.
191
192 The MapPolyline type displays a polyline on a map, specified in terms of an
193 ordered list of \l [QtPositioning]{geoCoordinate}{coordinates}.
194
195 Coordinates can be added or removed at any time using the \l addCoordinate and
196 \l removeCoordinate methods. They can also be modified like any other list element in QML:
197
198 \code
199 mapPolyline.path[0].latitude = 5;
200 \endcode
201
202 By default, the polyline is displayed as a 1-pixel thick black line. This
203 can be changed using the \l line.width and \l line.color properties.
204
205 \section2 Example Usage
206
207 The following snippet shows a MapPolyline with 4 points, making a shape
208 like the top part of a "question mark" (?), near Brisbane, Australia.
209 The line drawn is 3 pixels in width and green in color.
210
211 \code
212 Map {
213 MapPolyline {
214 line.width: 3
215 line.color: 'green'
216 path: [
217 { latitude: -27, longitude: 153.0 },
218 { latitude: -27, longitude: 154.1 },
219 { latitude: -28, longitude: 153.5 },
220 { latitude: -29, longitude: 153.5 }
221 ]
222 }
223 }
224 \endcode
225
226 \image api-mappolyline.png
227*/
228
229/*!
230 \qmlproperty bool QtLocation::MapPolyline::autoFadeIn
231
232 This property holds whether the item automatically fades in when zooming into the map
233 starting from very low zoom levels. By default this is \c true.
234 Setting this property to \c false causes the map item to always have the opacity specified
235 with the \l QtQuick::Item::opacity property, which is 1.0 by default.
236
237 \since 5.14
238*/
239
240/*!
241 \qmlproperty enum QtLocation::MapPolyline::referenceSurface
242
243 This property determines the reference surface of the polyline. If it is set to
244 \l QLocation::ReferenceSurface::Map the polylines vertices are connected with straight
245 lines on the map. If it is set to \l QLocation::ReferenceSurface::Globe, the vertices
246 are connected following the great circle path, describing the shortest connection of
247 two points on a sphere.
248 Default value is \l QLocation::ReferenceSurface::Map.
249
250 \since 6.5
251*/
252
253QDeclarativeMapLineProperties::QDeclarativeMapLineProperties(QObject *parent)
254 : QObject(parent)
255{
256}
257
258/*!
259 \internal
260*/
261QColor QDeclarativeMapLineProperties::color() const
262{
263 return color_;
264}
265
266/*!
267 \internal
268*/
269void QDeclarativeMapLineProperties::setColor(const QColor &color)
270{
271 if (color_ == color)
272 return;
273
274 color_ = color;
275 emit colorChanged(color_);
276}
277
278/*!
279 \internal
280*/
281qreal QDeclarativeMapLineProperties::width() const
282{
283 return width_;
284}
285
286/*!
287 \internal
288*/
289void QDeclarativeMapLineProperties::setWidth(qreal width)
290{
291 if (width_ == width)
292 return;
293
294 width_ = width;
295 emit widthChanged(width_);
296}
297
298/*!
299 \internal
300*/
302 const QList<QDoubleVector2D> &basePath)
303{
304 // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes
305 // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly
306 if (!sourceDirty_)
307 return;
308 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
309 srcPath_ = QPainterPath();
310 srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0, 0)); //avoid warning of NaN values if function is returned early
311
312 //0 Wrap the points around the globe if the path makes more sense that way.
313 // Ultimately, this is done if it is closer to walk around the day-border than the other direction
314 QVarLengthArray<QList<QDoubleVector2D>, 3> wrappedPaths;
315 wrappedPaths << QList<QDoubleVector2D>({basePath[0]});
316 wrappedPaths.last().reserve(basePath.size());
317 for (int i = 1; i < basePath.size(); i++) {
318 if (basePath[i].x() > wrappedPaths.last().last().x() + 0.5)
319 wrappedPaths.last() << basePath[i] - QDoubleVector2D(1.0, 0.0);
320 else if (basePath[i].x() < wrappedPaths.last().last().x() - 0.5)
321 wrappedPaths.last() << basePath[i] + QDoubleVector2D(1.0, 0.0);
322 else
323 wrappedPaths.last() << basePath[i];
324 }
325
326 //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible
327 // The viewport is periodic in x-direction in the interval [-1; 1].
328 // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1;
329 const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
330 QRectF itemRect;
331 for (const auto &path : wrappedPaths)
332 itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path).adjusted(-1e-6, -1e-6, 2e-6, 2e-6); //TODO: Maybe use linewidth?
333 for (double xoffset : {-1.0, 1.0}) {
334 if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset,0))))
335 continue;
336 wrappedPaths.append(QList<QDoubleVector2D>());
337 QList<QDoubleVector2D> &wP = wrappedPaths.last();
338 wP.reserve(wrappedPaths.first().size());
339 for (const QDoubleVector2D &coord : wrappedPaths.first())
340 wP.append(coord + QDoubleVector2D(xoffset, 0.0));
341 }
342 if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle
343 return;
344
345 //2 The polygons that are at least partially in the viewport are cliped to reduce their size
346 QList<QList<QDoubleVector2D>> clippedPaths;
347 const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded();
348 for (const auto &path : wrappedPaths) {
349 if (visibleRegion.size()) {
350 clippedPaths << clipLine(path, visibleRegion);
351 //TODO: Replace clipping with Clipper lib, similar to QPolygonMapItem
352 } else {
353 clippedPaths.append(path); //Do we really need this if there are no visible regions??
354 }
355 }
356 if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion
357 return;
358
359 QRectF bb;
360 for (const auto &path: clippedPaths)
361 bb |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path);
362 //Offset by origin, find the maximum coordinate
363 maxCoord_ = 0.0;
364 srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top()));
365 QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections
366 for (const auto &path: clippedPaths) {
367 QDoubleVector2D lastAddedPoint;
368 for (qsizetype i = 0; i < path.size(); ++i) {
369 QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i));
370 point = point - origin; // (0,0) if point == origin
371
372 if (qMax(point.x(), point.y()) > maxCoord_)
373 maxCoord_ = qMax(point.x(), point.y());
374
375 if (i == 0) {
376 srcPath_.moveTo(point.toPointF());
377 lastAddedPoint = point;
378 } else {
379 if ((point - lastAddedPoint).manhattanLength() > 3 ||
380 i == path.size() - 1) {
381 srcPath_.lineTo(point.toPointF());
382 lastAddedPoint = point;
383 }
384 }
385 }
386 }
387
388 sourceBounds_ = srcPath_.boundingRect();
389}
390
391/*
392 * QDeclarativePolygonMapItem Private Implementations
393 */
394
395QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate()
396{
397}
398
399QDeclarativePolylineMapItemPrivateCPU::QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly)
400 : QDeclarativePolylineMapItemPrivate(poly)
401{
402 m_shape = new QQuickShape(&m_poly);
403 m_shape->setObjectName("_qt_map_item_shape");
404 m_shape->setZ(-1);
405 m_shape->setContainsMode(QQuickShape::FillContains);
406
407 m_shapePath = new QQuickShapePath(m_shape);
408 m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath);
409
410 auto pathElements = m_shapePath->pathElements();
411 pathElements.append(&pathElements, m_painterPath);
412
413 auto shapePaths = m_shape->data();
414 shapePaths.append(&shapePaths, m_shapePath);
415}
416
417QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU()
418{
419 delete m_shape;
420}
421
422void QDeclarativePolylineMapItemPrivateCPU::regenerateCache()
423{
424 if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
425 return;
426 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
427 m_geopathProjected.clear();
428 if (m_poly.referenceSurface() == QLocation::ReferenceSurface::Globe) {
429 const QList<QGeoCoordinate> realPath = QDeclarativeGeoMapItemUtils::greaterCirclePath(m_poly.m_geopath.path());
430 m_geopathProjected.reserve(realPath.size());
431 for (const QGeoCoordinate &c : realPath)
432 m_geopathProjected << p.geoToMapProjection(c);
433 } else {
434 m_geopathProjected.reserve(m_poly.m_geopath.path().size());
435 const QList<QGeoCoordinate> path = m_poly.m_geopath.path();
436 for (const QGeoCoordinate &c : path)
437 m_geopathProjected << p.geoToMapProjection(c);
438 }
439}
440
441void QDeclarativePolylineMapItemPrivateCPU::updateCache()
442{
443 if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
444 return;
445 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
446 m_geopathProjected << p.geoToMapProjection(m_poly.m_geopath.path().last());
447}
448
449void QDeclarativePolylineMapItemPrivateCPU::updatePolish()
450{
451 if (m_poly.m_geopath.path().length() < 2) { // Possibly cleared
452 m_geometry.clear();
453 m_poly.setWidth(0);
454 m_poly.setHeight(0);
455 m_shape->setVisible(false);
456 return;
457 }
458 QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
459 m_poly.m_updatingGeometry = true;
460
461 const QGeoMap *map = m_poly.map();
462 const qreal borderWidth = m_poly.m_line.width();
463
464 m_geometry.updateSourcePoints(*map, m_geopathProjected);
465
466 const QRectF bb = m_geometry.sourceBoundingBox();
467 m_poly.setSize(bb.size() + QSizeF(borderWidth, borderWidth));
468 // it has to be shifted so that the center of the line is on the correct geocoord
469 m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth) * 0.5);
470
471
472 m_poly.setShapeTriangulationScale(m_shape, m_geometry.maxCoord_);
473
474 m_shapePath->setStrokeColor(m_poly.m_line.color());
475 m_shapePath->setStrokeWidth(borderWidth);
476 m_shapePath->setFillColor(Qt::transparent);
477
478 QPainterPath path = m_geometry.srcPath();
479 path.translate(-bb.left() + borderWidth * 0.5, -bb.top() + borderWidth * 0.5);
480 m_painterPath->setPath(path);
481
482 m_shape->setSize(m_poly.size());
483 m_shape->setOpacity(m_poly.zoomLevelOpacity());
484 m_shape->setVisible(true);
485}
486
487QSGNode *QDeclarativePolylineMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode,
488 QQuickItem::UpdatePaintNodeData * /*data*/)
489{
490 delete oldNode;
491
492 if (m_geometry.isScreenDirty() || !oldNode) {
493 m_geometry.markClean();
494 }
495 return nullptr;
496}
497
498bool QDeclarativePolylineMapItemPrivateCPU::contains(const QPointF &point) const
499{
500 // With Shapes, do not just call
501 // m_shape->contains(m_poly.mapToItem(m_shape, point)) because that can
502 // only do FillContains at best, whereas the polyline relies on stroking.
503
504 const QPainterPath &path = m_shapePath->path();
505 const double &lineWidth = m_poly.m_line.width();
506 const QPointF p = m_poly.mapToItem(m_shape, point);
507
508 for (int i = 1; i < path.elementCount(); i++) {
509 if (path.elementAt(i).type == QPainterPath::MoveToElement)
510 continue;
511 const double dsqr = QDeclarativeGeoMapItemUtils::distanceSqrPointLine(p.x(), p.y(),
512 path.elementAt(i - 1).x, path.elementAt(i - 1).y,
513 path.elementAt(i).x, path.elementAt(i).y);
514 if (dsqr < 0.25 * lineWidth * lineWidth)
515 return true;
516 }
517 return false;
518}
519
520/*
521 * QDeclarativePolygonMapItem Implementation
522 */
523
524QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent)
525 : QDeclarativeGeoMapItemBase(parent), m_line(this),
526 m_d(new QDeclarativePolylineMapItemPrivateCPU(*this))
527{
528 m_itemType = QGeoMap::MapPolyline;
529 m_geopath = QGeoPathEager();
530 setFlag(ItemHasContents, true);
531 QObject::connect(&m_line, &QDeclarativeMapLineProperties::colorChanged,
532 this, &QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged);
533 QObject::connect(&m_line, &QDeclarativeMapLineProperties::widthChanged,
534 this, &QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged);
535 QObject::connect(this, &QDeclarativePolylineMapItem::referenceSurfaceChanged,
536 this, [this]() { m_d->onGeoGeometryChanged(); });
537}
538
539QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
540{
541}
542
543/*!
544 \internal
545*/
546void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
547{
548 m_d->onLinePropertiesChanged();
549}
550
551/*!
552 \internal
553*/
554void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
555{
556 QDeclarativeGeoMapItemBase::setMap(quickMap,map);
557 if (map)
558 m_d->onMapSet();
559}
560
561/*!
562 \qmlproperty list<coordinate> MapPolyline::path
563
564 This property holds the ordered list of coordinates which
565 define the polyline.
566*/
567
568QList<QGeoCoordinate> QDeclarativePolylineMapItem::path() const
569{
570 return m_geopath.path();
571}
572
573void QDeclarativePolylineMapItem::setPath(const QList<QGeoCoordinate> &value)
574{
575 setPathFromGeoList(value);
576}
577
578/*!
579 \qmlmethod void MapPolyline::setPath(geoPath path)
580
581 Sets the \a path using a geoPath type.
582
583 \since 5.10
584
585 \sa path
586*/
587void QDeclarativePolylineMapItem::setPath(const QGeoPath &path)
588{
589 if (m_geopath.path() == path.path())
590 return;
591
592 m_geopath = QGeoPathEager(path);
593 m_d->onGeoGeometryChanged();
594 emit pathChanged();
595}
596
597/*!
598 \internal
599*/
600void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> &path)
601{
602 if (m_geopath.path() == path)
603 return;
604
605 m_geopath.setPath(path);
606
607 m_d->onGeoGeometryChanged();
608 emit pathChanged();
609}
610
611/*!
612 \qmlmethod int MapPolyline::pathLength()
613
614 Returns the number of coordinates of the polyline.
615
616 \since QtLocation 5.6
617
618 \sa path
619*/
620int QDeclarativePolylineMapItem::pathLength() const
621{
622 return m_geopath.path().length();
623}
624
625/*!
626 \qmlmethod void MapPolyline::addCoordinate(coordinate)
627
628 Adds the specified \a coordinate to the end of the path.
629
630 \sa insertCoordinate, removeCoordinate, path
631*/
632void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate)
633{
634 if (!coordinate.isValid())
635 return;
636
637 m_geopath.addCoordinate(coordinate);
638
639 m_d->onGeoGeometryUpdated();
640 emit pathChanged();
641}
642
643/*!
644 \qmlmethod void MapPolyline::insertCoordinate(index, coordinate)
645
646 Inserts a \a coordinate to the path at the given \a index.
647
648 \since QtLocation 5.6
649
650 \sa addCoordinate, removeCoordinate, path
651*/
652void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate)
653{
654 if (index < 0 || index > m_geopath.path().length())
655 return;
656
657 m_geopath.insertCoordinate(index, coordinate);
658
659 m_d->onGeoGeometryChanged();
660 emit pathChanged();
661}
662
663/*!
664 \qmlmethod void MapPolyline::replaceCoordinate(index, coordinate)
665
666 Replaces the coordinate in the current path at the given \a index
667 with the new \a coordinate.
668
669 \since QtLocation 5.6
670
671 \sa addCoordinate, insertCoordinate, removeCoordinate, path
672*/
673void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate)
674{
675 if (index < 0 || index >= m_geopath.path().length())
676 return;
677
678 m_geopath.replaceCoordinate(index, coordinate);
679
680 m_d->onGeoGeometryChanged();
681 emit pathChanged();
682}
683
684/*!
685 \qmlmethod coordinate MapPolyline::coordinateAt(index)
686
687 Gets the coordinate of the polyline at the given \a index.
688 If the index is outside the path's bounds then an invalid
689 coordinate is returned.
690
691 \since QtLocation 5.6
692*/
693QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
694{
695 if (index < 0 || index >= m_geopath.path().length())
696 return QGeoCoordinate();
697
698 return m_geopath.coordinateAt(index);
699}
700
701/*!
702 \qmlmethod coordinate MapPolyline::containsCoordinate(coordinate)
703
704 Returns true if the given \a coordinate is part of the path.
705
706 \since QtLocation 5.6
707*/
708bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate)
709{
710 return m_geopath.containsCoordinate(coordinate);
711}
712
713/*!
714 \qmlmethod void MapPolyline::removeCoordinate(coordinate)
715
716 Removes \a coordinate from the path. If there are multiple instances of the
717 same coordinate, the one added last is removed.
718
719 If \a coordinate is not in the path this method does nothing.
720
721 \sa addCoordinate, insertCoordinate, path
722*/
723void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate)
724{
725 int length = m_geopath.path().length();
726 m_geopath.removeCoordinate(coordinate);
727 if (m_geopath.path().length() == length)
728 return;
729
730 m_d->onGeoGeometryChanged();
731 emit pathChanged();
732}
733
734/*!
735 \qmlmethod void MapPolyline::removeCoordinate(index)
736
737 Removes a coordinate from the path at the given \a index.
738
739 If \a index is invalid then this method does nothing.
740
741 \since QtLocation 5.6
742
743 \sa addCoordinate, insertCoordinate, path
744*/
745void QDeclarativePolylineMapItem::removeCoordinate(int index)
746{
747 if (index < 0 || index >= m_geopath.path().length())
748 return;
749
750 m_geopath.removeCoordinate(index);
751
752 m_d->onGeoGeometryChanged();
753 emit pathChanged();
754}
755
756/*!
757 \qmlpropertygroup Location::MapPolyline::line
758 \qmlproperty int MapPolyline::line.width
759 \qmlproperty color MapPolyline::line.color
760
761 This property is part of the line property group. The line
762 property group holds the width and color used to draw the line.
763
764 The width is in pixels and is independent of the zoom level of the map.
765 The default values correspond to a black border with a width of 1 pixel.
766
767 For no line, use a width of 0 or a transparent color.
768*/
769
770QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line()
771{
772 return &m_line;
773}
774
775/*!
776 \internal
777*/
778void QDeclarativePolylineMapItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
779{
780 if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopath.isValid() || m_updatingGeometry) {
781 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
782 return;
783 }
784 // TODO: change the algorithm to preserve the distances and size!
785 QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false);
786 QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false);
787 if (!newCenter.isValid() || !oldCenter.isValid())
788 return;
789 double offsetLongi = newCenter.longitude() - oldCenter.longitude();
790 double offsetLati = newCenter.latitude() - oldCenter.latitude();
791 if (offsetLati == 0.0 && offsetLongi == 0.0)
792 return;
793
794 m_geopath.translate(offsetLati, offsetLongi);
795 m_d->onGeoGeometryChanged();
796 emit pathChanged();
797
798 // Not calling QDeclarativeGeoMapItemBase::geometryChange() as it will be called from a nested
799 // call to this function.
800}
801
802/*!
803 \internal
804*/
805void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
806{
807 if (event.mapSize.isEmpty())
808 return;
809
810 m_d->afterViewportChanged();
811}
812
813/*!
814 \internal
815*/
816void QDeclarativePolylineMapItem::updatePolish()
817{
818 if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
819 return;
820 m_d->updatePolish();
821}
822
823/*!
824 \internal
825*/
826QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
827{
828 return m_d->updateMapItemPaintNode(oldNode, data);
829}
830
831bool QDeclarativePolylineMapItem::contains(const QPointF &point) const
832{
833 return m_d->contains(point);
834}
835
836const QGeoShape &QDeclarativePolylineMapItem::geoShape() const
837{
838 return m_geopath;
839}
840
841void QDeclarativePolylineMapItem::setGeoShape(const QGeoShape &shape)
842{
843 const QGeoPath geopath(shape); // if shape isn't a path, path will be created as a default-constructed path
844 setPath(geopath);
845}
846
847//////////////////////////////////////////////////////////////////////
848
849QT_END_NAMESPACE
static QT_BEGIN_NAMESPACE bool get_line_intersection(const double p0_x, const double p0_y, const double p1_x, const double p1_y, const double p2_x, const double p2_y, const double p3_x, const double p3_y, double *i_x, double *i_y, double *i_t)
static QList< QList< QDoubleVector2D > > clipLine(const QList< QDoubleVector2D > &l, const QList< QDoubleVector2D > &poly)
void updateSourcePoints(const QGeoMap &map, const QList< QDoubleVector2D > &basePath)