summaryrefslogtreecommitdiff
path: root/platform/ios/test/MGLMapViewDirectionTests.mm
blob: 8a724a06bcfc66f175c26ff7f2c1677cebc0dc21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>

#import <mbgl/math/wrap.hpp>

@interface MGLMapView (MGLMapViewDirectionTests)
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate;
- (void)resetNorthAnimated:(BOOL)animated;
@end

@interface UIRotationGestureRecognizerMock : UIRotationGestureRecognizer
@end

@implementation UIRotationGestureRecognizerMock
- (CGPoint)locationInView:(nullable UIView*)view { return view.center; }
@end

@interface MGLMapViewDirectionTests : XCTestCase
@property (nonatomic) MGLMapView *mapView;
@end

@implementation MGLMapViewDirectionTests

- (void)setUp {
    [super setUp];

    [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
    NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
    self.mapView = [[MGLMapView alloc] initWithFrame:CGRectMake(0, 0, 64, 64) styleURL:styleURL];
}

- (void)tearDown {
    self.mapView = nil;
    [MGLAccountManager setAccessToken:nil];
    [super tearDown];
}

- (void)testDirection {
    self.mapView.zoomLevel = 0;
    self.mapView.direction = 30;
    XCTAssertEqual(self.mapView.direction, 0, @"Rotation is not allowed at world-scale zoom levels.");

    self.mapView.zoomLevel = 15;
    CLLocationCoordinate2D originalCenterCoordinate = self.mapView.centerCoordinate;

    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.);
        self.mapView.direction = inputDegrees;
        XCTAssertEqualWithAccuracy(self.mapView.direction, wrappedDegrees, 0.001);

        XCTAssertEqual(originalCenterCoordinate.latitude, self.mapView.centerCoordinate.latitude, "@Map center coordinate latitude should remain constant when direction is set to %@°.", degrees);
        XCTAssertEqual(originalCenterCoordinate.longitude, self.mapView.centerCoordinate.longitude, @"Map center coordinate longitude should remain constant when direction is set to %@°.", degrees);
    }

    [self.mapView resetNorthAnimated:NO];
    XCTAssertEqual(self.mapView.direction, 0, @"Reset-to-north should set direction to 0°.");
    XCTAssertEqual(originalCenterCoordinate.latitude, self.mapView.centerCoordinate.latitude, "@Map center coordinate latitude should remain constant when direction is reset.");
    XCTAssertEqual(originalCenterCoordinate.longitude, self.mapView.centerCoordinate.longitude, @"Map center coordinate latitude should remain constant when direction is reset.");
}

- (void)testRotateEnabled {
    self.mapView.zoomLevel = 10;

    UIRotationGestureRecognizerMock *gesture = [[UIRotationGestureRecognizerMock alloc] initWithTarget:nil action:nil];
    gesture.state = UIGestureRecognizerStateBegan;
    gesture.rotation = MGLRadiansFromDegrees(30);
    CGFloat wrappedRotation = mbgl::util::wrap(-MGLDegreesFromRadians(gesture.rotation), 0., 360.);

    // Disabled
    {
        self.mapView.rotateEnabled = NO;
        XCTAssertEqual(self.mapView.allowsRotating, NO);

        [self.mapView handleRotateGesture:gesture];
        XCTAssertNotEqual(self.mapView.direction, wrappedRotation, @"Gestural rotation should not work when rotation is disabled.");

        self.mapView.direction = 45.f;
        XCTAssertEqualWithAccuracy(self.mapView.direction, 45, 0.001, @"Programmatic rotation is allowed when rotateEnabled = NO.");
    }

    // Enabled
    {
        [self.mapView resetNorthAnimated:NO];
        self.mapView.rotateEnabled = YES;
        XCTAssertEqual(self.mapView.allowsRotating, YES);

        gesture.state = UIGestureRecognizerStateChanged;
        gesture.rotation = MGLRadiansFromDegrees(30);
        [self.mapView handleRotateGesture:gesture];
        XCTAssertEqualWithAccuracy(self.mapView.direction, wrappedRotation, 0.001, @"Gestural rotation should work when rotation is enabled.");
    }
}

- (void)testRotationGesture {
    self.mapView.zoomLevel = 15;
    CLLocationCoordinate2D originalCenterCoordinate = self.mapView.centerCoordinate;

    UIRotationGestureRecognizerMock *gesture = [[UIRotationGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
    gesture.state = UIGestureRecognizerStateBegan;
    gesture.rotation = 0;
    [self.mapView addGestureRecognizer:gesture];
    [self.mapView handleRotateGesture:gesture];
    XCTAssertEqual(self.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]);
        [self.mapView handleRotateGesture:gesture];
        CGFloat wrappedRotation = mbgl::util::wrap(-MGLDegreesFromRadians(gesture.rotation), 0., 360.);
        XCTAssertEqualWithAccuracy(self.mapView.direction, wrappedRotation, 0.001, @"Map direction should match gesture rotation for input of %@°.", degrees);

        // Given a hypothetical rotation around the exact center of the map, the center coordinate should remain the same.
        // See above where we override -[UIRotationGestureRecognizer locationInView:] to always return the center of the target view.
        XCTAssertEqualWithAccuracy(originalCenterCoordinate.latitude, self.mapView.centerCoordinate.latitude, 0.0000001, "@Map center coordinate latitude should remain constant during rotation of %@°.", degrees);
        XCTAssertEqualWithAccuracy(originalCenterCoordinate.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant during rotation of %@°.", degrees);
    }
}

- (void)testResetPosition {
    [self.mapView resetPosition];
    MGLMapCamera *defaultCamera = [MGLMapCamera cameraLookingAtCenterCoordinate:CLLocationCoordinate2DMake(0, 0) altitude:self.mapView.camera.altitude pitch:0 heading:0];
    XCTAssertTrue([self.mapView.camera isEqualToMapCamera:defaultCamera], @"Map camera %@ should be equal to default camera %@.", self.mapView.camera, defaultCamera);
}

- (CGFloat)degreesFromAffineTransform:(CGAffineTransform)transform {
    CGFloat angle = atan2f(transform.b, transform.a);
    return MGLDegreesFromRadians(angle);
}

@end