[darwin] Fix MGLMapView.direction wrapping
Re-adds the 0-to-360° wrapping for MGLMapView.direction. Without this wrapping, we’d get ±180° from core, which results in invalid CLLocationDirection for the negative half of the rotational spectrum.
This commit is contained in:
@@ -4,20 +4,27 @@
|
||||
static MGLMapView *mapView;
|
||||
|
||||
@interface MGLMapViewTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation MGLMapViewTests
|
||||
|
||||
+ (void)setUp {
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
[MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
|
||||
mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64) styleURL:MGLStyle.streetsStyleURL];
|
||||
[mapView setCenterCoordinate:CLLocationCoordinate2DMake(33, 179)];
|
||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
||||
mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64) styleURL:styleURL];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
mapView = nil;
|
||||
[MGLAccountManager setAccessToken:nil];
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testCoordinateBoundsConversion {
|
||||
[mapView setCenterCoordinate:CLLocationCoordinate2DMake(33, 179)];
|
||||
|
||||
MGLCoordinateBounds leftAntimeridianBounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(-75, 175), CLLocationCoordinate2DMake(75, 180));
|
||||
CGRect leftAntimeridianBoundsRect = [mapView convertCoordinateBounds:leftAntimeridianBounds toRectToView:mapView];
|
||||
|
||||
@@ -30,14 +37,8 @@ static MGLMapView *mapView;
|
||||
// If the resulting CGRect from -convertCoordinateBounds:toRectToView:
|
||||
// intersects the set of bounds to the left and right of the
|
||||
// antimeridian, then we know that the CGRect spans across the antimeridian
|
||||
XCTAssertTrue(CGRectIntersectsRect(spanningBoundsRect, leftAntimeridianBoundsRect), @"Resulting ");
|
||||
XCTAssertTrue(CGRectIntersectsRect(spanningBoundsRect, leftAntimeridianBoundsRect), @"Resulting");
|
||||
XCTAssertTrue(CGRectIntersectsRect(spanningBoundsRect, rightAntimeridianBoundsRect), @"Something");
|
||||
}
|
||||
|
||||
+ (void)tearDown {
|
||||
mapView = nil;
|
||||
[MGLAccountManager setAccessToken:nil];
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -458,6 +458,7 @@
|
||||
96E516FC20005A4400A02306 /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
|
||||
96E516FF20005A4F00A02306 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; };
|
||||
96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848891CBB037E00AB86E3 /* SMCalloutView.h */; };
|
||||
96ED34DE22374C0900E9FCA9 /* MGLMapViewDirectionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96ED34DD22374C0900E9FCA9 /* MGLMapViewDirectionTests.mm */; };
|
||||
96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; };
|
||||
AC1B0916221CA14D00DB56C8 /* CLLocationManager+MMEMobileEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = AC1B0914221CA14500DB56C8 /* CLLocationManager+MMEMobileEvents.h */; };
|
||||
AC1B0917221CA14D00DB56C8 /* CLLocationManager+MMEMobileEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = AC1B0914221CA14500DB56C8 /* CLLocationManager+MMEMobileEvents.h */; };
|
||||
@@ -1141,6 +1142,7 @@
|
||||
96E0272C1E57C7E5004B8E66 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
96ED34DD22374C0900E9FCA9 /* MGLMapViewDirectionTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapViewDirectionTests.mm; sourceTree = "<group>"; };
|
||||
96F017292118FBAE00892778 /* MGLMapView_Experimental.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapView_Experimental.h; sourceTree = "<group>"; };
|
||||
96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; };
|
||||
AC1B0914221CA14500DB56C8 /* CLLocationManager+MMEMobileEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CLLocationManager+MMEMobileEvents.h"; path = "../vendor/mapbox-events-ios/MapboxMobileEvents/CLLocationManager+MMEMobileEvents.h"; sourceTree = "<group>"; };
|
||||
@@ -2025,6 +2027,7 @@
|
||||
DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */,
|
||||
DA695425215B1E75002041A4 /* MGLMapCameraTests.m */,
|
||||
076171C22139C70900668A35 /* MGLMapViewTests.m */,
|
||||
96ED34DD22374C0900E9FCA9 /* MGLMapViewDirectionTests.mm */,
|
||||
16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */,
|
||||
9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */,
|
||||
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
|
||||
@@ -3152,6 +3155,7 @@
|
||||
07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */,
|
||||
920A3E5D1E6F995200C16EFC /* MGLSourceQueryTests.m in Sources */,
|
||||
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */,
|
||||
96ED34DE22374C0900E9FCA9 /* MGLMapViewDirectionTests.mm in Sources */,
|
||||
FAE1CDCB1E9D79CB00C40B5B /* MGLFillExtrusionStyleLayerTests.mm in Sources */,
|
||||
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
|
||||
357579831D502AE6000B822E /* MGLRasterStyleLayerTests.mm in Sources */,
|
||||
|
||||
@@ -3431,7 +3431,7 @@ public:
|
||||
|
||||
- (CLLocationDirection)direction
|
||||
{
|
||||
return *self.mbglMap.getCameraOptions().bearing;
|
||||
return mbgl::util::wrap(*self.mbglMap.getCameraOptions().bearing, 0., 360.);
|
||||
}
|
||||
|
||||
- (void)setDirection:(CLLocationDirection)direction animated:(BOOL)animated
|
||||
|
||||
95
platform/ios/test/MGLMapViewDirectionTests.mm
Normal file
95
platform/ios/test/MGLMapViewDirectionTests.mm
Normal file
@@ -0,0 +1,95 @@
|
||||
#import <Mapbox/Mapbox.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import <mbgl/math/wrap.hpp>
|
||||
|
||||
static MGLMapView *mapView;
|
||||
|
||||
@interface MGLMapView (MGLMapViewDirectionTests)
|
||||
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate;
|
||||
- (void)resetNorthAnimated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
@interface UIRotationGestureRecognizer (MGLMapViewDirectionTests)
|
||||
@property (nonatomic, readwrite) UIGestureRecognizerState state;
|
||||
@end
|
||||
|
||||
@interface MGLMapViewDirectionTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation MGLMapViewDirectionTests
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
[MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
|
||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
||||
mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64) styleURL:styleURL];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
mapView = nil;
|
||||
[MGLAccountManager setAccessToken:nil];
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testDirection {
|
||||
mapView.zoomLevel = 0;
|
||||
mapView.direction = 30;
|
||||
XCTAssertEqual(mapView.direction, 0, @"Rotation is not allowed at world-scale zoom levels.");
|
||||
|
||||
mapView.zoomLevel = 15;
|
||||
|
||||
for (NSNumber *degrees in @[@-999, @-359, @-240, @-180, @-90, @-45, @0, @45, @90, @180, @240, @360, @999]) {
|
||||
double inputDegrees = [degrees doubleValue];
|
||||
double wrappedDegrees = mbgl::util::wrap(inputDegrees, 0., 360.);
|
||||
mapView.direction = inputDegrees;
|
||||
XCTAssertEqualWithAccuracy(mapView.direction, wrappedDegrees, 0.001);
|
||||
}
|
||||
|
||||
[mapView resetNorthAnimated:NO];
|
||||
XCTAssertEqual(mapView.direction, 0, @"Reset-to-north should set direction to 0°.");
|
||||
}
|
||||
|
||||
- (void)testRotateGesture {
|
||||
mapView.zoomLevel = 15;
|
||||
|
||||
UIRotationGestureRecognizer *gesture = [[UIRotationGestureRecognizer alloc] initWithTarget:nil action:nil];
|
||||
gesture.state = UIGestureRecognizerStateBegan;
|
||||
gesture.rotation = 0;
|
||||
[mapView handleRotateGesture:gesture];
|
||||
XCTAssertEqual(mapView.direction, gesture.rotation);
|
||||
|
||||
for (NSNumber *degrees in @[@-999, @-360, @-240, @-180, @-90, @-45, @0, @45, @90, @180, @240, @359, @999]) {
|
||||
gesture.state = UIGestureRecognizerStateChanged;
|
||||
gesture.rotation = MGLRadiansFromDegrees([degrees doubleValue]);
|
||||
[mapView handleRotateGesture:gesture];
|
||||
CGFloat wrappedRotation = mbgl::util::wrap(-MGLDegreesFromRadians(gesture.rotation), 0., 360.);
|
||||
XCTAssertEqualWithAccuracy(mapView.direction, wrappedRotation, 0.001, @"Map direction should match gesture rotation for input of %@°.", degrees);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testCompassRotation {
|
||||
mapView.zoomLevel = 15;
|
||||
|
||||
for (NSNumber *degrees in @[@-999, @-359, @-240, @-180, @-90, @-45, @0, @45, @90, @180, @240, @360, @999]) {
|
||||
mapView.direction = [degrees doubleValue];
|
||||
CGFloat wrappedDirection = mbgl::util::wrap(-mapView.direction, 0., 360.);
|
||||
CGAffineTransform rotation = CGAffineTransformMakeRotation(MGLRadiansFromDegrees(wrappedDirection));
|
||||
XCTAssertTrue(CGAffineTransformEqualToTransform(mapView.compassView.transform, rotation),
|
||||
@"Compass transform direction %f° should equal wrapped transform direction %f° (~%.f°).", [self degreesFromAffineTransform:mapView.compassView.transform], [self degreesFromAffineTransform:rotation], wrappedDirection);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testResetPosition {
|
||||
[mapView resetPosition];
|
||||
MGLMapCamera *defaultCamera = [MGLMapCamera cameraLookingAtCenterCoordinate:CLLocationCoordinate2DMake(0, 0) altitude:mapView.camera.altitude pitch:0 heading:0];
|
||||
XCTAssertTrue([mapView.camera isEqualToMapCamera:defaultCamera], @"Map camera %@ should be equal to default camera %@.", mapView.camera, defaultCamera);
|
||||
}
|
||||
|
||||
- (CGFloat)degreesFromAffineTransform:(CGAffineTransform)transform {
|
||||
CGFloat angle = atan2f(transform.b, transform.a);
|
||||
return MGLDegreesFromRadians(angle);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1132,7 +1132,7 @@ public:
|
||||
}
|
||||
|
||||
- (CLLocationDirection)direction {
|
||||
return *_mbglMap->getCameraOptions().bearing;
|
||||
return mbgl::util::wrap(*_mbglMap->getCameraOptions().bearing, 0., 360.);
|
||||
}
|
||||
|
||||
- (void)setDirection:(CLLocationDirection)direction {
|
||||
|
||||
Reference in New Issue
Block a user