112QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
113: QDeclarativeGeoMapItemBase(parent), m_border(
this), m_color(Qt::transparent),
114 m_updatingGeometry(
false)
115 , m_d(
new QDeclarativeCircleMapItemPrivateCPU(*
this))
118 m_itemType = QGeoMap::MapCircle;
119 setFlag(ItemHasContents,
true);
120 QObject::connect(&m_border, &QDeclarativeMapLineProperties::colorChanged,
121 this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
122 QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged,
123 this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
124 QObject::connect(
this, &QDeclarativeCircleMapItem::referenceSurfaceChanged,
this,
125 [
this]() {m_d->onGeoGeometryChanged();});
303void QDeclarativeCircleMapItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
305 if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) {
306 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
310 QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5;
311 QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint,
false);
312 if (newCoordinate.isValid())
313 setCenter(newCoordinate);
323QDeclarativeCircleMapItemPrivateCPU::QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle)
324 : QDeclarativeCircleMapItemPrivate(circle)
326 m_shape =
new QQuickShape(&m_circle);
327 m_shape->setObjectName(
"_qt_map_item_shape");
329 m_shape->setContainsMode(QQuickShape::FillContains);
331 m_shapePath =
new QQuickShapePath(m_shape);
332 m_painterPath =
new QDeclarativeGeoMapPainterPath(m_shapePath);
334 auto pathElements = m_shapePath->pathElements();
335 pathElements.append(&pathElements, m_painterPath);
337 auto shapePaths = m_shape->data();
338 shapePaths.append(&shapePaths, m_shapePath);
353void QDeclarativeCircleMapItemPrivate::includeOnePoleInPath(QList<QDoubleVector2D> &path,
354 const QGeoCoordinate ¢er,
355 qreal distance,
const QGeoProjectionWebMercator &p)
357 const qreal poleLat = 90;
358 const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0));
359 const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0));
360 const bool crossNorthPole = distanceToNorthPole < distance;
361 const bool crossSouthPole = distanceToSouthPole < distance;
363 if (!crossNorthPole && !crossSouthPole)
366 if (crossNorthPole && crossSouthPole)
369 const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
370 const qreal xAtBorder = cameraRect.left();
377 for (
auto &c : path) {
379 while (c.x() - xAtBorder > 1.0)
381 while (c.x() - xAtBorder < 0.0)
385 std::sort(path.begin(), path.end(),
386 [](
const QDoubleVector2D &a,
const QDoubleVector2D &b) ->
bool
387 {
return a.x() < b.x();});
389 const qreal newPoleLat = crossNorthPole ? -0.1 : 1.1;
390 const QDoubleVector2D P1 = path.first() + QDoubleVector2D(1.0, 0.0);
391 const QDoubleVector2D P2 = path.last() - QDoubleVector2D(1.0, 0.0);
393 path.push_front(QDoubleVector2D(P2.x(), newPoleLat));
395 path.append(QDoubleVector2D(P1.x(), newPoleLat));
398int QDeclarativeCircleMapItemPrivate::crossEarthPole(
const QGeoCoordinate ¢er, qreal distance)
401 QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
402 QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
404 qreal distanceToNorthPole = center.distanceTo(northPole);
405 qreal distanceToSouthPole = center.distanceTo(southPole);
406 return (distanceToNorthPole < distance? 1 : 0) +
407 (distanceToSouthPole < distance? 1 : 0);
427void QDeclarativeCircleMapItemPrivate::calculatePeripheralPointsGreatCircle(QList<QDoubleVector2D> &path,
428 const QGeoCoordinate ¢er,
430 const QGeoProjectionWebMercator &p,
438 steps = qMax(steps, 3);
439 qreal centerLon = center.longitude();
440 qreal latRad = QLocationUtils::radians(center.latitude());
441 qreal lonRad = QLocationUtils::radians(centerLon);
442 qreal cosLatRad = std::cos(latRad);
443 qreal sinLatRad = std::sin(latRad);
444 qreal ratio = (distance / QLocationUtils::earthMeanRadius());
445 qreal cosRatio = std::cos(ratio);
446 qreal sinRatio = std::sin(ratio);
447 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
448 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
449 for (
int i = 0; i < steps; ++i) {
450 const qreal azimuthRad = 2 *
M_PI * i / steps;
451 const qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
452 + cosLatRad_x_sinRatio * std::cos(azimuthRad));
453 const qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
454 cosRatio - sinLatRad * std::sin(resultLatRad));
455 const qreal lat2 = QLocationUtils::degrees(resultLatRad);
456 qreal lon2 = QLocationUtils::degrees(resultLonRad);
460 while (lon2 > 180.0) {
464 while (lon2 < -180.0) {
468 path << p.geoToMapProjection(QGeoCoordinate(lat2, lon2, center.altitude())) + QDoubleVector2D(offset, 0.0);
474void QDeclarativeCircleMapItemPrivateCPU::updatePolish()
476 if (!m_circle.m_circle.isValid()) {
478 m_circle.setWidth(0);
479 m_circle.setHeight(0);
480 m_shape->setVisible(
false);
484 const QGeoProjectionWebMercator &p =
static_cast<
const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
485 QScopedValueRollback<
bool> rollback(m_circle.m_updatingGeometry);
486 m_circle.m_updatingGeometry =
true;
488 QList<QDoubleVector2D> circlePath = m_circlePath;
490 const QGeoCoordinate ¢er = m_circle.m_circle.center();
491 const qreal &radius = m_circle.m_circle.radius();
494 int crossingPoles = m_circle.referenceSurface() == QLocation::ReferenceSurface::Globe ? crossEarthPole(center, radius) : 0;
495 if (crossingPoles == 1) {
496 includeOnePoleInPath(circlePath, center, radius, p);
497 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce);
499 else if (crossingPoles == 2) {
503 const qreal centerX = p.geoToMapProjection(center).x();
504 for (
int i = 0; i < circlePath.count(); i++) {
505 if (circlePath.at(i).x() > centerX)
506 circlePath[i].setX(circlePath.at(i).x() - 1.0);
508 QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
509 const QRectF circleRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(circlePath);
510 QGeoMapPolygonGeometry::MapBorderBehaviour wrappingMode = QGeoMapPolygonGeometry::DrawOnce;
511 QList<QDoubleVector2D> surroundingRect;
512 if (cameraRect.contains(circleRect)){
513 cameraRect = cameraRect.adjusted(-0.1, -0.1, 0.2, 0.2);
514 surroundingRect = {{cameraRect.left(), cameraRect.top()}, {cameraRect.right(), cameraRect.top()},
515 {cameraRect.right(), cameraRect.bottom()}, {cameraRect.left() , cameraRect.bottom()}};
517 const qreal anchorRect = centerX;
519 surroundingRect = {{anchorRect, -0.1}, {anchorRect + 1.0, -0.1},
520 {anchorRect + 1.0, 1.1}, {anchorRect, 1.1}};
521 wrappingMode = QGeoMapPolygonGeometry::Duplicate;
523 m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode);
525 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath});
528 m_circle.setShapeTriangulationScale(m_shape, m_geometry.maxCoord());
530 const bool hasBorder = m_circle.m_border.color().alpha() != 0 && m_circle.m_border.width() > 0;
531 const float borderWidth = hasBorder ? m_circle.m_border.width() : 0.0f;
532 m_shapePath->setStrokeColor(hasBorder ? m_circle.m_border.color() : Qt::transparent);
533 m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f);
534 m_shapePath->setFillColor(m_circle.color());
536 const QRectF bb = m_geometry.sourceBoundingBox();
537 QPainterPath path = m_geometry.srcPath();
538 path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth);
540 m_painterPath->setPath(path);
542 m_circle.setSize(bb.size());
543 m_shape->setSize(m_circle.size());
544 m_shape->setOpacity(m_circle.zoomLevelOpacity());
545 m_shape->setVisible(
true);
547 m_circle.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth));