From a01bddca77a22222530f82b26932ffd3326a81b8 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Thu, 17 Aug 2017 19:15:43 -0400 Subject: [PATCH] [ios] Fix visible coordinates when boundaries cross international dateline. --- platform/darwin/src/MGLGeometry_Private.h | 45 +++++++++++++++++++++++ platform/ios/src/MGLMapView.mm | 25 +++++++++++++ 2 files changed, 70 insertions(+) diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 7ad8314a792..6656b2ebbce 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -123,3 +123,48 @@ NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGL cos(distance) - sin(coordinate.latitude) * sin(otherLatitude)); return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude); } + +/** Returns the coordinate at a given fraction between two coordinates. + + @param origin Origin `CLLocationCoordinate2D`. + @param destination Destination `CLLocationCoordinate2D`. + @param fraction Fraction between coordinates (0 = origin, 0.5 = middle, 1 = destination). + + @return A coordinate at given fraction. */ +// Ported from http://www.movable-type.co.uk/scripts/latlong.html +NS_INLINE CLLocationCoordinate2D MGLCLCoordinateAtFraction(CLLocationCoordinate2D origin, CLLocationCoordinate2D destination, double fraction) { + double φ1 = MGLRadiansFromDegrees(origin.latitude); + double λ1 = MGLRadiansFromDegrees(origin.longitude); + + double φ2 = MGLRadiansFromDegrees(destination.latitude); + double λ2 = MGLRadiansFromDegrees(destination.longitude); + double sinφ1 = sin(φ1); + double cosφ1 = cos(φ1); + double sinλ1 = sin(λ1); + double cosλ1 = cos(λ1); + double sinφ2 = sin(φ2); + double cosφ2 = cos(φ2); + double sinλ2 = sin(λ2); + double cosλ2 = cos(λ2); + + // distance between points + double Δφ = φ2 - φ1; + double Δλ = λ2 - λ1; + double a = sin(Δφ/2) * sin(Δφ/2) + cos(φ1) * cos(φ2) * sin(Δλ/2) * sin(Δλ/2); + double δ = 2 * atan2(sqrt(a), sqrt(1-a)); + + double A = sin((1-fraction)*δ) / sin(δ); + double B = sin(fraction*δ) / sin(δ); + + double x = A * cosφ1 * cosλ1 + B * cosφ2 * cosλ2; + double y = A * cosφ1 * sinλ1 + B * cosφ2 * sinλ2; + double z = A * sinφ1 + B * sinφ2; + + double φ3 = atan2(z, sqrt(x*x + y*y)); + double λ3 = atan2(y, x); + + CLLocationDegrees latitude = MGLDegreesFromRadians(φ3); + CLLocationDegrees longitude = fmod(MGLDegreesFromRadians(λ3), 360 - 180); + + return CLLocationCoordinate2DMake(latitude, longitude); +} diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 715c32186d9..17d655f9507 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2667,8 +2667,21 @@ - (void)_setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count { latLngs.push_back({coordinates[i].latitude, coordinates[i].longitude}); } + + BOOL boundariesCrossDateTime = NO; + if (count == 4 && coordinates[1].longitude > 0 && coordinates[3].longitude < 0) { + boundariesCrossDateTime = YES; + latLngs = [self normalizeCoordinatesForDateTimeChangeSW:coordinates[1] ne:coordinates[3]]; + + } mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngs(latLngs, padding); + + if (boundariesCrossDateTime) { + CLLocationCoordinate2D centerCoordinate = MGLCLCoordinateAtFraction(coordinates[1], coordinates[3], 0.5); + cameraOptions.center = MGLLatLngFromLocationCoordinate2D(centerCoordinate); + } + if (direction >= 0) { cameraOptions.angle = MGLRadiansFromDegrees(-direction); @@ -2707,6 +2720,18 @@ - (void)_setVisibleCoordinates:(const CLLocationCoordinate2D *)coordinates count [self didChangeValueForKey:@"visibleCoordinateBounds"]; } +- (std::vector)normalizeCoordinatesForDateTimeChangeSW:(CLLocationCoordinate2D)sw ne:(CLLocationCoordinate2D)ne +{ + CLLocationDegrees delta = (180 - fabs(sw.longitude)) + (180 - fabs(ne.longitude)); + CLLocationCoordinate2D swap = sw; + sw = CLLocationCoordinate2DMake(ne.latitude, sw.longitude - delta); + ne = swap; + + std::vector latLngs = { {ne.latitude, sw.longitude}, {sw.latitude, sw.longitude}, {sw.latitude, ne.longitude}, {ne.latitude, ne.longitude} }; + + return latLngs; +} + + (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection { return [NSSet setWithObject:@"camera"];