8#include "AbstractProjection_p.h"
9#include "AzimuthalProjection_p.h"
12#include "GeoDataCoordinates.h"
13#include "GeoDataLatLonAltBox.h"
14#include "GeoDataLineString.h"
15#include "GeoDataLinearRing.h"
18#include <QPainterPath>
28qreal AzimuthalProjection::clippingRadius()
const
33bool AzimuthalProjection::screenCoordinates(
const GeoDataLineString &lineString,
const ViewportParams *viewport,
QList<QPolygonF *> &polygons)
const
35 Q_D(
const AzimuthalProjection);
38 if (!viewport->resolves(lineString.latLonAltBox())) {
43 d->lineStringToPolygon(lineString, viewport, polygons);
49 qint64 radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
50 qint64 width = viewport->width();
51 qint64 height = viewport->height();
55 if (radius > width + height)
60 if (4 * radius * radius >= width * width + height * height)
76 if (2.0 * viewport->radius() <= viewport->height() && 2.0 * viewport->radius() <= viewport->width()) {
79 if (pitch > 0.0 && pitch < +M_PI) {
85 if (pitch < 0.0 && pitch > -M_PI) {
95 if (pitch == 0.0 || pitch == -M_PI || pitch == +M_PI) {
96 qreal yaw = viewport->planetAxis().yaw();
115 qreal dummyX, dummyY;
118 if (screenCoordinates(maxLatPoint, viewport, dummyX, dummyY, dummyVal) || screenCoordinates(minLatPoint, viewport, dummyX, dummyY, dummyVal)) {
128 int radius = viewport->radius() * viewport->currentProjection()->clippingRadius();
129 int imgWidth = viewport->width();
130 int imgHeight = viewport->height();
133 fullRect.
addRect(0, 0, imgWidth, imgHeight);
139 if (!viewport->mapCoversViewport()) {
141 mapShape.addEllipse(imgWidth / 2 - radius, imgHeight / 2 - radius, 2 * radius, 2 * radius);
142 return mapShape.intersected(fullRect);
148AzimuthalProjection::AzimuthalProjection(AzimuthalProjectionPrivate *dd)
153AzimuthalProjection::~AzimuthalProjection() =
default;
155void AzimuthalProjectionPrivate::tessellateLineSegment(
const GeoDataCoordinates &aCoords,
162 const ViewportParams *viewport,
164 bool allowLatePolygonCut)
const
168 qreal distance = fabs((bx - ax)) + fabs((by - ay));
175 const qreal safeDistance = -0.5 * distance;
176 if (!(bx < safeDistance && ax < safeDistance) || !(by < safeDistance && ay < safeDistance)
177 || !(bx + safeDistance > viewport->width() && ax + safeDistance > viewport->width())
178 || !(by + safeDistance > viewport->height() && ay + safeDistance > viewport->height())) {
180 int maxTessellationFactor = viewport->radius() < 20000 ? 10 : 20;
181 int const finalTessellationPrecision = qBound(2, viewport->radius() / 200, maxTessellationFactor) * tessellationPrecision;
187 if (distance > finalTessellationPrecision) {
188 const int tessellatedNodes = qMin<int>(distance / finalTessellationPrecision, maxTessellationNodes);
190 processTessellation(aCoords, bCoords, tessellatedNodes, polygons, viewport, f, allowLatePolygonCut);
192 crossHorizon(bCoords, polygons, viewport, allowLatePolygonCut);
199void AzimuthalProjectionPrivate::processTessellation(
const GeoDataCoordinates &previousCoords,
201 int tessellatedNodes,
202 QList<QPolygonF *> &polygons,
205 bool allowLatePolygonCut)
const
207 const bool clampToGround = f.testFlag(FollowGround);
208 const bool followLatitudeCircle = f.testFlag(RespectLatitudeCircle) && previousCoords.latitude() == currentCoords.latitude();
212 if (followLatitudeCircle) {
213 const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
214 const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
216 lonDiff = currentCoords.longitude() - previousCoords.longitude();
217 if (previousSign != currentSign && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI) {
218 if (previousSign > currentSign) {
230 for (
int i = 1; i <= tessellatedNodes; ++i) {
231 const qreal t = (qreal)(i) / (qreal)(tessellatedNodes + 1);
235 if (followLatitudeCircle) {
239 const qreal altitude = altDiff * t + previousCoords.
altitude();
240 const qreal lon = lonDiff * t + previousCoords.longitude();
241 const qreal lat = previousTessellatedCoords.latitude();
247 currentTessellatedCoords = previousCoords.
nlerp(currentCoords, t);
254 crossHorizon(currentTessellatedCoords, polygons, viewport, allowLatePolygonCut);
255 previousTessellatedCoords = currentTessellatedCoords;
261 currentModifiedCoords.setAltitude(0.0);
263 crossHorizon(currentModifiedCoords, polygons, viewport, allowLatePolygonCut);
267 QList<QPolygonF *> &polygons,
269 bool allowLatePolygonCut)
const
272 bool globeHidesPoint;
276 q->screenCoordinates(bCoord, viewport, x, y, globeHidesPoint);
278 if (!globeHidesPoint) {
279 *polygons.
last() << QPointF(x, y);
281 if (allowLatePolygonCut && !polygons.
last()->isEmpty()) {
282 auto path =
new QPolygonF;
288bool AzimuthalProjectionPrivate::lineStringToPolygon(
const GeoDataLineString &lineString,
const ViewportParams *viewport, QList<QPolygonF *> &polygons)
const
292 const TessellationFlags f = lineString.tessellationFlags();
293 bool const tessellate = lineString.tessellate();
294 const bool noFilter = f.testFlag(PreventNodeFiltering);
298 bool globeHidesPoint =
false;
300 qreal previousX = -1.0;
301 qreal previousY = -1.0;
302 bool previousGlobeHidesPoint =
false;
304 qreal horizonX = -1.0;
305 qreal horizonY = -1.0;
307 auto polygon =
new QPolygonF;
309 polygon->reserve(lineString.size());
313 GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
314 GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
327 bool horizonPair =
false;
334 bool horizonOrphan =
false;
337 GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
338 GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
340 bool processingLastNode =
false;
346 const bool isLong = lineString.size() > 10;
347 const int maximumDetail = levelForResolution(viewport->angularResolution());
349 const bool hasDetail = itBegin->detail() != 0;
351 while (itCoords != itEnd) {
353 bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
354 : itCoords != itBegin && isLong && !processingLastNode && !viewport->resolves(*itPreviousCoords, *itCoords));
356 if (!skipNode || noFilter) {
357 q->screenCoordinates(*itCoords, viewport, x, y, globeHidesPoint);
360 if (!processingLastNode && itCoords == itBegin) {
361 previousGlobeHidesPoint = globeHidesPoint;
362 itPreviousCoords = itCoords;
368 const bool isAtHorizon = (globeHidesPoint || previousGlobeHidesPoint) && (globeHidesPoint != previousGlobeHidesPoint);
372 horizonCoords = findHorizon(*itPreviousCoords, *itCoords, viewport, f);
374 if (lineString.isClosed()) {
376 horizonToPolygon(viewport, horizonDisappearCoords, horizonCoords, polygons.
last());
379 if (globeHidesPoint) {
380 horizonDisappearCoords = horizonCoords;
383 horizonOrphanCoords = horizonCoords;
384 horizonOrphan =
true;
389 q->screenCoordinates(horizonCoords, viewport, horizonX, horizonY);
393 if (previousGlobeHidesPoint) {
394 *polygons.
last() << QPointF(horizonX, horizonY);
402 if (lineString.tessellate() ) {
404 tessellateLineSegment(*itPreviousCoords, previousX, previousY, *itCoords, x, y, polygons, viewport, f, !lineString.isClosed());
409 if (previousGlobeHidesPoint) {
410 tessellateLineSegment(horizonCoords, horizonX, horizonY, *itCoords, x, y, polygons, viewport, f, !lineString.isClosed());
412 tessellateLineSegment(*itPreviousCoords,
421 !lineString.isClosed());
425 if (!globeHidesPoint) {
426 *polygons.
last() << QPointF(x, y);
428 if (!previousGlobeHidesPoint && isAtHorizon) {
429 *polygons.
last() << QPointF(horizonX, horizonY);
434 if (globeHidesPoint) {
435 if (!previousGlobeHidesPoint && !lineString.isClosed()) {
436 polygons.
append(
new QPolygonF);
440 previousGlobeHidesPoint = globeHidesPoint;
441 itPreviousCoords = itCoords;
449 if (processingLastNode) {
454 if (itCoords == itEnd && lineString.isClosed()) {
456 processingLastNode =
true;
462 if (horizonOrphan && lineString.isClosed()) {
463 horizonToPolygon(viewport, horizonCoords, horizonOrphanCoords, polygons.
last());
466 if (polygons.
last()->size() <= 1) {
467 delete polygons.
last();
474void AzimuthalProjectionPrivate::horizonToPolygon(
const ViewportParams *viewport,
477 QPolygonF *polygon)
const
481 const qreal imageHalfWidth = viewport->width() / 2;
482 const qreal imageHalfHeight = viewport->height() / 2;
484 bool dummyGlobeHidesPoint =
false;
488 q->screenCoordinates(disappearCoords, viewport, x, y, dummyGlobeHidesPoint);
489 qreal alpha = atan2(y - imageHalfHeight, x - imageHalfWidth);
491 q->screenCoordinates(reappearCoords, viewport, x, y, dummyGlobeHidesPoint);
492 qreal beta = atan2(y - imageHalfHeight, x - imageHalfWidth);
497 qreal sgndiff = diff < 0 ? -1 : 1;
499 const qreal arcradius = q->clippingRadius() * viewport->radius();
500 const int itEnd = fabs(diff * RAD2DEG);
504 for (
int it = 1; it <= itEnd; ++it) {
505 const qreal angle = alpha + DEG2RAD * sgndiff * it;
506 const qreal itx = imageHalfWidth + arcradius * cos(angle);
507 const qreal ity = imageHalfHeight + arcradius * sin(angle);
508 *polygon << QPointF(itx, ity);
515 TessellationFlags f)
const
517 bool currentHide = globeHidesPoint(currentCoords, viewport);
519 return doFindHorizon(previousCoords, currentCoords, viewport, f, currentHide, 0);
527 int recursionCounter)
const
529 if (recursionCounter > 20) {
530 return currentHide ? previousCoords : currentCoords;
534 bool followLatitudeCircle =
false;
538 qreal previousLongitude = 0.0;
539 qreal previousLatitude = 0.0;
541 if (f.testFlag(RespectLatitudeCircle)) {
542 previousCoords.
geoCoordinates(previousLongitude, previousLatitude);
543 qreal previousSign = previousLongitude > 0 ? 1 : -1;
545 qreal currentLongitude = 0.0;
546 qreal currentLatitude = 0.0;
548 qreal currentSign = currentLongitude > 0 ? 1 : -1;
550 if (previousLatitude == currentLatitude) {
551 followLatitudeCircle =
true;
553 lonDiff = currentLongitude - previousLongitude;
554 if (previousSign != currentSign && fabs(previousLongitude) + fabs(currentLongitude) > M_PI) {
555 if (previousSign > currentSign) {
571 if (followLatitudeCircle) {
575 const qreal altitude = previousCoords.
altitude() + 0.5 * altDiff;
576 const qreal lon = lonDiff * 0.5 + previousLongitude;
577 const qreal lat = previousLatitude;
583 horizonCoords = previousCoords.
nlerp(currentCoords, 0.5);
586 bool horizonHide = globeHidesPoint(horizonCoords, viewport);
588 if (horizonHide != currentHide) {
589 return doFindHorizon(horizonCoords, currentCoords, viewport, f, currentHide, recursionCounter);
592 return doFindHorizon(previousCoords, horizonCoords, viewport, f, horizonHide, recursionCounter);
597 bool globeHidesPoint;
598 qreal dummyX, dummyY;
601 q->screenCoordinates(coordinates, viewport, dummyX, dummyY, globeHidesPoint);
602 return globeHidesPoint;
This file contains the headers for AzimuthalProjection.
This file contains the headers for ViewportParams.
A 3d point representation.
GeoDataCoordinates nlerp(const GeoDataCoordinates &target, double t) const
nlerp (normalized linear interpolation) between this coordinates and the given target coordinates
void setAltitude(const qreal altitude)
set the altitude of the Point in meters
qreal altitude() const
return the altitude of the Point in meters
void geoCoordinates(qreal &lon, qreal &lat, GeoDataCoordinates::Unit unit) const
use this function to get the longitude and latitude with one call - use the unit parameter to switch ...
A base class for all projections in Marble.
qreal minLat() const
Returns the arbitrarily chosen minimum (southern) latitude.
qreal maxLat() const
Returns the arbitrarily chosen maximum (northern) latitude.
virtual GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
A base class for the Gnomonic and Orthographic (Globe) projections in Marble.
bool isClippedToSphere() const override
Defines whether a projection is supposed to be clipped to a certain radius.
GeoDataLatLonAltBox latLonAltBox(const QRect &screenRect, const ViewportParams *viewport) const override
Returns a GeoDataLatLonAltBox bounding box of the given screenrect inside the given viewport.
bool mapCoversViewport(const ViewportParams *viewport) const override
Returns whether the projected data fully obstructs the current viewport.
QPainterPath mapShape(const ViewportParams *viewport) const override
Returns the shape/outline of a map projection.
A 3d point representation.
static qreal normalizeLon(qreal lon, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize the longitude to always be -M_PI <= lon <= +M_PI (Radian).
static qreal normalizeLat(qreal lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize latitude to always be in -M_PI / 2.
A class that defines a 3D bounding box for geographic data.
A LineString that allows to store a contiguous set of line segments.
A public class that controls what is visible in the viewport of a Marble map.
QString path(const QString &relativePath)
Binds a QML item to a specific geodetic location in screen coordinates.
void append(QList< T > &&value)
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void addRect(const QRectF &rectangle)