summaryrefslogtreecommitdiff
path: root/platform/ios/test/MGLMapViewZoomTests.m
blob: bd617857fd8c135d93ea7c047f82a7786093f753 (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
132
133
134
135
136
137
138
139
140
#import <Mapbox/Mapbox.h>
#import <XCTest/XCTest.h>

@interface MGLMapView (MGLMapViewZoomTests)
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch;
@end

@interface UIPinchGestureRecognizerMock : UIPinchGestureRecognizer
@property (nonatomic) CGPoint locationInViewOverride;
@end

@implementation UIPinchGestureRecognizerMock
- (CGPoint)locationInView:(nullable UIView *)view { return self.locationInViewOverride; }
@end

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

@implementation MGLMapViewZoomTests

- (void)setUp {
    [super setUp];

    [MGLAccountManager setAccessToken:@"pk.feedcafedeadbeefbadebede"];
    NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
    self.mapView = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
}

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

- (void)testZoom {
    CLLocationCoordinate2D originalCenterCoordinate = self.mapView.centerCoordinate;

    for (NSNumber *zoom in @[@1, @5, @10, @15, @22]) {
        self.mapView.zoomLevel = [zoom doubleValue];
        XCTAssertEqual(self.mapView.zoomLevel, [zoom doubleValue], @"Map zoom should match z%@.", zoom);

        XCTAssertEqualWithAccuracy(originalCenterCoordinate.latitude, self.mapView.centerCoordinate.latitude, 0.0000001, "@Map center coordinate latitude should remain constant during zoom to z%@.", zoom);
        XCTAssertEqualWithAccuracy(originalCenterCoordinate.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant during zoom to z%@.", zoom);
    }
}

- (void)testZoomEnabled {
    UIPinchGestureRecognizerMock *gesture = [[UIPinchGestureRecognizerMock alloc] initWithTarget:nil action:nil];
    gesture.state = UIGestureRecognizerStateBegan;
    gesture.scale = 10.f;
    [self.mapView handlePinchGesture:gesture];
    gesture.state = UIGestureRecognizerStateChanged;

    // Disabled
    {
        self.mapView.zoomEnabled = NO;
        XCTAssertEqual(self.mapView.allowsZooming, NO);

        [self.mapView handlePinchGesture:gesture];
        XCTAssertNotEqual(self.mapView.zoomLevel, log2(gesture.scale), @"Gestural zoom should not work when zoom is disabled.");

        self.mapView.zoomLevel = 15.f;
        XCTAssertEqualWithAccuracy(self.mapView.zoomLevel, 15, 0.001, @"Programmatic zoom is allowed when zoomEnabled = NO.");
    }

    // Enabled
    {
        // No need to reset the map zoom or gesture scale, since gesture scale hasn't been applied yet and the map zoom will be overriden when the gesture is handled.
        self.mapView.zoomEnabled = YES;
        XCTAssertEqual(self.mapView.allowsZooming, YES);

        [self.mapView handlePinchGesture:gesture];
        XCTAssertEqualWithAccuracy(self.mapView.zoomLevel, log2(gesture.scale), 0.001, @"Gestural zoom should work when zoom is enabled.");
    }
}

- (void)testPinchGesture {
    CLLocationCoordinate2D originalCenterCoordinate = self.mapView.centerCoordinate;

    UIPinchGestureRecognizerMock *gesture = [[UIPinchGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
    gesture.state = UIGestureRecognizerStateBegan;
    gesture.scale = 0;
    gesture.locationInViewOverride = self.mapView.center;
    [self.mapView handlePinchGesture:gesture];
    XCTAssertEqual(self.mapView.zoomLevel, 0);

    for (NSNumber *zoom in @[@1, @5, @10, @15, @22]) {
        gesture.state = UIGestureRecognizerStateChanged;
        gesture.scale = MGLScaleFromZoomLevel([zoom doubleValue]);
        [self.mapView handlePinchGesture:gesture];
        XCTAssertEqual(self.mapView.zoomLevel, [zoom doubleValue], @"Map zoom should match gesture to z%@.", zoom);

        // Given a hypothetical zoom into the exact center of the map, the center coordinate should remain the same.
        XCTAssertEqualWithAccuracy(originalCenterCoordinate.latitude, self.mapView.centerCoordinate.latitude, 0.0000001, "@Map center coordinate latitude should remain constant during zoom to z%@.", zoom);
        XCTAssertEqualWithAccuracy(originalCenterCoordinate.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant during zoom to z%@.", zoom);
    }
}

// Regression test for: https://github.com/mapbox/mapbox-gl-native/issues/14977
- (void)testPinchGestureOffCenter {
    self.mapView.zoomLevel = 15;

    // Set up pinch gesture at z15 with an origin of 0,0.
    UIPinchGestureRecognizerMock *gesture = [[UIPinchGestureRecognizerMock alloc] initWithTarget:self.mapView action:nil];
    gesture.state = UIGestureRecognizerStateBegan;
    gesture.scale = 0;
    gesture.locationInViewOverride = CGPointMake(0, 0);
    [self.mapView handlePinchGesture:gesture];
    XCTAssertEqual(self.mapView.zoomLevel, 15);

    // Set a map rotation so that we can later check if un-rotating happens around the correct center point.
    self.mapView.direction = 45;

    // Zoom to z18 at the off-center origin.
    gesture.state = UIGestureRecognizerStateChanged;
    gesture.scale = MGLScaleFromZoomLevel(3);
    [self.mapView handlePinchGesture:gesture];
    XCTAssertEqual(self.mapView.zoomLevel, 18, @"Map zoom should match original zoom plus gesture zoom.");

    // Check that the center coordinate remains the same when direction is reset to north.
    CLLocationCoordinate2D centerCoordinateBeforeReset = self.mapView.centerCoordinate;
    CLLocationCoordinate2D manuallyDerivedCenterCoordinate = [self.mapView convertPoint:self.mapView.center toCoordinateFromView:nil];
    XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.latitude, manuallyDerivedCenterCoordinate.latitude, 0.0000001, "@Map center latitude should be equal to manually derived latitude.");
    XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.longitude, manuallyDerivedCenterCoordinate.longitude, 0.0000001, @"Map center longitude should be equal to manually derived longitude.");

    self.mapView.direction = 0;
    XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.latitude, self.mapView.centerCoordinate.latitude, 0.0000001, "@Map center coordinate latitude should remain constant after resetting to north.");
    XCTAssertEqualWithAccuracy(centerCoordinateBeforeReset.longitude, self.mapView.centerCoordinate.longitude, 0.0000001, @"Map center coordinate longitude should remain constant after resetting to north.");
}

NS_INLINE CGFloat MGLScaleFromZoomLevel(double zoom) {
    return pow(2, zoom);
}

__unused NS_INLINE double MGLZoomLevelFromScale(CGFloat scale) {
    return log2(scale);
}

@end