/*! \internal */ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, const QList<QGeoCoordinate> &path) { if (!sourceDirty_) return; qreal minX = -1.0; // build the actual path QPointF origin; QPointF lastPoint; srcPath_ = QPainterPath(); double unwrapBelowX = 0; if (preserveGeometry_ ) unwrapBelowX = map.coordinateToScreenPosition(geoLeftBound_, false).x(); for (int i = 0; i < path.size(); ++i) { const QGeoCoordinate &coord = path.at(i); if (!coord.isValid()) continue; QPointF point = map.coordinateToScreenPosition(coord, false); // We can get NaN if the map isn't set up correctly, or the projection // is faulty -- probably best thing to do is abort if (!qIsFinite(point.x()) || !qIsFinite(point.y())) return; // unwrap x to preserve geometry if moved to border of map if (preserveGeometry_ && point.x() < unwrapBelowX && !qFuzzyCompare(point.x(), unwrapBelowX)) point.setX(unwrapBelowX + geoDistanceToScreenWidth(map, geoLeftBound_, coord)); if (i == 0) { origin = point; minX = point.x(); srcOrigin_ = coord; srcPath_.moveTo(point - origin); lastPoint = point; } else { if (point.x() <= minX) minX = point.x(); const QPointF diff = (point - lastPoint); if (diff.x() * diff.x() + diff.y() * diff.y() >= 3.0) { srcPath_.lineTo(point - origin); lastPoint = point; } } } srcPath_.closeSubpath(); if (!assumeSimple_) srcPath_ = srcPath_.simplified(); sourceBounds_ = srcPath_.boundingRect(); geoLeftBound_ = map.screenPositionToCoordinate(QPointF(minX, 0), false); }
/*! \internal */ void QGeoMapRectangleGeometry::updatePoints(const QGeoMap &map, const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight) { if (!screenDirty_ && !sourceDirty_) return; QPointF tl = map.coordinateToScreenPosition(topLeft, false); QPointF br = map.coordinateToScreenPosition(bottomRight, false); // We can get NaN if the map isn't set up correctly, or the projection // is faulty -- probably best thing to do is abort if (!qIsFinite(tl.x()) || !qIsFinite(tl.y())) return; if (!qIsFinite(br.x()) || !qIsFinite(br.y())) return; if ( preserveGeometry_ ) { double unwrapBelowX = map.coordinateToScreenPosition(geoLeftBound_, false).x(); if (br.x() < unwrapBelowX) br.setX(tl.x() + screenBounds_.width()); } QRectF re(tl, br); re.translate(-1 * tl); clear(); screenVertices_.reserve(6); screenVertices_ << re.topLeft(); screenVertices_ << re.topRight(); screenVertices_ << re.bottomLeft(); screenVertices_ << re.topRight(); screenVertices_ << re.bottomLeft(); screenVertices_ << re.bottomRight(); firstPointOffset_ = QPointF(0,0); srcOrigin_ = topLeft; screenBounds_ = re; screenOutline_ = QPainterPath(); screenOutline_.addRect(re); geoLeftBound_ = topLeft; }
/*! \internal */ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, qreal strokeWidth) { if (!screenDirty_) return; QPointF origin = map.coordinateToScreenPosition(srcOrigin_, false).toPointF(); if (!qIsFinite(origin.x()) || !qIsFinite(origin.y())) { clear(); return; } // Create the viewport rect in the same coordinate system // as the actual points QRectF viewport(0, 0, map.width(), map.height()); viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth, strokeWidth); viewport.translate(-1 * origin); // Perform clipping to the viewport limits QVector<qreal> points; QVector<QPainterPath::ElementType> types; if (clipToViewport_) { clipPathToRect(srcPoints_, srcPointTypes_, viewport, points, types); } else { points = srcPoints_; types = srcPointTypes_; } QVectorPath vp(points.data(), types.size(), types.data()); QTriangulatingStroker ts; ts.process(vp, QPen(QBrush(Qt::black), strokeWidth), viewport, QPainter::Qt4CompatiblePainting); clear(); // Nothing is on the screen if (ts.vertexCount() == 0) return; // QTriangulatingStroker#vertexCount is actually the length of the array, // not the number of vertices screenVertices_.reserve(ts.vertexCount()); screenOutline_ = QPainterPath(); QPolygonF tri; const float *vs = ts.vertices(); for (int i = 0; i < (ts.vertexCount()/2*2); i += 2) { screenVertices_ << QPointF(vs[i], vs[i + 1]); if (!qIsFinite(vs[i]) || !qIsFinite(vs[i + 1])) break; tri << QPointF(vs[i], vs[i + 1]); if (tri.size() == 4) { tri.remove(0); screenOutline_.addPolygon(tri); } } QRectF bb = screenOutline_.boundingRect(); screenBounds_ = bb; this->translate( -1 * sourceBounds_.topLeft()); }
/*! \internal */ void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map, const QList<QGeoCoordinate> &path) { bool foundValid = false; double minX = -1.0; double minY = -1.0; double maxX = -1.0; double maxY = -1.0; if (!sourceDirty_) return; // clear the old data and reserve enough memory srcPoints_.clear(); srcPoints_.reserve(path.size() * 2); srcPointTypes_.clear(); srcPointTypes_.reserve(path.size()); QDoubleVector2D origin, lastPoint, lastAddedPoint; double unwrapBelowX = 0; if (preserveGeometry_) unwrapBelowX = map.coordinateToScreenPosition(geoLeftBound_, false).x(); for (int i = 0; i < path.size(); ++i) { const QGeoCoordinate &coord = path.at(i); if (!coord.isValid()) continue; QDoubleVector2D point = map.coordinateToScreenPosition(coord, false); // We can get NaN if the map isn't set up correctly, or the projection // is faulty -- probably best thing to do is abort if (!qIsFinite(point.x()) || !qIsFinite(point.y())) return; // unwrap x to preserve geometry if moved to border of map if (preserveGeometry_ && point.x() < unwrapBelowX && !qFuzzyCompare(point.x(), unwrapBelowX)) point.setX(unwrapBelowX + geoDistanceToScreenWidth(map, geoLeftBound_, coord)); if (!foundValid) { foundValid = true; srcOrigin_ = coord; origin = point; point = QDoubleVector2D(0,0); minX = point.x(); maxX = minX; minY = point.y(); maxY = minY; srcPoints_ << point.x() << point.y(); srcPointTypes_ << QPainterPath::MoveToElement; lastAddedPoint = point; } else { point -= origin; minX = qMin(point.x(), minX); minY = qMin(point.y(), minY); maxX = qMax(point.x(), maxX); maxY = qMax(point.y(), maxY); if ((point - lastAddedPoint).manhattanLength() > 3 || i == path.size() - 1) { srcPoints_ << point.x() << point.y(); srcPointTypes_ << QPainterPath::LineToElement; lastAddedPoint = point; } } lastPoint = point; } sourceBounds_ = QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); geoLeftBound_ = map.screenPositionToCoordinate( QDoubleVector2D(minX + origin.x(), minY + origin.y()), false); }
/*! \internal */ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map) { if (!screenDirty_) return; if (map.width() == 0 || map.height() == 0) { clear(); return; } QPointF origin = map.coordinateToScreenPosition(srcOrigin_, false); // Create the viewport rect in the same coordinate system // as the actual points QRectF viewport(0, 0, map.width(), map.height()); viewport.translate(-1 * origin); QPainterPath vpPath; vpPath.addRect(viewport); QPainterPath ppi; if (clipToViewport_) ppi = srcPath_.intersected(vpPath); // get the clipped version of the path else ppi = srcPath_; clear(); // a polygon requires at least 3 points; if (ppi.elementCount() < 3) return; // Intersection between the viewport and a concave polygon can create multiple polygons // joined by a line at the viewport border, and poly2tri does not triangulate this very well // so use the full src path if the resulting polygon is concave. if (clipToViewport_) { int changeInX = 0; int changeInY = 0; QPainterPath::Element e1 = ppi.elementAt(1); QPainterPath::Element e = ppi.elementAt(0); QVector2D edgeA(e1.x - e.x ,e1.y - e.y); for (int i = 2; i <= ppi.elementCount(); ++i) { e = ppi.elementAt(i % ppi.elementCount()); if (e.x == e1.x && e.y == e1.y) continue; QVector2D edgeB(e.x - e1.x, e.y - e1.y); if ((edgeA.x() < 0) == (edgeB.x() >= 0)) changeInX++; if ((edgeA.y() < 0) == (edgeB.y() >= 0)) changeInY++; edgeA = edgeB; e1 = e; } if (changeInX > 2 || changeInY > 2) // polygon is concave ppi = srcPath_; } // translate the path into top-left-centric coordinates QRectF bb = ppi.boundingRect(); ppi.translate(-bb.left(), -bb.top()); firstPointOffset_ = -1 * bb.topLeft(); ppi.closeSubpath(); screenOutline_ = ppi; std::vector<p2t::Point*> curPts; curPts.reserve(ppi.elementCount()); for (int i = 0; i < ppi.elementCount(); ++i) { const QPainterPath::Element e = ppi.elementAt(i); if (e.isMoveTo() || i == ppi.elementCount() - 1 || (qAbs(e.x - curPts.front()->x) < 0.1 && qAbs(e.y - curPts.front()->y) < 0.1)) { if (curPts.size() > 2) { p2t::CDT *cdt = new p2t::CDT(curPts); cdt->Triangulate(); std::vector<p2t::Triangle*> tris = cdt->GetTriangles(); screenVertices_.reserve(screenVertices_.size() + int(tris.size())); for (size_t i = 0; i < tris.size(); ++i) { p2t::Triangle *t = tris.at(i); for (int j = 0; j < 3; ++j) { p2t::Point *p = t->GetPoint(j); screenVertices_ << Point(p->x, p->y); } } delete cdt; } curPts.clear(); curPts.reserve(ppi.elementCount() - i); curPts.push_back(new p2t::Point(e.x, e.y)); } else if (e.isLineTo()) { curPts.push_back(new p2t::Point(e.x, e.y)); } else { qWarning("Unhandled element type in polygon painterpath"); } } if (curPts.size() > 0) { qDeleteAll(curPts.begin(), curPts.end()); curPts.clear(); } screenBounds_ = ppi.boundingRect(); }
/*! \internal */ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QGeoMap &map) { if (!screenDirty_) return; if (map.width() == 0 || map.height() == 0) { clear(); return; } QPointF origin = map.coordinateToItemPosition(srcOrigin_, false).toPointF(); QPainterPath ppi = srcPath_; clear(); // a circle requires at least 3 points; if (ppi.elementCount() < 3) return; // translate the path into top-left-centric coordinates QRectF bb = ppi.boundingRect(); ppi.translate(-bb.left(), -bb.top()); firstPointOffset_ = -1 * bb.topLeft(); ppi.closeSubpath(); // calculate actual width of map on screen in pixels QGeoCoordinate mapCenter(0, map.cameraData().center().longitude()); QDoubleVector2D midPoint = map.coordinateToItemPosition(mapCenter, false); QDoubleVector2D midPointPlusOne = QDoubleVector2D(midPoint.x() + 1.0, midPoint.y()); QGeoCoordinate coord1 = map.itemPositionToCoordinate(midPointPlusOne, false); double geoDistance = coord1.longitude() - map.cameraData().center().longitude(); if ( geoDistance < 0 ) geoDistance += 360.0; double mapWidth = 360.0 / geoDistance; qreal leftOffset = origin.x() - (map.width()/2.0 - mapWidth/2.0) - firstPointOffset_.x(); qreal topOffset = origin.y() - (midPoint.y() - mapWidth/2.0) - firstPointOffset_.y(); QPainterPath ppiBorder; ppiBorder.moveTo(QPointF(-leftOffset, -topOffset)); ppiBorder.lineTo(QPointF(mapWidth - leftOffset, -topOffset)); ppiBorder.lineTo(QPointF(mapWidth - leftOffset, mapWidth - topOffset)); ppiBorder.lineTo(QPointF(-leftOffset, mapWidth - topOffset)); screenOutline_ = ppiBorder; std::vector<p2t::Point*> borderPts; borderPts.reserve(4); std::vector<p2t::Point*> curPts; curPts.reserve(ppi.elementCount()); for (int i = 0; i < ppi.elementCount(); ++i) { const QPainterPath::Element e = ppi.elementAt(i); if (e.isMoveTo() || i == ppi.elementCount() - 1 || (qAbs(e.x - curPts.front()->x) < 0.1 && qAbs(e.y - curPts.front()->y) < 0.1)) { if (curPts.size() > 2) { for (int j = 0; j < 4; ++j) { const QPainterPath::Element e2 = ppiBorder.elementAt(j); borderPts.push_back(new p2t::Point(e2.x, e2.y)); } p2t::CDT *cdt = new p2t::CDT(borderPts); cdt->AddHole(curPts); cdt->Triangulate(); std::vector<p2t::Triangle*> tris = cdt->GetTriangles(); screenVertices_.reserve(screenVertices_.size() + int(tris.size())); for (size_t i = 0; i < tris.size(); ++i) { p2t::Triangle *t = tris.at(i); for (int j = 0; j < 3; ++j) { p2t::Point *p = t->GetPoint(j); screenVertices_ << QPointF(p->x, p->y); } } delete cdt; } curPts.clear(); curPts.reserve(ppi.elementCount() - i); curPts.push_back(new p2t::Point(e.x, e.y)); } else if (e.isLineTo()) { curPts.push_back(new p2t::Point(e.x, e.y)); } else { qWarning("Unhandled element type in circle painterpath"); } } if (curPts.size() > 0) { qDeleteAll(curPts.begin(), curPts.end()); curPts.clear(); } if (borderPts.size() > 0) { qDeleteAll(borderPts.begin(), borderPts.end()); borderPts.clear(); } screenBounds_ = ppiBorder.boundingRect(); }