11#include "CylindricalProjection_p.h"
14#include "GeoDataCoordinates.h"
15#include "GeoDataLatLonAltBox.h"
16#include "GeoDataLineString.h"
17#include "GeoDataLinearRing.h"
20#include <QPainterPath>
23static const int maxTessellationNodes = 200;
28CylindricalProjection::CylindricalProjection()
29 : AbstractProjection(new CylindricalProjectionPrivate(this))
33CylindricalProjection::CylindricalProjection(CylindricalProjectionPrivate *dd)
34 : AbstractProjection(dd)
38CylindricalProjection::~CylindricalProjection() =
default;
40CylindricalProjectionPrivate::CylindricalProjectionPrivate(CylindricalProjection *parent)
41 : AbstractProjectionPrivate(parent)
49 int width = viewport->width();
50 int height = viewport->height();
57 screenCoordinates(0.0, maxLat(), viewport, xDummy, yTop);
58 screenCoordinates(0.0, minLat(), viewport, xDummy, yBottom);
67 mapShape.
addRect(0, yTop, width, yBottom - yTop);
83 d->lineStringToPolygon(lineString, viewport, subPolygons);
85 polygons << subPolygons;
88int CylindricalProjectionPrivate::tessellateLineSegment(
const GeoDataCoordinates &aCoords,
91 const GeoDataCoordinates &bCoords,
95 const ViewportParams *viewport,
98 qreal repeatDistance)
const
102 qreal
distance = fabs((bx - ax)) + fabs((by - ay));
109 const qreal safeDistance = -0.5 *
distance;
110 if (!(bx < safeDistance && ax < safeDistance) || !(by < safeDistance && ay < safeDistance)
111 || !(bx + safeDistance > viewport->width() && ax + safeDistance > viewport->width())
112 || !(by + safeDistance > viewport->height() && ay + safeDistance > viewport->height())) {
114 int maxTessellationFactor = viewport->radius() < 20000 ? 10 : 20;
115 int const finalTessellationPrecision = qBound(2, viewport->radius() / 200, maxTessellationFactor) * tessellationPrecision;
120 if (distance > finalTessellationPrecision) {
121 const int tessellatedNodes = qMin<int>(distance / finalTessellationPrecision, maxTessellationNodes);
123 mirrorCount = processTessellation(aCoords, bCoords, tessellatedNodes, polygons, viewport, f, mirrorCount, repeatDistance);
125 mirrorCount = crossDateLine(aCoords, bCoords, bx, by, polygons, mirrorCount, repeatDistance);
133int CylindricalProjectionPrivate::processTessellation(
const GeoDataCoordinates &previousCoords,
134 const GeoDataCoordinates ¤tCoords,
135 int tessellatedNodes,
137 const ViewportParams *viewport,
140 qreal repeatDistance)
const
142 const bool clampToGround = f.testFlag(FollowGround);
143 const bool followLatitudeCircle = f.testFlag(RespectLatitudeCircle) && previousCoords.latitude() == currentCoords.latitude();
147 if (followLatitudeCircle) {
148 const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
149 const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
151 lonDiff = currentCoords.longitude() - previousCoords.longitude();
152 if (previousSign != currentSign && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI) {
153 if (previousSign > currentSign) {
161 if (fabs(lonDiff) == 2 * M_PI) {
167 GeoDataCoordinates previousTessellatedCoords = previousCoords;
168 for (
int i = 1; i <= tessellatedNodes; ++i) {
169 const qreal t = (qreal)(i) / (qreal)(tessellatedNodes + 1);
171 GeoDataCoordinates currentTessellatedCoords;
173 if (followLatitudeCircle) {
177 const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
178 const qreal altitude = altDiff * t + previousCoords.altitude();
179 const qreal lon = lonDiff * t + previousCoords.longitude();
180 const qreal lat = previousTessellatedCoords.latitude();
182 currentTessellatedCoords = GeoDataCoordinates(lon, lat, altitude);
186 currentTessellatedCoords = previousCoords.nlerp(currentCoords, t);
190 currentTessellatedCoords.setAltitude(0);
193 Q_Q(
const CylindricalProjection);
195 q->screenCoordinates(currentTessellatedCoords, viewport, bx, by);
196 mirrorCount = crossDateLine(previousTessellatedCoords, currentTessellatedCoords, bx, by, polygons, mirrorCount, repeatDistance);
197 previousTessellatedCoords = currentTessellatedCoords;
201 GeoDataCoordinates currentModifiedCoords(currentCoords);
203 currentModifiedCoords.setAltitude(0.0);
205 Q_Q(
const CylindricalProjection);
207 q->screenCoordinates(currentModifiedCoords, viewport, bx, by);
208 mirrorCount = crossDateLine(previousTessellatedCoords, currentModifiedCoords, bx, by, polygons, mirrorCount, repeatDistance);
212int CylindricalProjectionPrivate::crossDateLine(
const GeoDataCoordinates &aCoord,
213 const GeoDataCoordinates &bCoord,
218 qreal repeatDistance)
220 qreal aLon = aCoord.longitude();
221 qreal aSign = aLon > 0 ? 1 : -1;
223 qreal bLon = bCoord.longitude();
224 qreal bSign = bLon > 0 ? 1 : -1;
227 if (aSign != bSign && fabs(aLon) + fabs(bLon) > M_PI) {
228 int sign = aSign > bSign ? 1 : -1;
231 delta = repeatDistance * mirrorCount;
237bool CylindricalProjectionPrivate::lineStringToPolygon(
const GeoDataLineString &lineString,
const ViewportParams *viewport,
QList<QPolygonF *> &polygons)
const
239 const TessellationFlags f = lineString.tessellationFlags();
240 bool const tessellate = lineString.tessellate();
241 const bool noFilter = f.testFlag(PreventNodeFiltering);
246 qreal previousX = -1.0;
247 qreal previousY = -1.0;
250 qreal
distance = repeatDistance(viewport);
254 polygon->reserve(lineString.size());
258 GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
259 GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
261 GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
262 GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
264 bool processingLastNode =
false;
270 const bool isLong = lineString.size() > 10;
271 const int maximumDetail = levelForResolution(viewport->angularResolution());
273 const bool hasDetail = itBegin->detail() != 0;
275 bool isStraight = lineString.latLonAltBox().height() == 0 || lineString.latLonAltBox().width() == 0;
277 Q_Q(
const CylindricalProjection);
278 bool const isClosed = lineString.isClosed();
279 while (itCoords != itEnd) {
281 bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
282 : isLong && !processingLastNode && itCoords != itBegin && !viewport->resolves(*itPreviousCoords, *itCoords));
284 if (!skipNode || noFilter) {
285 q->screenCoordinates(*itCoords, viewport, x, y);
288 if (!processingLastNode && itCoords == itBegin) {
289 itPreviousCoords = itCoords;
297 if (tessellate && !isStraight) {
298 mirrorCount = tessellateLineSegment(*itPreviousCoords, previousX, previousY, *itCoords, x, y, polygons, viewport, f, mirrorCount, distance);
305 mirrorCount = crossDateLine(*itPreviousCoords, *itCoords, x, y, polygons, mirrorCount, distance);
308 itPreviousCoords = itCoords;
316 if (processingLastNode) {
321 if (isClosed && itCoords == itEnd) {
323 processingLastNode =
true;
351 repeatPolygons(viewport, polygons);
364 for (; itPolygon != itEnd; ++itPolygon) {
366 *polygon = **itPolygon;
367 polygon->translate(xOffset, 0);
368 translatedPolygons.
append(polygon);
372void CylindricalProjectionPrivate::repeatPolygons(
const ViewportParams *viewport,
QList<QPolygonF *> &polygons)
const
374 Q_Q(
const CylindricalProjection);
381 const qreal centerLatitude = viewport->viewLatLonAltBox().center().latitude();
383 const GeoDataCoordinates westCoords(-M_PI, centerLatitude);
384 const GeoDataCoordinates eastCoords(+M_PI, centerLatitude);
386 q->screenCoordinates(westCoords, viewport, xWest, y);
387 q->screenCoordinates(eastCoords, viewport, xEast, y);
389 if (xWest <= 0 && xEast >= viewport->width() - 1) {
394 const qreal repeatXInterval = xEast - xWest;
396 const int repeatsLeft = (xWest > 0) ? (
int)(xWest / repeatXInterval) + 1 : 0;
397 const int repeatsRight = (xEast < viewport->width()) ? (int)((viewport->width() - xEast) / repeatXInterval) + 1 : 0;
401 for (
int it = repeatsLeft; it > 0; --it) {
402 const qreal xOffset = -it * repeatXInterval;
404 translatePolygons(polygons, translatedPolygons, xOffset);
405 repeatedPolygons << translatedPolygons;
408 repeatedPolygons << polygons;
410 for (
int it = 1; it <= repeatsRight; ++it) {
411 const qreal xOffset = +it * repeatXInterval;
413 translatePolygons(polygons, translatedPolygons, xOffset);
414 repeatedPolygons << translatedPolygons;
417 polygons = repeatedPolygons;
423qreal CylindricalProjectionPrivate::repeatDistance(
const ViewportParams *viewport)
const
426 qreal centerLatitude = viewport->viewLatLonAltBox().center().latitude();
428 GeoDataCoordinates westCoords(-M_PI, centerLatitude);
429 GeoDataCoordinates eastCoords(+M_PI, centerLatitude);
430 qreal xWest, xEast, dummyY;
432 Q_Q(
const AbstractProjection);
434 q->screenCoordinates(westCoords, viewport, xWest, dummyY);
435 q->screenCoordinates(eastCoords, viewport, xEast, dummyY);
437 return xEast - xWest;
This file contains the headers for CylindricalProjection.
This file contains the headers for ViewportParams.
A base class for the Equirectangular and Mercator projections in Marble.
A LineString that allows to store a contiguous set of line segments.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
A public class that controls what is visible in the viewport of a Marble map.
Binds a QML item to a specific geodetic location in screen coordinates.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void append(QList< T > &&value)
const_iterator constBegin() const const
const_iterator constEnd() const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void addRect(const QRectF &rectangle)