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