From d72b54c797b777a1b07406feeeb4d4e7ed020547 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:14:44 -0500 Subject: [PATCH 1/8] [ios] Fix safeAreaInsets availability warning in MGLMapViewLayoutTests Fixes: 'safeAreaInsets' is only available on iOS 11.0 or newer [-Wunguarded-availability-new] --- platform/ios/test/MGLMapViewLayoutTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/test/MGLMapViewLayoutTests.m b/platform/ios/test/MGLMapViewLayoutTests.m index a41e7695f90..e707cfdb41a 100644 --- a/platform/ios/test/MGLMapViewLayoutTests.m +++ b/platform/ios/test/MGLMapViewLayoutTests.m @@ -67,7 +67,7 @@ - (void)testOrnamentPlacement { CGFloat bottomSafeAreaInset = 0.0; double accuracy = 0.01; - if ( [self.mapView respondsToSelector:@selector(safeAreaInsets)] ) { + if (@available(iOS 11.0, *)) { bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom; } From aa632355e12982018a738a7846d2229d38265bbe Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:39:12 -0500 Subject: [PATCH 2/8] [ios] Fix type conversion warnings in MGLFeatureTests Fixes: object of type 'MGLPointAnnotation/MGLPolyline *' is not compatible with array element type 'MGLShape *' [-Wobjc-literal-conversion] --- platform/darwin/test/MGLFeatureTests.mm | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm index 818ad8200e6..14b64be8545 100644 --- a/platform/darwin/test/MGLFeatureTests.mm +++ b/platform/darwin/test/MGLFeatureTests.mm @@ -298,29 +298,36 @@ - (void)testPointCollectionFeatureGeoJSONDictionary { } - (void)testShapeCollectionFeatureGeoJSONDictionary { - MGLPointAnnotation *pointFeature = [[MGLPointAnnotation alloc] init]; + MGLPointFeature *pointFeature = [[MGLPointFeature alloc] init]; CLLocationCoordinate2D pointCoordinate = { 10, 10 }; pointFeature.coordinate = pointCoordinate; CLLocationCoordinate2D coord1 = { 0, 0 }; CLLocationCoordinate2D coord2 = { 10, 10 }; CLLocationCoordinate2D coords[] = { coord1, coord2 }; - MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:coords count:2]; + MGLPolylineFeature *polylineFeature = [MGLPolylineFeature polylineWithCoordinates:coords count:2]; + + MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, polylineFeature]]; - MGLShapeCollectionFeature *shapeCollectionFeature = [MGLShapeCollectionFeature shapeCollectionWithShapes:@[pointFeature, - polyline]]; // A GeoJSON feature NSDictionary *geoJSONFeature = [shapeCollectionFeature geoJSONDictionary]; // it has the correct geometry NSDictionary *expectedGeometry = @{@"type": @"GeometryCollection", @"geometries": @[ - @{@"type": @"Point", - @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]}, - @{@"type": @"LineString", - @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)], - @[@(coord2.longitude), @(coord2.latitude)]]} - ]}; + @{ @"geometry": @{@"type": @"Point", + @"coordinates": @[@(pointCoordinate.longitude), @(pointCoordinate.latitude)]}, + @"properties": [NSNull null], + @"type": @"Feature", + }, + @{ @"geometry": @{@"type": @"LineString", + @"coordinates": @[@[@(coord1.longitude), @(coord1.latitude)], + @[@(coord2.longitude), @(coord2.latitude)]]}, + @"properties": [NSNull null], + @"type": @"Feature", + } + ] + }; XCTAssertEqualObjects(geoJSONFeature[@"geometry"], expectedGeometry); // When the shape collection is created with an empty array of shapes From 58ac64e7eb5b799aa7cd7b39b51065a379b53566 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 16:14:29 -0500 Subject: [PATCH 3/8] [ios] Fix iOS 8's broken pluralization in MGLCoordinateFormatterTests --- .../darwin/test/MGLCoordinateFormatterTests.m | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m index ac083fa1035..d693f739ecb 100644 --- a/platform/darwin/test/MGLCoordinateFormatterTests.m +++ b/platform/darwin/test/MGLCoordinateFormatterTests.m @@ -24,7 +24,12 @@ - (void)testStrings { coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°54′48″N, 77°1′57″W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°54′48″ north, 77°1′57″ west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s), 54 minute(s), and 48 second(s) north by 77 degree(s), 1 minute(s), and 57 second(s) west"); + } shortFormatter.allowsSeconds = NO; mediumFormatter.allowsSeconds = NO; @@ -33,7 +38,12 @@ - (void)testStrings { coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"38°55′N, 77°2′W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"38°55′ north, 77°2′ west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degree(s) and 55 minute(s) north by 77 degree(s) and 2 minute(s) west"); + } shortFormatter.allowsMinutes = NO; mediumFormatter.allowsMinutes = NO; @@ -42,7 +52,12 @@ - (void)testStrings { coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° west"); - XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); + if (@available(iOS 9.0, *)) { + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); + } else { + // Foundation in iOS 8 does not know how to pluralize coordinates. + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degree(s) north by 77 degree(s) west"); + } } @end From 41e423093e54bf84c04d31bf8450086682b4657c Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 19:11:22 -0500 Subject: [PATCH 4/8] [ios] Bump testMGLMapSnapshotter timeout to 5s Timing based tests are inherently flakey and prone to failure on slow CI: > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone X' (3.375 seconds) > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone 8' (3.413 seconds) > Test case 'MGLDocumentationExampleTests.testMGLMapSnapshotter()' failed on 'iPhone 7' (2.944 seconds) --- platform/darwin/test/MGLDocumentationExampleTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/darwin/test/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift index 5a6e00bc4e2..a216d9ad1ca 100644 --- a/platform/darwin/test/MGLDocumentationExampleTests.swift +++ b/platform/darwin/test/MGLDocumentationExampleTests.swift @@ -391,7 +391,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate { } //#-end-example-code - wait(for: [expectation], timeout: 1) + wait(for: [expectation], timeout: 5) } // For testMGLMapView(). From 6130fb270b158dee35c7785b8b9dd76990e1c165 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 15 Feb 2018 12:18:08 -0500 Subject: [PATCH 5/8] [ios] Disable -[MGLExpressionTests testConditionalExpressionObject] on iOS 8 Temporarily disable this test until iOS 8 compatibility is added. --- platform/darwin/test/MGLExpressionTests.mm | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 821c5dbdb4d..a5ed2f7bf54 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -573,23 +573,27 @@ - (void)testInterpolationExpressionObject { } - (void)testConditionalExpressionObject { - { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; - NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; - NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; - NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; - NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); - } - { - NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; - NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; - XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); - XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + // FIXME: This test crashes because iOS 8 doesn't have `+[NSExpression expressionForConditional:trueExpression:falseExpression:]`. + // https://github.com/mapbox/mapbox-gl-native/issues/11007 + if (@available(iOS 9.0, *)) { + { + NSPredicate *conditional = [NSPredicate predicateWithFormat:@"1 = 2"]; + NSExpression *trueExpression = [NSExpression expressionForConstantValue:@YES]; + NSExpression *falseExpression = [NSExpression expressionForConstantValue:@NO]; + NSExpression *expression = [NSExpression expressionForConditional:conditional trueExpression:trueExpression falseExpression:falseExpression]; + NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithFormat:@"TERNARY(1 = 2, TRUE, FALSE)"].mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(0 = 1, TRUE, TERNARY(1 = 2, TRUE, FALSE))"]; + NSArray *jsonExpression = @[@"case", @[@"==", @0, @1], @YES, @[@"==", @1, @2], @YES, @NO]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); + XCTAssertEqualObjects([NSExpression mgl_expressionWithJSONObject:jsonExpression], expression); + } } } From e119be76777469273448378287c5677046226fb3 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:01:03 -0500 Subject: [PATCH 6/8] [ios] Fix loop when first asking for location permission (#11229) --- platform/ios/CHANGELOG.md | 4 ++++ platform/ios/src/MGLMapView.mm | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index c11fc63ebd8..ab375265731 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -44,6 +44,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) +## 3.7.5 + +* Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) + ## 3.7.4 - February 12, 2018 * Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141)) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 72fa4613fc3..cc6dd5ddd19 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -543,8 +543,9 @@ - (void)commonInit [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationWillEnterForegroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sleepGL:) name:UIApplicationWillResignActiveNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wakeGL:) name:UIApplicationDidBecomeActiveNotification object:nil]; + // As of 3.7.5, we intentionally do not listen for `UIApplicationWillResignActiveNotification` or call `sleepGL:` in response to it, as doing + // so causes a loop when asking for location permission. See: https://github.com/mapbox/mapbox-gl-native/issues/11225 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; From 35e2c8bfa004d8c3089fb7184afe48700ce80935 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:06:32 -0500 Subject: [PATCH 7/8] [ios] Bump podspecs for 3.7.5 --- platform/ios/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ab375265731..094634980e9 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -44,7 +44,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Labels are now transliterated from more languages when VoiceOver is enabled. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) * Long-pressing the attribution button causes the SDK’s version number to be displayed in the action sheet that appears. ([#10650](https://github.com/mapbox/mapbox-gl-native/pull/10650)) -## 3.7.5 +## 3.7.5 - February 16, 2018 * Fixed an issue where requesting location services permission would trigger an unrecoverable loop. ([#11229](https://github.com/mapbox/mapbox-gl-native/pull/11229)) From b9df1d91fa838c51227223ae195009f589e48156 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Fri, 16 Feb 2018 12:38:23 -0800 Subject: [PATCH 8/8] [core] Support a range of zooms in TileRange. Accounts for TilePyramid requesting parent tiles of ideal zoom tiles. --- src/mbgl/renderer/tile_pyramid.cpp | 7 +++- src/mbgl/util/tile_range.hpp | 60 ++++++++++++++++++++---------- test/util/tile_range.test.cpp | 13 ++++++- 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index c4372e7112a..8f83a0f9829 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -143,10 +143,13 @@ void TilePyramid::update(const std::vector>& layer auto it = tiles.find(tileID); return it == tiles.end() ? nullptr : it->second.get(); }; - + + // The min and max zoom for TileRange are based on the updateRenderables algorithm. + // Tiles are created at the ideal tile zoom or at lower zoom levels. Child + // tiles are used from the cache, but not created. optional tileRange = {}; if (bounds) { - tileRange = util::TileRange::fromLatLngBounds(*bounds, std::min(tileZoom, (int32_t)zoomRange.max)); + tileRange = util::TileRange::fromLatLngBounds(*bounds, zoomRange.min, std::min(tileZoom, (int32_t)zoomRange.max)); } auto createTileFn = [&](const OverscaledTileID& tileID) -> Tile* { if (tileRange && !tileRange->contains(tileID.canonical)) { diff --git a/src/mbgl/util/tile_range.hpp b/src/mbgl/util/tile_range.hpp index f630a490780..8554cfb65ec 100644 --- a/src/mbgl/util/tile_range.hpp +++ b/src/mbgl/util/tile_range.hpp @@ -6,41 +6,61 @@ #include namespace mbgl { - namespace util { class TileRange { public: - Range> range; - uint8_t z; + Range> range; + Range zoomRange; + + // Compute the range of tiles covered by the bounds at maxZoom. + static TileRange fromLatLngBounds(const LatLngBounds& bounds, uint8_t minZoom, uint8_t maxZoom) { + if (minZoom > maxZoom) { + std::swap(minZoom, maxZoom); + } + + auto swProj = Projection::project(bounds.southwest().wrapped(), maxZoom); + auto ne = bounds.northeast(); + auto neProj = Projection::project(ne.longitude() > util::LONGITUDE_MAX ? ne.wrapped() : ne , maxZoom); + + const auto maxTile = std::pow(2.0, maxZoom); + const auto minX = static_cast(std::floor(swProj.x)); + const auto maxX = static_cast(std::floor(neProj.x)); + const auto minY = static_cast(util::clamp(std::floor(neProj.y), 0.0 , maxTile)); + const auto maxY = static_cast(util::clamp(std::floor(swProj.y), 0.0, maxTile)); + + return TileRange({ {minX, minY}, {maxX, maxY} }, {minZoom, maxZoom}); + } // Compute the range of tiles covered by the bounds. static TileRange fromLatLngBounds(const LatLngBounds& bounds, uint8_t z) { - auto swProj = Projection::project(bounds.southwest().wrapped(), z); - auto ne = bounds.northeast(); - auto neProj = Projection::project(ne.longitude() > util::LONGITUDE_MAX ? ne.wrapped() : ne , z); - const auto minX = std::floor(swProj.x); - const auto maxX = std::ceil(neProj.x); - const auto minY = std::floor(neProj.y); - const auto maxY = std::ceil(swProj.y); - return TileRange({ {minX, minY}, {maxX, maxY} }, z); + return fromLatLngBounds(bounds, z, z); } bool contains(const CanonicalTileID& tileID) { - return z == tileID.z && - (range.min.x >= range.max.x ? //For wrapped bounds - tileID.x >= range.min.x || tileID.x < range.max.x : - tileID.x < range.max.x && tileID.x >= range.min.x) && - tileID.y < range.max.y && - tileID.y >= range.min.y; + if (tileID.z <= zoomRange.max && tileID.z >= zoomRange.min) { + if (tileID.z == 0) { + return true; + } + uint8_t dz = (zoomRange.max - tileID.z); + auto x0 = range.min.x >> dz; + auto x1 = range.max.x >> dz; + auto y0 = range.min.y >> dz; + auto y1 = range.max.y >> dz; + return (range.min.x > range.max.x ? //For wrapped bounds + tileID.x >= x0 || tileID.x <= x1 : + tileID.x <= x1 && tileID.x >= x0) && + tileID.y <= y1 && + tileID.y >= y0; + } + return false; } private: - TileRange(Range> range_, uint8_t z_) + TileRange(Range> range_, Range z_) : range(range_), - z(z_) { + zoomRange(z_) { } - }; } // namespace util diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp index dc8ae287059..c4c37c74d7d 100644 --- a/test/util/tile_range.test.cpp +++ b/test/util/tile_range.test.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -25,6 +24,18 @@ TEST(TileRange, ContainsBoundsFromTile) { EXPECT_TRUE(range.contains(CanonicalTileID(10, 162, 395))); } } + +TEST(TileRange, ContainsMultiZoom) { + auto wrappedBounds = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); + auto range = util::TileRange::fromLatLngBounds(wrappedBounds, 5, 13); + EXPECT_FALSE(range.contains(CanonicalTileID(0, 0, 0))); + EXPECT_FALSE(range.contains(CanonicalTileID(5, 3, 11))); + EXPECT_FALSE(range.contains(CanonicalTileID(6, 9, 22))); + EXPECT_TRUE(range.contains(CanonicalTileID(5, 5, 12))); + EXPECT_TRUE(range.contains(CanonicalTileID(6, 10, 24))); + EXPECT_TRUE(range.contains(CanonicalTileID(13, 1310, 3166))); +} + TEST(TileRange, ContainsIntersectingTiles) { auto bounds = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); auto range = util::TileRange::fromLatLngBounds(bounds, 13);