11 #include "CylindricalProjection_p.h"
14 #include "GeoDataLinearRing.h"
15 #include "GeoDataLineString.h"
16 #include "GeoDataCoordinates.h"
17 #include "GeoDataLatLonAltBox.h"
20 #include <QPainterPath>
23 static const int maxTessellationNodes = 200;
27 CylindricalProjection::CylindricalProjection()
28 : AbstractProjection( new CylindricalProjectionPrivate( this ) )
32 CylindricalProjection::CylindricalProjection( CylindricalProjectionPrivate* dd )
33 : AbstractProjection( dd )
37 CylindricalProjection::~CylindricalProjection()
41 CylindricalProjectionPrivate::CylindricalProjectionPrivate( CylindricalProjection * parent )
42 : AbstractProjectionPrivate( parent ),
52 int width = viewport->width();
53 int height = viewport->height();
60 screenCoordinates( 0.0, maxLat(), viewport, xDummy, yTop );
61 screenCoordinates( 0.0, minLat(), viewport, xDummy, yBottom );
66 if ( yBottom > height )
79 bool CylindricalProjection::screenCoordinates(
const GeoDataLineString &lineString,
87 if ( !viewport->resolves( lineString.
latLonAltBox() ) ) {
93 d->lineStringToPolygon( lineString, viewport, subPolygons );
95 polygons << subPolygons;
98 int CylindricalProjectionPrivate::tessellateLineSegment(
const GeoDataCoordinates &aCoords,
100 const GeoDataCoordinates &bCoords,
103 const ViewportParams *viewport,
106 qreal repeatDistance)
const
110 qreal
distance = fabs((bx - ax)) + fabs((by - ay));
117 const qreal safeDistance = - 0.5 *
distance;
118 if ( !( bx < safeDistance && ax < safeDistance )
119 || !( by < safeDistance && ay < safeDistance )
120 || !( bx + safeDistance > viewport->width()
121 && ax + safeDistance > viewport->width() )
122 || !( by + safeDistance > viewport->height()
123 && ay + safeDistance > viewport->height() )
127 int maxTessellationFactor = viewport->radius() < 20000 ? 10 : 20;
128 int const finalTessellationPrecision = qBound(2, viewport->radius()/200, maxTessellationFactor) * tessellationPrecision;
133 if ( distance > finalTessellationPrecision ) {
134 const int tessellatedNodes = qMin<int>( distance / finalTessellationPrecision, maxTessellationNodes );
136 mirrorCount = processTessellation( aCoords, bCoords,
145 mirrorCount = crossDateLine( aCoords, bCoords, bx, by, polygons, mirrorCount, repeatDistance );
154 int CylindricalProjectionPrivate::processTessellation(
const GeoDataCoordinates &previousCoords,
155 const GeoDataCoordinates ¤tCoords,
156 int tessellatedNodes,
158 const ViewportParams *viewport,
161 qreal repeatDistance)
const
164 const bool clampToGround = f.testFlag( FollowGround );
165 const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle )
166 && previousCoords.latitude() == currentCoords.latitude();
170 if ( followLatitudeCircle ) {
171 const int previousSign = previousCoords.longitude() > 0 ? 1 : -1;
172 const int currentSign = currentCoords.longitude() > 0 ? 1 : -1;
174 lonDiff = currentCoords.longitude() - previousCoords.longitude();
175 if ( previousSign != currentSign
176 && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI ) {
177 if ( previousSign > currentSign ) {
179 lonDiff += 2 * M_PI ;
185 if ( fabs( lonDiff ) == 2 * M_PI ) {
191 GeoDataCoordinates previousTessellatedCoords = previousCoords;
192 for (
int i = 1; i <= tessellatedNodes; ++i ) {
193 const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 );
195 GeoDataCoordinates currentTessellatedCoords;
197 if ( followLatitudeCircle ) {
201 const qreal altDiff = currentCoords.altitude() - previousCoords.altitude();
202 const qreal altitude = altDiff * t + previousCoords.altitude();
203 const qreal lon = lonDiff * t + previousCoords.longitude();
204 const qreal lat = previousTessellatedCoords.latitude();
206 currentTessellatedCoords = GeoDataCoordinates(lon, lat, altitude);
211 currentTessellatedCoords = previousCoords.nlerp(currentCoords, t);
215 currentTessellatedCoords.setAltitude(0);
218 Q_Q(
const CylindricalProjection);
220 q->screenCoordinates( currentTessellatedCoords, viewport, bx, by );
221 mirrorCount = crossDateLine( previousTessellatedCoords, currentTessellatedCoords, bx, by, polygons,
222 mirrorCount, repeatDistance );
223 previousTessellatedCoords = currentTessellatedCoords;
227 GeoDataCoordinates currentModifiedCoords( currentCoords );
228 if ( clampToGround ) {
229 currentModifiedCoords.setAltitude( 0.0 );
231 Q_Q(
const CylindricalProjection);
233 q->screenCoordinates( currentModifiedCoords, viewport, bx, by );
234 mirrorCount = crossDateLine( previousTessellatedCoords, currentModifiedCoords, bx, by, polygons,
235 mirrorCount, repeatDistance );
239 int CylindricalProjectionPrivate::crossDateLine(
const GeoDataCoordinates & aCoord,
240 const GeoDataCoordinates & bCoord,
245 qreal repeatDistance )
247 qreal aLon = aCoord.longitude();
248 qreal aSign = aLon > 0 ? 1 : -1;
250 qreal bLon = bCoord.longitude();
251 qreal bSign = bLon > 0 ? 1 : -1;
254 if( aSign != bSign && fabs(aLon) + fabs(bLon) > M_PI ) {
255 int sign = aSign > bSign ? 1 : -1;
258 delta = repeatDistance * mirrorCount;
264 bool CylindricalProjectionPrivate::lineStringToPolygon(
const GeoDataLineString &lineString,
265 const ViewportParams *viewport,
268 const TessellationFlags f = lineString.tessellationFlags();
269 bool const tessellate = lineString.tessellate();
270 const bool noFilter = f.testFlag(PreventNodeFiltering);
275 qreal previousX = -1.0;
276 qreal previousY = -1.0;
279 qreal
distance = repeatDistance( viewport );
283 polygon->
reserve(lineString.size());
285 polygons.
append( polygon );
287 GeoDataLineString::ConstIterator itCoords = lineString.constBegin();
288 GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin();
290 GeoDataLineString::ConstIterator itBegin = lineString.constBegin();
291 GeoDataLineString::ConstIterator itEnd = lineString.constEnd();
293 bool processingLastNode =
false;
299 const bool isLong = lineString.size() > 10;
300 const int maximumDetail = levelForResolution(viewport->angularResolution());
302 const bool hasDetail = itBegin->detail() != 0;
304 bool isStraight = lineString.latLonAltBox().height() == 0 || lineString.latLonAltBox().width() == 0;
306 Q_Q(
const CylindricalProjection );
307 bool const isClosed = lineString.isClosed();
308 while ( itCoords != itEnd )
311 bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail
312 : isLong && !processingLastNode && itCoords != itBegin &&
313 !viewport->resolves( *itPreviousCoords, *itCoords ) );
315 if ( !skipNode || noFilter) {
316 q->screenCoordinates( *itCoords, viewport, x, y );
319 if ( !processingLastNode && itCoords == itBegin ) {
320 itPreviousCoords = itCoords;
328 if ( tessellate && !isStraight) {
329 mirrorCount = tessellateLineSegment( *itPreviousCoords, previousX, previousY,
332 f, mirrorCount, distance );
339 mirrorCount = crossDateLine( *itPreviousCoords, *itCoords, x, y, polygons, mirrorCount, distance );
342 itPreviousCoords = itCoords;
350 if ( processingLastNode ) {
355 if (isClosed && itCoords == itEnd) {
357 processingLastNode =
true;
385 repeatPolygons( viewport, polygons );
400 for( ; itPolygon != itEnd; ++itPolygon ) {
402 *polygon = **itPolygon;
404 translatedPolygons.
append( polygon );
408 void CylindricalProjectionPrivate::repeatPolygons(
const ViewportParams *viewport,
411 Q_Q(
const CylindricalProjection );
418 const qreal centerLatitude = viewport->viewLatLonAltBox().center().latitude();
420 const GeoDataCoordinates westCoords(-M_PI, centerLatitude);
421 const GeoDataCoordinates eastCoords(+M_PI, centerLatitude);
423 q->screenCoordinates( westCoords, viewport, xWest, y );
424 q->screenCoordinates( eastCoords, viewport, xEast, y );
426 if ( xWest <= 0 && xEast >= viewport->width() - 1 ) {
431 const qreal repeatXInterval = xEast - xWest;
433 const int repeatsLeft = (xWest > 0 ) ? (
int)(xWest / repeatXInterval) + 1 : 0;
434 const int repeatsRight = (xEast < viewport->width()) ? (
int)((viewport->width() - xEast) / repeatXInterval) + 1 : 0;
438 for (
int it = repeatsLeft; it > 0; --it) {
439 const qreal xOffset = -it * repeatXInterval;
441 translatePolygons( polygons, translatedPolygons, xOffset );
442 repeatedPolygons << translatedPolygons;
445 repeatedPolygons << polygons;
447 for (
int it = 1; it <= repeatsRight; ++it) {
448 const qreal xOffset = +it * repeatXInterval;
450 translatePolygons( polygons, translatedPolygons, xOffset );
451 repeatedPolygons << translatedPolygons;
454 polygons = repeatedPolygons;
460 qreal CylindricalProjectionPrivate::repeatDistance(
const ViewportParams *viewport )
const
463 qreal centerLatitude = viewport->viewLatLonAltBox().center().latitude();
465 GeoDataCoordinates westCoords( -M_PI, centerLatitude );
466 GeoDataCoordinates eastCoords( +M_PI, centerLatitude );
467 qreal xWest, xEast, dummyY;
469 Q_Q(
const AbstractProjection );
471 q->screenCoordinates( westCoords, viewport, xWest, dummyY );
472 q->screenCoordinates( eastCoords, viewport, xEast, dummyY );
474 return xEast - xWest;