113QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
114: QDeclarativeGeoMapItemBase(parent), m_border(
this), m_color(Qt::transparent),
115 m_updatingGeometry(
false)
116 , m_d(
new QDeclarativeCircleMapItemPrivateCPU(*
this))
119 m_itemType = QGeoMap::MapCircle;
120 setFlag(ItemHasContents,
true);
121 QObject::connect(&m_border, &QDeclarativeMapLineProperties::colorChanged,
122 this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
123 QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged,
124 this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
125 QObject::connect(
this, &QDeclarativeCircleMapItem::referenceSurfaceChanged,
this,
126 [
this]() {m_d->onGeoGeometryChanged();});
304void QDeclarativeCircleMapItem::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
306 if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) {
307 QDeclarativeGeoMapItemBase::geometryChange(newGeometry, oldGeometry);
311 QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5;
312 QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint,
false);
313 if (newCoordinate.isValid())
314 setCenter(newCoordinate);
324QDeclarativeCircleMapItemPrivateCPU::QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle)
325 : QDeclarativeCircleMapItemPrivate(circle)
327 m_shape =
new QQuickShape(&m_circle);
328 m_shape->setObjectName(
"_qt_map_item_shape");
330 m_shape->setContainsMode(QQuickShape::FillContains);
332 m_shapePath =
new QQuickShapePath(m_shape);
333 m_painterPath =
new QDeclarativeGeoMapPainterPath(m_shapePath);
335 auto pathElements = m_shapePath->pathElements();
336 pathElements.append(&pathElements, m_painterPath);
338 auto shapePaths = m_shape->data();
339 shapePaths.append(&shapePaths, m_shapePath);
354void QDeclarativeCircleMapItemPrivate::includeOnePoleInPath(QList<QDoubleVector2D> &path,
355 const QGeoCoordinate ¢er,
356 qreal distance,
const QGeoProjectionWebMercator &p)
358 const qreal poleLat = 90;
359 const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0));
360 const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0));
361 const bool crossNorthPole = distanceToNorthPole < distance;
362 const bool crossSouthPole = distanceToSouthPole < distance;
364 if (!crossNorthPole && !crossSouthPole)
367 if (crossNorthPole && crossSouthPole)
370 const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
371 const qreal xAtBorder = cameraRect.left();
378 for (
auto &c : path) {
380 while (c.x() - xAtBorder > 1.0)
382 while (c.x() - xAtBorder < 0.0)
386 std::sort(path.begin(), path.end(),
387 [](
const QDoubleVector2D &a,
const QDoubleVector2D &b) ->
bool
388 {
return a.x() < b.x();});
390 const qreal newPoleLat = crossNorthPole ? -0.1 : 1.1;
391 const QDoubleVector2D P1 = path.first() + QDoubleVector2D(1.0, 0.0);
392 const QDoubleVector2D P2 = path.last() - QDoubleVector2D(1.0, 0.0);
394 path.push_front(QDoubleVector2D(P2.x(), newPoleLat));
396 path.append(QDoubleVector2D(P1.x(), newPoleLat));
399int QDeclarativeCircleMapItemPrivate::crossEarthPole(
const QGeoCoordinate ¢er, qreal distance)
402 QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
403 QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
405 qreal distanceToNorthPole = center.distanceTo(northPole);
406 qreal distanceToSouthPole = center.distanceTo(southPole);
407 return (distanceToNorthPole < distance? 1 : 0) +
408 (distanceToSouthPole < distance? 1 : 0);
428void QDeclarativeCircleMapItemPrivate::calculatePeripheralPointsGreatCircle(QList<QDoubleVector2D> &path,
429 const QGeoCoordinate ¢er,
431 const QGeoProjectionWebMercator &p,
439 steps = qMax(steps, 3);
440 qreal centerLon = center.longitude();
441 qreal latRad = QLocationUtils::radians(center.latitude());
442 qreal lonRad = QLocationUtils::radians(centerLon);
443 qreal cosLatRad = std::cos(latRad);
444 qreal sinLatRad = std::sin(latRad);
445 qreal ratio = (distance / QLocationUtils::earthMeanRadius());
446 qreal cosRatio = std::cos(ratio);
447 qreal sinRatio = std::sin(ratio);
448 qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
449 qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
450 for (
int i = 0; i < steps; ++i) {
451 const qreal azimuthRad = 2 *
M_PI * i / steps;
452 const qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
453 + cosLatRad_x_sinRatio * std::cos(azimuthRad));
454 const qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
455 cosRatio - sinLatRad * std::sin(resultLatRad));
456 const qreal lat2 = QLocationUtils::degrees(resultLatRad);
457 qreal lon2 = QLocationUtils::degrees(resultLonRad);
461 while (lon2 > 180.0) {
465 while (lon2 < -180.0) {
469 path << p.geoToMapProjection(QGeoCoordinate(lat2, lon2, center.altitude())) + QDoubleVector2D(offset, 0.0);
475void QDeclarativeCircleMapItemPrivateCPU::updatePolish()
477 if (!m_circle.m_circle.isValid()) {
479 m_circle.setWidth(0);
480 m_circle.setHeight(0);
481 m_shape->setVisible(
false);
485 const QGeoProjectionWebMercator &p =
static_cast<
const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
486 QScopedValueRollback<
bool> rollback(m_circle.m_updatingGeometry);
487 m_circle.m_updatingGeometry =
true;
489 QList<QDoubleVector2D> circlePath = m_circlePath;
491 const QGeoCoordinate ¢er = m_circle.m_circle.center();
492 const qreal &radius = m_circle.m_circle.radius();
495 int crossingPoles = m_circle.referenceSurface() == QLocation::ReferenceSurface::Globe ? crossEarthPole(center, radius) : 0;
496 if (crossingPoles == 1) {
497 includeOnePoleInPath(circlePath, center, radius, p);
498 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce);
500 else if (crossingPoles == 2) {
504 const qreal centerX = p.geoToMapProjection(center).x();
505 for (
int i = 0; i < circlePath.count(); i++) {
506 if (circlePath.at(i).x() > centerX)
507 circlePath[i].setX(circlePath.at(i).x() - 1.0);
509 QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
510 const QRectF circleRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(circlePath);
511 QGeoMapPolygonGeometry::MapBorderBehaviour wrappingMode = QGeoMapPolygonGeometry::DrawOnce;
512 QList<QDoubleVector2D> surroundingRect;
513 if (cameraRect.contains(circleRect)){
514 cameraRect = cameraRect.adjusted(-0.1, -0.1, 0.2, 0.2);
515 surroundingRect = {{cameraRect.left(), cameraRect.top()}, {cameraRect.right(), cameraRect.top()},
516 {cameraRect.right(), cameraRect.bottom()}, {cameraRect.left() , cameraRect.bottom()}};
518 const qreal anchorRect = centerX;
520 surroundingRect = {{anchorRect, -0.1}, {anchorRect + 1.0, -0.1},
521 {anchorRect + 1.0, 1.1}, {anchorRect, 1.1}};
522 wrappingMode = QGeoMapPolygonGeometry::Duplicate;
524 m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode);
526 m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath});
529 m_circle.setShapeTriangulationScale(m_shape, m_geometry.maxCoord());
531 const bool hasBorder = m_circle.m_border.color().alpha() != 0 && m_circle.m_border.width() > 0;
532 const float borderWidth = hasBorder ? m_circle.m_border.width() : 0.0f;
533 m_shapePath->setStrokeColor(hasBorder ? m_circle.m_border.color() : Qt::transparent);
534 m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f);
535 m_shapePath->setFillColor(m_circle.color());
537 const QRectF bb = m_geometry.sourceBoundingBox();
538 QPainterPath path = m_geometry.srcPath();
539 path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth);
541 m_painterPath->setPath(path);
543 m_circle.setSize(bb.size());
544 m_shape->setSize(m_circle.size());
545 m_shape->setOpacity(m_circle.zoomLevelOpacity());
546 m_shape->setVisible(
true);
548 m_circle.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth));