From e4a40fe7974462369533722a20d252b753a55347 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Thu, 15 Feb 2018 12:02:00 -0500 Subject: [android] bounds can go over the antimeridian / date line. (#10892) --- .../mapboxsdk/constants/GeometryConstants.java | 14 +++ .../mapbox/mapboxsdk/geometry/LatLngBounds.java | 112 ++++++++++++++---- .../mapboxsdk/geometry/LatLngBoundsTest.java | 131 +++++++++++++++++++++ 3 files changed, 236 insertions(+), 21 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java index 1a7544d33a..7a17e500ca 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java @@ -36,6 +36,20 @@ public class GeometryConstants { */ public static final double MIN_LATITUDE = -90; + /** + * This constant represents the latitude span when representing a geolocation. + * + * @since 6.0.0 + */ + public static final double LATITUDE_SPAN = 180; + + /** + * This constant represents the longitude span when representing a geolocation. + * + * @since 6.0.0 + */ + public static final double LONGITUDE_SPAN = 360; + /** * This constant represents the highest latitude value available to represent a geolocation. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index cf647224ae..fc8d2ec8f0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -29,6 +29,10 @@ public class LatLngBounds implements Parcelable { * Construct a new LatLngBounds based on its corners, given in NESW * order. * + * If eastern longitude is smaller than the western one, bounds will include antimeridian. + * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees + * and cross the antimeridian. + * * @param northLatitude Northern Latitude * @param eastLongitude Eastern Longitude * @param southLatitude Southern Latitude @@ -48,10 +52,9 @@ public class LatLngBounds implements Parcelable { * @return the bounds representing the world */ public static LatLngBounds world() { - return new LatLngBounds.Builder() - .include(new LatLng(GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE)) - .include(new LatLng(GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE)) - .build(); + return LatLngBounds.from( + GeometryConstants.MAX_LATITUDE, GeometryConstants.MAX_LONGITUDE, + GeometryConstants.MIN_LATITUDE, GeometryConstants.MIN_LONGITUDE); } /** @@ -61,8 +64,21 @@ public class LatLngBounds implements Parcelable { * @return LatLng center of this LatLngBounds */ public LatLng getCenter() { - return new LatLng((this.latitudeNorth + this.latitudeSouth) / 2, - (this.longitudeEast + this.longitudeWest) / 2); + double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0; + double longCenter; + + if (this.longitudeEast > this.longitudeWest) { + longCenter = (this.longitudeEast + this.longitudeWest) / 2; + } else { + double halfSpan = (GeometryConstants.LONGITUDE_SPAN + this.longitudeEast - this.longitudeWest) / 2.0; + longCenter = this.longitudeWest + halfSpan; + if (longCenter >= GeometryConstants.MAX_LONGITUDE) { + longCenter = this.longitudeEast - halfSpan; + } + return new LatLng(latCenter, longCenter); + } + + return new LatLng(latCenter, longCenter); } /** @@ -163,10 +179,26 @@ public class LatLngBounds implements Parcelable { * @return Span distance */ public double getLongitudeSpan() { - return Math.abs(this.longitudeEast - this.longitudeWest); + double longSpan = Math.abs(this.longitudeEast - this.longitudeWest); + if (this.longitudeEast > this.longitudeWest) { + return longSpan; + } + + // shortest span contains antimeridian + return GeometryConstants.LONGITUDE_SPAN - longSpan; } + static double getLongitudeSpan(final double longEast, final double longWest) { + double longSpan = Math.abs(longEast - longWest); + if (longEast > longWest) { + return longSpan; + } + + // shortest span contains antimeridian + return GeometryConstants.LONGITUDE_SPAN - longSpan; + } + /** * Validate if LatLngBounds is empty, determined if absolute distance is * @@ -196,21 +228,44 @@ public class LatLngBounds implements Parcelable { */ static LatLngBounds fromLatLngs(final List latLngs) { double minLat = GeometryConstants.MAX_LATITUDE; - double minLon = GeometryConstants.MAX_LONGITUDE; double maxLat = GeometryConstants.MIN_LATITUDE; - double maxLon = GeometryConstants.MIN_LONGITUDE; + + double eastLon = latLngs.get(0).getLongitude(); + double westLon = latLngs.get(1).getLongitude(); + double lonSpan = Math.abs(eastLon - westLon); + if (lonSpan < GeometryConstants.LONGITUDE_SPAN / 2) { + if (eastLon < westLon) { + double temp = eastLon; + eastLon = westLon; + westLon = temp; + } + } else { + lonSpan = GeometryConstants.LONGITUDE_SPAN - lonSpan; + if (westLon < eastLon) { + double temp = eastLon; + eastLon = westLon; + westLon = temp; + } + } for (final ILatLng gp : latLngs) { final double latitude = gp.getLatitude(); - final double longitude = gp.getLongitude(); - minLat = Math.min(minLat, latitude); - minLon = Math.min(minLon, longitude); maxLat = Math.max(maxLat, latitude); - maxLon = Math.max(maxLon, longitude); + + final double longitude = gp.getLongitude(); + if (!containsLongitude(eastLon, westLon, longitude)) { + final double eastSpan = getLongitudeSpan(longitude, westLon); + final double westSpan = getLongitudeSpan(eastLon, longitude); + if (eastSpan <= westSpan) { + eastLon = longitude; + } else { + westLon = longitude; + } + } } - return new LatLngBounds(maxLat, maxLon, minLat, minLon); + return new LatLngBounds(maxLat, eastLon, minLat, westLon); } /** @@ -322,6 +377,24 @@ public class LatLngBounds implements Parcelable { return false; } + + private boolean containsLatitude(final double latitude) { + return (latitude <= this.latitudeNorth) + && (latitude >= this.latitudeSouth); + } + + private boolean containsLongitude(final double longitude) { + return containsLongitude(this.longitudeEast, this.longitudeWest, longitude); + } + + static boolean containsLongitude(final double eastLon, final double westLon, final double longitude) { + if (eastLon > westLon) { + return (longitude <= eastLon) + && (longitude >= westLon); + } + return (longitude < eastLon) || (longitude > westLon); + } + /** * Determines whether this LatLngBounds contains a point. * @@ -329,12 +402,8 @@ public class LatLngBounds implements Parcelable { * @return true, if the point is contained within the bounds */ public boolean contains(final ILatLng latLng) { - final double latitude = latLng.getLatitude(); - final double longitude = latLng.getLongitude(); - return ((latitude <= this.latitudeNorth) - && (latitude >= this.latitudeSouth)) - && ((longitude <= this.longitudeEast) - && (longitude >= this.longitudeWest)); + return containsLatitude(latLng.getLatitude()) + && containsLongitude(latLng.getLongitude()); } /** @@ -344,7 +413,8 @@ public class LatLngBounds implements Parcelable { * @return true, if the bounds is contained within the bounds */ public boolean contains(final LatLngBounds other) { - return contains(other.getNorthEast()) && contains(other.getSouthWest()); + return contains(other.getNorthEast()) + && contains(other.getSouthWest()); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e6c1fdd0cf..f03bbdb11c 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -70,6 +70,82 @@ public class LatLngBoundsTest { assertEquals("LatLngSpan should be the same", new LatLngSpan(2, 2), latLngSpan); } + @Test + public void dateLineSpanBuilder1() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanBuilder2() { + latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(-10, -170)) + .include(new LatLng(10, 170)) + .build(); + + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom1() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 20), + latLngSpan); + } + + @Test + public void dateLineSpanFrom2() { + latLngBounds = LatLngBounds.from(10, 170, -10, -170); + LatLngSpan latLngSpan = latLngBounds.getSpan(); + assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 340), + latLngSpan); + } + + @Test + public void nearDateLineCenter1() { + latLngBounds = LatLngBounds.from(10, -175, -10, 165); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 175), center); + } + + @Test + public void nearDateLineCenter2() { + latLngBounds = LatLngBounds.from(10, -165, -10, 175); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -175), center); + } + + @Test + public void nearDateLineCenter3() { + latLngBounds = LatLngBounds.from(10, -170, -10, 170); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, -180), center); + } + + @Test + public void nearDateLineCenter4() { + latLngBounds = LatLngBounds.from(10, -180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + @Test + public void nearDateLineCenter5() { + latLngBounds = LatLngBounds.from(10, 180, -10, 0); + LatLng center = latLngBounds.getCenter(); + assertEquals("Center should match", new LatLng(0, 90), center); + } + + @Test public void center() { LatLng center = latLngBounds.getCenter(); @@ -120,6 +196,46 @@ public class LatLngBoundsTest { assertEquals("LatLngBounds should match", latLngBounds1, latLngBounds2); } + @Test + public void includesOverDateline1() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, -170)) + .include(new LatLng(-10, -175)) + .include(new LatLng(0, 170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline2() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, 175)) + .include(new LatLng(0, -170)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + + @Test + public void includesOverDateline3() { + + LatLngBounds latLngBounds = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(-10, -170)) + .include(new LatLng(0, -180)) + .include(new LatLng(5, 180)) + .build(); + + assertEquals("LatLngSpan should be the same", + new LatLngSpan(20, 20), latLngBounds.getSpan()); + } + @Test public void containsNot() { assertFalse("LatLng should not be included", latLngBounds.contains(new LatLng(3, 1))); @@ -130,6 +246,21 @@ public class LatLngBoundsTest { assertTrue("LatLngBounds should be contained in the world", LatLngBounds.world().contains(latLngBounds)); } + @Test + public void worldSpan() { + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LATITUDE_SPAN, LatLngBounds.world().getLatitudeSpan(), DELTA); + assertEquals("LatLngBounds world span should be 180, 360", + GeometryConstants.LONGITUDE_SPAN, LatLngBounds.world().getLongitudeSpan(), DELTA); + } + + @Test + public void emptySpan() { + LatLngBounds latLngBounds = LatLngBounds.from(GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE, + GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE); + assertTrue("LatLngBounds empty span", latLngBounds.isEmptySpan()); + } + @Test public void containsBounds() { LatLngBounds inner = new LatLngBounds.Builder() -- cgit v1.2.1 From fdbcd47002d3d4b10df4a9b314616942a1249191 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:14:44 -0500 Subject: [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 a41e7695f9..e707cfdb41 100644 --- a/platform/ios/test/MGLMapViewLayoutTests.m +++ b/platform/ios/test/MGLMapViewLayoutTests.m @@ -67,7 +67,7 @@ CGFloat bottomSafeAreaInset = 0.0; double accuracy = 0.01; - if ( [self.mapView respondsToSelector:@selector(safeAreaInsets)] ) { + if (@available(iOS 11.0, *)) { bottomSafeAreaInset = self.mapView.safeAreaInsets.bottom; } -- cgit v1.2.1 From 709164e75a9da36e16a914b08666b21b87108c2f Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 15:39:12 -0500 Subject: [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 818ad8200e..14b64be854 100644 --- a/platform/darwin/test/MGLFeatureTests.mm +++ b/platform/darwin/test/MGLFeatureTests.mm @@ -298,29 +298,36 @@ } - (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 -- cgit v1.2.1 From 2c69b9985943133715a25ec771a3a83979eabeb8 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 16:14:29 -0500 Subject: [ios] Fix iOS 8's broken pluralization in MGLCoordinateFormatterTests --- platform/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 ac083fa103..d693f739ec 100644 --- a/platform/darwin/test/MGLCoordinateFormatterTests.m +++ b/platform/darwin/test/MGLCoordinateFormatterTests.m @@ -24,7 +24,12 @@ 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 @@ 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 @@ 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 -- cgit v1.2.1 From 47d96dddc438a0cc6716f3428b5a09b55b6ab133 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 13 Feb 2018 19:11:22 -0500 Subject: [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 5a6e00bc4e..a216d9ad1c 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(). -- cgit v1.2.1 From 63eb511d8365c5a22cde65a9118559dc7561f1de Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 15 Feb 2018 12:18:08 -0500 Subject: [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 821c5dbdb4..a5ed2f7bf5 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -573,23 +573,27 @@ using namespace std::string_literals; } - (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); + } } } -- cgit v1.2.1 From 341eb7645f98fb1835607dbe68b2bd74b0f6ec8a Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Fri, 16 Feb 2018 09:36:56 -0500 Subject: [android] incorrect LatLngBounds for the VisibleRegion for rotated map smallest bounding box for 4 points cannot be created using LatLngBounds.fromLatLngs() as the order matters in that method and that does not work for rotated map --- .../src/main/java/com/mapbox/mapboxsdk/maps/Projection.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 16c73b1ca5..ae559189ad 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -104,11 +104,12 @@ public class Projection { LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - LatLngBounds.from( - topRight.getLatitude(), - topRight.getLongitude(), - bottomLeft.getLatitude(), - bottomLeft.getLongitude()) + new LatLngBounds.Builder() + .include(topRight) + .include(bottomLeft) + .include(bottomRight) + .include(topLeft) + .build() ); } -- cgit v1.2.1 From 8635dab4c38fcd67962819224093d0be95f5ed43 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 8 Feb 2018 15:23:29 -0800 Subject: [core] Implement Expression::serialize() Issue #10714 - Each expression stores its operator as a string, and default serialization is [operator, serialize(child1), ...] - Custom implementations of `serialize` for Expression types that don't follow the pattern - expression::Value -> mbgl::Value converter - node_expression bindings to expose `serialize` --- include/mbgl/style/expression/array_assertion.hpp | 3 ++ include/mbgl/style/expression/assertion.hpp | 2 ++ include/mbgl/style/expression/at.hpp | 2 ++ include/mbgl/style/expression/boolean_operator.hpp | 2 ++ include/mbgl/style/expression/case.hpp | 1 + include/mbgl/style/expression/coalesce.hpp | 1 + include/mbgl/style/expression/coercion.hpp | 1 + .../mbgl/style/expression/compound_expression.hpp | 15 +++++--- include/mbgl/style/expression/equals.hpp | 1 + include/mbgl/style/expression/expression.hpp | 11 ++++++ include/mbgl/style/expression/interpolate.hpp | 3 ++ include/mbgl/style/expression/let.hpp | 4 +++ include/mbgl/style/expression/literal.hpp | 14 ++++++-- include/mbgl/style/expression/match.hpp | 4 ++- include/mbgl/style/expression/step.hpp | 2 ++ include/mbgl/style/expression/value.hpp | 1 + include/mbgl/style/function/convert.hpp | 1 + platform/node/src/node_expression.cpp | 27 +++++++++----- platform/node/src/node_expression.hpp | 3 ++ platform/node/test/expression.test.js | 4 ++- src/mbgl/style/expression/array_assertion.cpp | 19 ++++++++++ src/mbgl/style/expression/assertion.cpp | 4 +++ src/mbgl/style/expression/coercion.cpp | 7 ++++ src/mbgl/style/expression/compound_expression.cpp | 42 +++++++++++----------- src/mbgl/style/expression/interpolate.cpp | 24 +++++++++++++ src/mbgl/style/expression/let.cpp | 15 ++++++++ src/mbgl/style/expression/literal.cpp | 8 +++++ src/mbgl/style/expression/match.cpp | 36 +++++++++++++++++++ src/mbgl/style/expression/step.cpp | 12 +++++++ src/mbgl/style/expression/value.cpp | 33 ++++++++++++++++- 30 files changed, 262 insertions(+), 40 deletions(-) diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp index 7f36f8aac2..af153611ff 100644 --- a/include/mbgl/style/expression/array_assertion.hpp +++ b/include/mbgl/style/expression/array_assertion.hpp @@ -33,6 +33,9 @@ public: std::vector> possibleOutputs() const override { return input->possibleOutputs(); } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "array"; } private: std::unique_ptr input; diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp index 43ea73f2ba..d1e919b10f 100644 --- a/include/mbgl/style/expression/assertion.hpp +++ b/include/mbgl/style/expression/assertion.hpp @@ -26,6 +26,8 @@ public: bool operator==(const Expression& e) const override; std::vector> possibleOutputs() const override; + + std::string getOperator() const override; private: std::vector> inputs; diff --git a/include/mbgl/style/expression/at.hpp b/include/mbgl/style/expression/at.hpp index 27fccc761f..1e6f1c7dd2 100644 --- a/include/mbgl/style/expression/at.hpp +++ b/include/mbgl/style/expression/at.hpp @@ -31,6 +31,8 @@ public: std::vector> possibleOutputs() const override { return { nullopt }; } + + std::string getOperator() const override { return "at"; } private: std::unique_ptr index; diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp index 115a096665..6d0f85756a 100644 --- a/include/mbgl/style/expression/boolean_operator.hpp +++ b/include/mbgl/style/expression/boolean_operator.hpp @@ -23,6 +23,7 @@ public: bool operator==(const Expression& e) const override; std::vector> possibleOutputs() const override; + std::string getOperator() const override { return "any"; } private: std::vector> inputs; }; @@ -41,6 +42,7 @@ public: bool operator==(const Expression& e) const override; std::vector> possibleOutputs() const override; + std::string getOperator() const override { return "all"; } private: std::vector> inputs; }; diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp index e61a55fc6d..667ca53712 100644 --- a/include/mbgl/style/expression/case.hpp +++ b/include/mbgl/style/expression/case.hpp @@ -28,6 +28,7 @@ public: std::vector> possibleOutputs() const override; + std::string getOperator() const override { return "case"; } private: std::vector branches; std::unique_ptr otherwise; diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp index 52d9498cbd..a858bef695 100644 --- a/include/mbgl/style/expression/coalesce.hpp +++ b/include/mbgl/style/expression/coalesce.hpp @@ -38,6 +38,7 @@ public: return args.at(i).get(); } + std::string getOperator() const override { return "coalesce"; } private: Args args; }; diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp index 40d2490186..d83bd6dfa7 100644 --- a/include/mbgl/style/expression/coercion.hpp +++ b/include/mbgl/style/expression/coercion.hpp @@ -28,6 +28,7 @@ public: std::vector> possibleOutputs() const override; + std::string getOperator() const override; private: EvaluationResult (*coerceSingleValue) (const Value& v); std::vector> inputs; diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index 8b74027578..6baaae862f 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -40,14 +40,16 @@ namespace detail { // each CompoundExpression definition's type::Type data from the type of its // "evaluate" function. struct SignatureBase { - SignatureBase(type::Type result_, variant, VarargsType> params_) : + SignatureBase(type::Type result_, variant, VarargsType> params_, std::string name_) : result(std::move(result_)), - params(std::move(params_)) + params(std::move(params_)), + name(std::move(name_)) {} virtual ~SignatureBase() = default; - virtual std::unique_ptr makeExpression(const std::string& name, std::vector>) const = 0; + virtual std::unique_ptr makeExpression(std::vector>) const = 0; type::Type result; variant, VarargsType> params; + std::string name; }; } // namespace detail @@ -111,6 +113,10 @@ public: } return false; } + + std::string getOperator() const override { + return signature.name; + } private: Signature signature; @@ -128,8 +134,7 @@ struct CompoundExpressionRegistry { ParseResult parseCompoundExpression(const std::string name, const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); -ParseResult createCompoundExpression(const std::string& name, - const CompoundExpressionRegistry::Definition& definition, +ParseResult createCompoundExpression(const CompoundExpressionRegistry::Definition& definition, std::vector> args, ParsingContext& ctx); diff --git a/include/mbgl/style/expression/equals.hpp b/include/mbgl/style/expression/equals.hpp index 80550bd59d..54df890a68 100644 --- a/include/mbgl/style/expression/equals.hpp +++ b/include/mbgl/style/expression/equals.hpp @@ -21,6 +21,7 @@ public: EvaluationResult evaluate(const EvaluationContext&) const override; std::vector> possibleOutputs() const override; + std::string getOperator() const override { return negate ? "!=" : "=="; } private: std::unique_ptr lhs; std::unique_ptr rhs; diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp index cf9fa0cb21..c41ac0b5f1 100644 --- a/include/mbgl/style/expression/expression.hpp +++ b/include/mbgl/style/expression/expression.hpp @@ -135,6 +135,17 @@ public: * complete set of outputs is statically undecidable. */ virtual std::vector> possibleOutputs() const = 0; + + virtual mbgl::Value serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + eachChild([&](const Expression &child) { + serialized.emplace_back(child.serialize()); + }); + return serialized; + }; + + virtual std::string getOperator() const = 0; protected: template diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp index dbed74b4cd..cc744ac7b7 100644 --- a/include/mbgl/style/expression/interpolate.hpp +++ b/include/mbgl/style/expression/interpolate.hpp @@ -185,6 +185,9 @@ public: } return false; } + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "interpolate"; } }; } // namespace expression diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp index 6829ded9b8..75d2adda62 100644 --- a/include/mbgl/style/expression/let.hpp +++ b/include/mbgl/style/expression/let.hpp @@ -39,6 +39,8 @@ public: return result.get(); } + mbgl::Value serialize() const override; + std::string getOperator() const override { return "let"; } private: Bindings bindings; std::unique_ptr result; @@ -66,6 +68,8 @@ public: std::vector> possibleOutputs() const override; + mbgl::Value serialize() const override; + std::string getOperator() const override { return "var"; } private: std::string name; std::shared_ptr value; diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index 82983d78af..d854b419f4 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -12,8 +12,16 @@ namespace expression { class Literal : public Expression { public: - Literal(Value value_) : Expression(typeOf(value_)), value(value_) {} - Literal(type::Array type_, std::vector value_) : Expression(type_), value(value_) {} + Literal(Value value_) + : Expression(typeOf(value_)) + , value(value_) + {} + + Literal(type::Array type_, std::vector value_) + : Expression(type_) + , value(value_) + {} + EvaluationResult evaluate(const EvaluationContext&) const override { return value; } @@ -33,6 +41,8 @@ public: return {{ value }}; } + mbgl::Value serialize() const override; + std::string getOperator() const override { return "literal"; } private: Value value; }; diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp index 682d784b0f..3775e38067 100644 --- a/include/mbgl/style/expression/match.hpp +++ b/include/mbgl/style/expression/match.hpp @@ -32,7 +32,9 @@ public: bool operator==(const Expression& e) const override; std::vector> possibleOutputs() const override; - + + mbgl::Value serialize() const override; + std::string getOperator() const override { return "match"; } private: std::unique_ptr input; Branches branches; diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp index 6bf42e20f1..2f9524a53c 100644 --- a/include/mbgl/style/expression/step.hpp +++ b/include/mbgl/style/expression/step.hpp @@ -38,6 +38,8 @@ public: static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx); + mbgl::Value serialize() const override; + std::string getOperator() const override { return "step"; } private: const std::unique_ptr input; const std::map> stops; diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp index be5be64752..7839ff2ca7 100644 --- a/include/mbgl/style/expression/value.hpp +++ b/include/mbgl/style/expression/value.hpp @@ -110,6 +110,7 @@ struct ValueConverter { template<> struct ValueConverter { static Value toExpressionValue(const mbgl::Value& value); + static mbgl::Value fromExpressionValue(const Value& value); }; template diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp index 8e544d3ad5..401a81d52e 100644 --- a/include/mbgl/style/function/convert.hpp +++ b/include/mbgl/style/function/convert.hpp @@ -49,6 +49,7 @@ public: return {}; } + std::string getOperator() const override { return "error"; } private: std::string message; }; diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index 8958d5c6c7..84515060a3 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -1,5 +1,6 @@ #include "node_conversion.hpp" #include "node_expression.hpp" +#include "node_feature.hpp" #include #include @@ -24,6 +25,8 @@ void NodeExpression::Init(v8::Local target) { Nan::SetPrototypeMethod(tpl, "isFeatureConstant", IsFeatureConstant); Nan::SetPrototypeMethod(tpl, "isZoomConstant", IsZoomConstant); + Nan::SetPrototypeMethod(tpl, "serialize", Serialize); + Nan::SetMethod(tpl, "parse", Parse); constructor.Reset(tpl->GetFunction()); // what is this doing? @@ -39,31 +42,31 @@ type::Type parseType(v8::Local type) { {"color", type::Color}, {"value", type::Value} }; - + v8::Local v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked(); std::string kind(*v8::String::Utf8Value(v8kind)); - + if (kind == "array") { type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject()); mbgl::optional N; - + v8::Local Nkey = Nan::New("N").ToLocalChecked(); if (Nan::Has(type, Nkey).FromMaybe(false)) { N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value(); } return type::Array(itemType, N); } - + return types[kind]; } void NodeExpression::Parse(const Nan::FunctionCallbackInfo& info) { v8::Local cons = Nan::New(constructor); - + if (info.Length() < 1 || info[0]->IsUndefined()) { return Nan::ThrowTypeError("Requires a JSON style expression argument."); } - + mbgl::optional expected; if (info.Length() > 1 && info[1]->IsObject()) { expected = parseType(info[1]->ToObject()); @@ -84,7 +87,7 @@ void NodeExpression::Parse(const Nan::FunctionCallbackInfo& info) { info.GetReturnValue().Set(wrapped); return; } - + v8::Local result = Nan::New(); for (std::size_t i = 0; i < ctx.getErrors().size(); i++) { const auto& error = ctx.getErrors()[i]; @@ -140,7 +143,7 @@ struct ToValue { } return scope.Escape(result); } - + v8::Local operator()(const mbgl::Color& color) { return operator()(std::vector { static_cast(color.r), @@ -227,4 +230,12 @@ void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo& info.GetReturnValue().Set(Nan::New(isZoomConstant(*expression))); } +void NodeExpression::Serialize(const Nan::FunctionCallbackInfo& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); + const std::unique_ptr& expression = nodeExpr->expression; + + const mbgl::Value serialized = expression->serialize(); + info.GetReturnValue().Set(toJS(serialized)); +} + } // namespace node_mbgl diff --git a/platform/node/src/node_expression.hpp b/platform/node/src/node_expression.hpp index 7af5b7ab51..05af217bde 100644 --- a/platform/node/src/node_expression.hpp +++ b/platform/node/src/node_expression.hpp @@ -32,6 +32,9 @@ private: static void GetType(const Nan::FunctionCallbackInfo&); static void IsFeatureConstant(const Nan::FunctionCallbackInfo&); static void IsZoomConstant(const Nan::FunctionCallbackInfo&); + + static void Serialize(const Nan::FunctionCallbackInfo&); + static Nan::Persistent constructor; std::unique_ptr expression; diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js index aac039ce18..d7a44abbaa 100644 --- a/platform/node/test/expression.test.js +++ b/platform/node/test/expression.test.js @@ -45,6 +45,9 @@ suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { compiled.isZoomConstant = expression.isZoomConstant(); compiled.type = expression.getType(); + console.log("input: " + JSON.stringify(fixture.expression)); + console.log("output: " + JSON.stringify(expression.serialize())); + const evaluate = fixture.inputs || []; const evaluateResults = []; for (const input of evaluate) { @@ -68,4 +71,3 @@ suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { return result; }); - diff --git a/src/mbgl/style/expression/array_assertion.cpp b/src/mbgl/style/expression/array_assertion.cpp index 29f6a47b10..4049301b0b 100644 --- a/src/mbgl/style/expression/array_assertion.cpp +++ b/src/mbgl/style/expression/array_assertion.cpp @@ -81,6 +81,25 @@ ParseResult ArrayAssertion::parse(const Convertible& value, ParsingContext& ctx) )); } +mbgl::Value ArrayAssertion::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + + + const auto array = getType().get(); + if (array.itemType.is() + || array.itemType.is() + || array.itemType.is()) { + serialized.emplace_back(type::toString(array.itemType)); + if (array.N) { + serialized.emplace_back(uint64_t(*array.N)); + } + } + + serialized.emplace_back(input->serialize()); + return serialized; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp index 0187921af9..d6f3f1b584 100644 --- a/src/mbgl/style/expression/assertion.cpp +++ b/src/mbgl/style/expression/assertion.cpp @@ -35,6 +35,10 @@ ParseResult Assertion::parse(const Convertible& value, ParsingContext& ctx) { return ParseResult(std::make_unique(it->second, std::move(parsed))); } +std::string Assertion::getOperator() const { + return type::toString(getType()); +} + EvaluationResult Assertion::evaluate(const EvaluationContext& params) const { for (std::size_t i = 0; i < inputs.size(); i++) { EvaluationResult value = inputs[i]->evaluate(params); diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp index 56ab33fcfd..d9cd3ffdc9 100644 --- a/src/mbgl/style/expression/coercion.cpp +++ b/src/mbgl/style/expression/coercion.cpp @@ -81,6 +81,13 @@ Coercion::Coercion(type::Type type_, std::vector> in } } +std::string Coercion::getOperator() const { + return getType().match( + [](const type::NumberType&) { return "to-number"; }, + [](const type::ColorType&) { return "to-color"; }, + [](const auto&) { assert(false); return ""; }); +} + using namespace mbgl::style::conversion; ParseResult Coercion::parse(const Convertible& value, ParsingContext& ctx) { static std::unordered_map types { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 42cb655024..86d968c521 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -42,20 +42,19 @@ template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - Signature(R (*evaluate_)(Params...)) : + Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - std::vector {valueTypeToExpressionType>()...} + std::vector {valueTypeToExpressionType>()...}, + std::move(name_) ), - evaluate(evaluate_) - {} + evaluate(evaluate_) {} EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); @@ -80,16 +79,16 @@ template struct Signature&)> : SignatureBase { using Args = std::vector>; - Signature(R (*evaluate_)(const Varargs&)) : + Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - VarargsType { valueTypeToExpressionType() } + VarargsType { valueTypeToExpressionType() }, + std::move(name_) ), evaluate(evaluate_) {} - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { return std::make_unique>(name, *this, std::move(args)); }; @@ -115,16 +114,16 @@ template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - Signature(R (*evaluate_)(const EvaluationContext&, Params...)) : + Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), - std::vector {valueTypeToExpressionType>()...} + std::vector {valueTypeToExpressionType>()...}, + std::move(name_) ), evaluate(evaluate_) {} - std::unique_ptr makeExpression(const std::string& name, - std::vector> args) const override { + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); @@ -176,14 +175,14 @@ struct Signature::value>> using Definition = CompoundExpressionRegistry::Definition; template -static std::unique_ptr makeSignature(Fn evaluateFunction) { - return std::make_unique>(evaluateFunction); +static std::unique_ptr makeSignature(Fn evaluateFunction, std::string name) { + return std::make_unique>(evaluateFunction, std::move(name)); } std::unordered_map initializeDefinitions() { std::unordered_map definitions; auto define = [&](std::string name, auto fn) { - definitions[name].push_back(makeSignature(fn)); + definitions[name].push_back(makeSignature(fn, name)); }; define("e", []() -> Result { return 2.718281828459045; }); @@ -461,7 +460,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v } args.push_back(std::move(*parsed)); } - return createCompoundExpression(name, definition, std::move(args), ctx); + return createCompoundExpression(definition, std::move(args), ctx); } @@ -469,12 +468,11 @@ ParseResult createCompoundExpression(const std::string& name, std::vector> args, ParsingContext& ctx) { - return createCompoundExpression(name, CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx); + return createCompoundExpression(CompoundExpressionRegistry::definitions.at(name), std::move(args), ctx); } -ParseResult createCompoundExpression(const std::string& name, - const Definition& definition, +ParseResult createCompoundExpression(const Definition& definition, std::vector> args, ParsingContext& ctx) { @@ -512,7 +510,7 @@ ParseResult createCompoundExpression(const std::string& name, } if (signatureContext.getErrors().size() == 0) { - return ParseResult(signature->makeExpression(name, std::move(args))); + return ParseResult(signature->makeExpression(std::move(args))); } } diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp index 4cb22a3e4f..30b2cba81b 100644 --- a/src/mbgl/style/expression/interpolate.cpp +++ b/src/mbgl/style/expression/interpolate.cpp @@ -216,6 +216,30 @@ std::vector> InterpolateBase::possibleOutputs() const { return result; } +template +mbgl::Value Interpolate::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + + interpolator.match( + [&](const ExponentialInterpolator& exponential) { + serialized.emplace_back(std::vector{{ std::string("exponential"), exponential.base }}); + }, + [&](const CubicBezierInterpolator& cubicBezier) { + static const std::string cubicBezierTag("cubic-bezier"); + auto p1 = cubicBezier.ub.getP1(); + auto p2 = cubicBezier.ub.getP2(); + serialized.emplace_back(std::vector{{ cubicBezierTag, p1.first, p1.second, p2.first, p2.second }}); + } + ); + serialized.emplace_back(input->serialize()); + for (auto& entry : stops) { + serialized.emplace_back(entry.first); + serialized.emplace_back(entry.second->serialize()); + }; + return serialized; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp index fe48138ac3..242a995b0b 100644 --- a/src/mbgl/style/expression/let.cpp +++ b/src/mbgl/style/expression/let.cpp @@ -65,6 +65,17 @@ ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) { return ParseResult(std::make_unique(std::move(bindings_), std::move(*result_))); } +mbgl::Value Let::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + for (auto entry : bindings) { + serialized.emplace_back(entry.first); + serialized.emplace_back(entry.second->serialize()); + } + serialized.emplace_back(result->serialize()); + return serialized; +} + EvaluationResult Var::evaluate(const EvaluationContext& params) const { return value->evaluate(params); } @@ -95,6 +106,10 @@ ParseResult Var::parse(const Convertible& value_, ParsingContext& ctx) { return ParseResult(std::make_unique(name_, std::move(*bindingValue))); } +mbgl::Value Var::serialize() const { + return std::vector{{ getOperator(), name }}; +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 7e79fcbfe6..8a63980dba 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -102,6 +102,14 @@ ParseResult Literal::parse(const Convertible& value, ParsingContext& ctx) { } } +mbgl::Value Literal::serialize() const { + if (getType().is() || getType().is()) { + return std::vector{{ getOperator(), *fromExpressionValue(value) }}; + } else { + return *fromExpressionValue(value); + } +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp index 0b2790b688..3d41f0bdd3 100644 --- a/src/mbgl/style/expression/match.cpp +++ b/src/mbgl/style/expression/match.cpp @@ -40,6 +40,42 @@ std::vector> Match::possibleOutputs() const { return result; } +template +mbgl::Value Match::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + serialized.emplace_back(input->serialize()); + + // Sort so serialization has an arbitrary defined order, even though branch order doesn't affect evaluation + std::map> sortedBranches(branches.begin(), branches.end()); + + // Group branches by unique match expression to support condensed serializations + // of the form [case1, case2, ...] -> matchExpression + std::map outputLookup; + std::vector>> groupedByOutput; + for (auto& entry : sortedBranches) { + auto outputIndex = outputLookup.find(entry.second.get()); + if (outputIndex == outputLookup.end()) { + // First time seeing this output, add it to the end of the grouped list + outputLookup[entry.second.get()] = groupedByOutput.size(); + groupedByOutput.emplace_back(entry.second.get(), std::vector{{entry.first}}); + } else { + // We've seen this expression before, add the label to that output's group + groupedByOutput[outputIndex->second].second.emplace_back(entry.first); + } + }; + + for (auto& entry : groupedByOutput) { + entry.second.size() == 1 + ? serialized.emplace_back(entry.second[0]) // Only a single label matches this output expression + : serialized.emplace_back(entry.second); // Array of literal labels pointing to this output expression + serialized.emplace_back(entry.first->serialize()); // The output expression itself + } + + serialized.emplace_back(otherwise->serialize()); + return serialized; +} + template<> EvaluationResult Match::evaluate(const EvaluationContext& params) const { const EvaluationResult inputValue = input->evaluate(params); diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp index 34537d48ae..ddaf9417cb 100644 --- a/src/mbgl/style/expression/step.cpp +++ b/src/mbgl/style/expression/step.cpp @@ -168,6 +168,18 @@ ParseResult Step::parse(const mbgl::style::conversion::Convertible& value, Parsi return ParseResult(std::make_unique(*outputType, std::move(*input), std::move(stops))); } +mbgl::Value Step::serialize() const { + std::vector serialized; + serialized.emplace_back(getOperator()); + serialized.emplace_back(input->serialize()); + for (auto& entry : stops) { + if (entry.first > -std::numeric_limits::infinity()) { + serialized.emplace_back(entry.first); + } + serialized.emplace_back(entry.second->serialize()); + } + return serialized; +} } // namespace expression } // namespace style diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp index faa44e78aa..72779d4956 100644 --- a/src/mbgl/style/expression/value.cpp +++ b/src/mbgl/style/expression/value.cpp @@ -103,6 +103,37 @@ Value ValueConverter::toExpressionValue(const mbgl::Value& value) { return mbgl::Value::visit(value, FromMBGLValue()); } +mbgl::Value ValueConverter::fromExpressionValue(const Value& value) { + return value.match( + [&](const Color& color)->mbgl::Value { + return std::vector{ + std::string("rgba"), + double(255 * color.r / color.a), + double(255 * color.g / color.a), + double(255 * color.b / color.a), + double(color.a) + }; + }, + [&](const std::vector& values)->mbgl::Value { + std::vector converted; + converted.reserve(values.size()); + for (const Value& v : values) { + converted.emplace_back(fromExpressionValue(v)); + } + return converted; + }, + [&](const std::unordered_map& values)->mbgl::Value { + std::unordered_map converted; + converted.reserve(values.size()); + for(const auto& entry : values) { + converted.emplace(entry.first, fromExpressionValue(entry.second)); + } + return converted; + }, + [&](const auto& a)->mbgl::Value { return a; } + ); +} + Value ValueConverter::toExpressionValue(const float value) { return static_cast(value); } @@ -237,7 +268,7 @@ template <> type::Type valueTypeToExpressionType() { return typ template Value toExpressionValue(const mbgl::Value&); - +template optional fromExpressionValue(const Value&); // for to_rgba expression template type::Type valueTypeToExpressionType>(); -- cgit v1.2.1 From e771210d13ab7fe1cf3816b1eeaee18a7bed85bd Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 9 Feb 2018 17:21:58 -0800 Subject: [test] Native expression test support for: - Round-tripping expressions through serialization and checking that outputs don't change - Checking expression serialization against expected value from fixture --- mapbox-gl-js | 2 +- platform/node/test/expression.test.js | 60 +++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index d61850a6bd..8a19f60799 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit d61850a6bd84ce6d697be392c7ef6d4b5555c20e +Subproject commit 8a19f6079933817fd73eed71159130b8ac53a00f diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js index d7a44abbaa..ffd1c68ff2 100644 --- a/platform/node/test/expression.test.js +++ b/platform/node/test/expression.test.js @@ -32,41 +32,53 @@ function getExpectedType(spec) { suite.run('native', {ignores: ignores, tests: tests}, (fixture) => { const compiled = {}; + const recompiled = {}; const result = { - compiled + compiled, + recompiled }; const spec = fixture.propertySpec || {}; const expression = mbgl.Expression.parse(fixture.expression, getExpectedType(spec)); - if (expression instanceof mbgl.Expression) { - compiled.result = 'success'; - compiled.isFeatureConstant = expression.isFeatureConstant(); - compiled.isZoomConstant = expression.isZoomConstant(); - compiled.type = expression.getType(); + const evaluateExpression = (expression, compilationResult) => { + if (expression instanceof mbgl.Expression) { + compilationResult.result = 'success'; + compilationResult.isFeatureConstant = expression.isFeatureConstant(); + compilationResult.isZoomConstant = expression.isZoomConstant(); + compilationResult.type = expression.getType(); - console.log("input: " + JSON.stringify(fixture.expression)); - console.log("output: " + JSON.stringify(expression.serialize())); + const evaluate = fixture.inputs || []; + const evaluateResults = []; + for (const input of evaluate) { + const feature = Object.assign({ + type: 'Feature', + properties: {}, + geometry: { type: 'Point', coordinates: [0, 0] } + }, input[1]) - const evaluate = fixture.inputs || []; - const evaluateResults = []; - for (const input of evaluate) { - const feature = Object.assign({ - type: 'Feature', - properties: {}, - geometry: { type: 'Point', coordinates: [0, 0] } - }, input[1]) + const output = expression.evaluate(input[0], feature); + evaluateResults.push(output); + } - const output = expression.evaluate(input[0], feature); - evaluateResults.push(output); + if (fixture.inputs) { + return evaluateResults; + } + } else { + compilationResult.result = 'error'; + compilationResult.errors = expression; } + } - if (fixture.inputs) { - result.outputs = evaluateResults; - } - } else { - compiled.result = 'error'; - compiled.errors = expression; + result.outputs = evaluateExpression(expression, compiled); + if (expression instanceof mbgl.Expression) { + result.serialized = expression.serialize(); + const recompiledExpression = mbgl.Expression.parse(result.serialized, getExpectedType(spec)); + result.roundTripOutputs = evaluateExpression(recompiledExpression, recompiled); + // Type is allowed to change through serialization + // (eg "array" -> "array") + // Override the round-tripped type here so that the equality check passes + recompiled.type = compiled.type; } return result; -- cgit v1.2.1 From d3e6fc5eff6b50490cf7e10c87a09c5eff7cda35 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 16 Feb 2018 13:14:41 -0800 Subject: [test] Native ignore for GL JS issue #6160 --- platform/node/test/ignores.json | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 42751da30f..50e2aa1d61 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -34,6 +34,7 @@ "render-tests/regressions/mapbox-gl-js#5599": "https://github.com/mapbox/mapbox-gl-native/issues/10399", "render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619", + "render-tests/regressions/mapbox-gl-js#6160": "https://github.com/mapbox/mapbox-gl-native/pull/11206", "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357", "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847", "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", -- cgit v1.2.1 From 9989e71e3fe436d1a46e0f023adf0218fe777f83 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:01:03 -0500 Subject: [ios] Fix loop when first asking for location permission (#11229) --- platform/ios/CHANGELOG.md | 6 +++++- platform/ios/src/MGLMapView.mm | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 19d6d36ace..3bfbbd4a18 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 3.7.4 +## 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)) * Fixed an issue that caused `-[MGLMapSnapshotter pointForCoordinate:]` to return the wrong point. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035)) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index f2141c3840..02f529d094 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -539,8 +539,9 @@ public: [[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]; -- cgit v1.2.1 From 9a30e252b53b654d9b756f839bd33f2ff6db1d91 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 16 Feb 2018 20:06:32 -0500 Subject: [ios] Bump podspecs for 3.7.5 --- platform/ios/CHANGELOG.md | 2 +- platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 3bfbbd4a18..7d23a3fbaf 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 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)) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 65adcf8d3c..bf2f854b50 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.4' + version = '3.7.5' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index fff485ebb4..e0c787e5da 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.4' + version = '3.7.5' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 3181b66918..ea7ce47a8b 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.4' + version = '3.7.5' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From d0f66b132f263fda9c0ca40053253fae20cb06ec Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Mon, 19 Feb 2018 12:08:35 -0500 Subject: [android] added missing delete local references --- .../android/src/geojson/feature_collection.cpp | 25 ++++++++++++--------- platform/android/src/geojson/line_string.cpp | 3 ++- platform/android/src/geojson/multi_polygon.cpp | 2 +- platform/android/src/geojson/point.cpp | 26 ++++++++++++++-------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp index 59f1e317e6..06f8f10b9a 100644 --- a/platform/android/src/geojson/feature_collection.cpp +++ b/platform/android/src/geojson/feature_collection.cpp @@ -1,3 +1,4 @@ +#include #include "feature_collection.hpp" #include "feature.hpp" @@ -7,19 +8,23 @@ namespace android { namespace geojson { mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object jCollection) { - auto jFeatureList = FeatureCollection::features(env, jCollection); - auto jFeatures = java::util::List::toArray(env, jFeatureList); - auto size = size_t(jFeatures.Length(env)); - auto collection = mbgl::FeatureCollection(); - collection.reserve(size); - for (size_t i = 0; i < size; i++) { - auto jFeature = jFeatures.Get(env, i); - collection.push_back(Feature::convert(env, jFeature)); - jni::DeleteLocalRef(env, jFeature); - } + if (jCollection) { + auto jFeatureList = FeatureCollection::features(env, jCollection); + auto jFeatures = java::util::List::toArray(env, jFeatureList); + auto size = size_t(jFeatures.Length(env)); + collection.reserve(size); + for (size_t i = 0; i < size; i++) { + auto jFeature = jFeatures.Get(env, i); + collection.push_back(Feature::convert(env, jFeature)); + jni::DeleteLocalRef(env, jFeature); + } + + jni::DeleteLocalRef(env, jFeatures); + jni::DeleteLocalRef(env, jFeatureList); + } return collection; } diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp index 9e99c72c4c..8eebd53550 100644 --- a/platform/android/src/geojson/line_string.cpp +++ b/platform/android/src/geojson/line_string.cpp @@ -23,8 +23,9 @@ mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object(env, jPointList); - auto size = jPointArray.Length(env); + lineString.reserve(size); + for (std::size_t i = 0; i < size; i++) { auto jPoint = jPointArray.Get(env, i); lineString.push_back(Point::convert(env, jPoint)); diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp index f4eb0f6b2a..aadba8c8a6 100644 --- a/platform/android/src/geojson/multi_polygon.cpp +++ b/platform/android/src/geojson/multi_polygon.cpp @@ -22,8 +22,8 @@ mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Obje jni::DeleteLocalRef(env, jPositionListsList); } - jni::DeleteLocalRef(env, jPointListsListList); jni::DeleteLocalRef(env, jPointListsListArray); + jni::DeleteLocalRef(env, jPointListsListList); } return multiPolygon; diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index 5feb1b8521..d064547145 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -1,6 +1,8 @@ +#include #include "point.hpp" #include "../java/util.hpp" #include "../java_types.hpp" +#include "../style/value.hpp" namespace mbgl { namespace android { @@ -19,17 +21,23 @@ mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object jPoin } mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object*/> jDoubleList) { - auto jDoubleArray = java::util::List::toArray(env, jDoubleList); + mapbox::geojson::point point; + + if (jDoubleList) { + auto jDoubleArray = java::util::List::toArray(env, jDoubleList); - jni::jdouble lon = jni::CallMethod(env, - jDoubleArray.Get(env, 0), - *java::Number::doubleValueMethodId); - jni::jdouble lat = jni::CallMethod(env, - jDoubleArray.Get(env, 1), - *java::Number::doubleValueMethodId); - mapbox::geojson::point point(lon, lat); - jni::DeleteLocalRef(env, jDoubleArray); + auto lonObject = jDoubleArray.Get(env, 0); + auto latObject = jDoubleArray.Get(env, 1); + point.x = jni::CallMethod(env, lonObject, + *java::Number::doubleValueMethodId); + point.y = jni::CallMethod(env, latObject, + *java::Number::doubleValueMethodId); + + jni::DeleteLocalRef(env, lonObject); + jni::DeleteLocalRef(env, latObject); + jni::DeleteLocalRef(env, jDoubleArray); + } return point; } -- cgit v1.2.1 From 2cef23e3ff532faf3ddff10c26e1495b30f11c4a Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Fri, 16 Feb 2018 12:38:23 -0800 Subject: [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 c4372e7112..8f83a0f982 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 f630a49078..8554cfb65e 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 dc8ae28705..c4c37c74d7 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); -- cgit v1.2.1 From 17e5ae84af11006ed8336cd19eff57f65a6a577a Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 19 Feb 2018 19:08:07 +0100 Subject: [android] - check if hosting Activity isn't finishing before showing an dialog --- .../com/mapbox/mapboxsdk/maps/AttributionDialogManager.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index 2bcbd5ce40..5ccd6bd795 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.maps; +import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; @@ -48,7 +49,17 @@ public class AttributionDialogManager implements View.OnClickListener, DialogInt @Override public void onClick(View view) { attributionSet = new AttributionBuilder(mapboxMap).build(); - showAttributionDialog(getAttributionTitles()); + + boolean isActivityFinishing = false; + if (context instanceof Activity) { + isActivityFinishing = ((Activity) context).isFinishing(); + } + + // check is hosting activity isn't finishing + // https://github.com/mapbox/mapbox-gl-native/issues/11238 + if (!isActivityFinishing) { + showAttributionDialog(getAttributionTitles()); + } } protected void showAttributionDialog(String[] attributionTitles) { -- cgit v1.2.1 From 06213d9145d3b20b63e235cc25678fd76dc296d0 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 17 Jan 2018 14:51:05 +0100 Subject: [android] - add instrumentation tests for FileSource activation/deactivation --- .../com/mapbox/mapboxsdk/storage/FileSource.java | 5 + .../android/MapboxGLAndroidSDKTestApp/build.gradle | 1 + .../com/mapbox/mapboxsdk/maps/OrientationTest.java | 12 +- .../mapboxsdk/testapp/action/WaitAction.java | 39 +++++++ .../testapp/activity/BaseActivityTest.java | 41 ++----- .../testapp/maps/widgets/CompassViewTest.java | 2 +- .../mapboxsdk/testapp/storage/FileSourceTest.java | 129 +++++++++++++++++++++ platform/android/gradle/dependencies.gradle | 1 + platform/android/src/file_source.cpp | 10 +- platform/android/src/file_source.hpp | 2 + 10 files changed, 202 insertions(+), 40 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java index f0cb8d973a..929e4b4279 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java @@ -6,6 +6,8 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.os.Environment; import android.support.annotation.NonNull; +import android.support.annotation.UiThread; + import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; import timber.log.Timber; @@ -43,6 +45,7 @@ public class FileSource { * @param context the context to derive the cache path from * @return the single instance of FileSource */ + @UiThread public static synchronized FileSource getInstance(Context context) { if (INSTANCE == null) { String cachePath = getCachePath(context); @@ -122,6 +125,8 @@ public class FileSource { initialize(Mapbox.getAccessToken(), cachePath, assetManager); } + public native boolean isActivated(); + public native void activate(); public native void deactivate(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index caff70e543..6707527bf2 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -72,6 +72,7 @@ dependencies { androidTestImplementation dependenciesList.testRules androidTestImplementation dependenciesList.testEspressoCore androidTestImplementation dependenciesList.testEspressoIntents + androidTestImplementation dependenciesList.testEspressoContrib } apply from: "${rootDir}/gradle/gradle-make.gradle" diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java index 7a1fcbf5f3..89397c30eb 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/OrientationTest.java @@ -16,17 +16,17 @@ public class OrientationTest extends BaseActivityTest { @Test public void testChangeDeviceOrientation() { onView(isRoot()).perform(orientationLandscape()); - waitLoop(2200); + waitAction(2200); onView(isRoot()).perform(orientationPortrait()); - waitLoop(2500); + waitAction(2500); onView(isRoot()).perform(orientationLandscapeReverse()); - waitLoop(500); + waitAction(500); onView(isRoot()).perform(orientationPortraitReverse()); - waitLoop(1250); + waitAction(1250); onView(isRoot()).perform(orientationLandscape()); - waitLoop(750); + waitAction(750); onView(isRoot()).perform(orientationPortrait()); - waitLoop(950); + waitAction(950); onView(isRoot()).perform(orientationLandscapeReverse()); onView(isRoot()).perform(orientationPortraitReverse()); onView(isRoot()).perform(orientationLandscape()); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java new file mode 100644 index 0000000000..26a3a2e4ab --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java @@ -0,0 +1,39 @@ +package com.mapbox.mapboxsdk.testapp.action; + +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.view.View; + +import org.hamcrest.Matcher; + +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; + +public final class WaitAction implements ViewAction { + + private static final long DEFAULT_LOOP_TIME = 375; + private final long loopTime; + + public WaitAction() { + this(DEFAULT_LOOP_TIME); + } + + public WaitAction(long loopTime) { + this.loopTime = loopTime; + } + + @Override + public Matcher getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + uiController.loopMainThreadForAtLeast(loopTime); + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java index 3f32443021..6d90c20a46 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java @@ -6,18 +6,19 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.test.espresso.Espresso; import android.support.test.espresso.IdlingResourceTimeoutException; -import android.support.test.espresso.UiController; -import android.support.test.espresso.ViewAction; import android.support.test.rule.ActivityTestRule; -import android.view.View; + import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; import com.mapbox.mapboxsdk.testapp.utils.OnMapReadyIdlingResource; + import junit.framework.Assert; -import org.hamcrest.Matcher; + import org.junit.After; import org.junit.Before; import org.junit.Rule; + import timber.log.Timber; import static android.support.test.espresso.Espresso.onView; @@ -67,12 +68,12 @@ public abstract class BaseActivityTest { onView(withId(id)).check(matches(isDisplayed())); } - protected void waitLoop() { - waitLoop(500); + protected void waitAction() { + waitAction(500); } - protected void waitLoop(long waitTime) { - onView(withId(R.id.mapView)).perform(new LoopAction(waitTime)); + protected void waitAction(long waitTime) { + onView(withId(R.id.mapView)).perform(new WaitAction(waitTime)); } static boolean isConnected(Context context) { @@ -87,29 +88,5 @@ public abstract class BaseActivityTest { Timber.e("@After test: unregister idle resource"); Espresso.unregisterIdlingResources(idlingResource); } - - private class LoopAction implements ViewAction { - - private long loopTime; - - public LoopAction(long loopTime) { - this.loopTime = loopTime; - } - - @Override - public Matcher getConstraints() { - return isDisplayed(); - } - - @Override - public String getDescription() { - return getClass().getSimpleName(); - } - - @Override - public void perform(UiController uiController, View view) { - uiController.loopMainThreadForAtLeast(loopTime); - } - } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java index 2a510b4dc5..26aee2de98 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java @@ -62,7 +62,7 @@ public class CompassViewTest extends BaseActivityTest { .build() ))); onView(withId(R.id.compassView)).perform(click()); - waitLoop(); + waitAction(); onView(withId(R.id.compassView)).check(matches(not(isDisplayed()))); invoke(mapboxMap, (uiController, mapboxMap) -> { CameraPosition cameraPosition = mapboxMap.getCameraPosition(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java new file mode 100644 index 0000000000..554bc988a6 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java @@ -0,0 +1,129 @@ +package com.mapbox.mapboxsdk.testapp.storage; + +import android.os.Looper; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; + +import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.WaitAction; +import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.Espresso.pressBack; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.isRoot; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class FileSourceTest { + + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FeatureOverviewActivity.class); + + private FileSource fileSource; + + @Before + public void setUp() throws Exception { + onView(withId(R.id.recyclerView)).perform(new FileSourceCreator()); + } + + @Test + public void testDefault() throws Exception { + assertFalse("FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testActivateDeactivate() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(true)); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(false)); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testOpenCloseMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + @Test + public void testRotateMapView() throws Exception { + assertFalse("1) FileSource should not be active", fileSource.isActivated()); + onView(withText("Simple Map")).perform(click()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + onView(isRoot()).perform(orientationLandscape()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + assertTrue("2) FileSource should be active", fileSource.isActivated()); + onView(withId(R.id.mapView)).perform(new WaitAction()); + pressBack(); + assertFalse("3) FileSource should not be active", fileSource.isActivated()); + } + + private class FileSourceCreator implements ViewAction { + @Override + public Matcher getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + fileSource = FileSource.getInstance(rule.getActivity()); + } + } + + private class FileSourceActivator implements ViewAction { + + private boolean activate; + + FileSourceActivator(boolean activate) { + this.activate = activate; + } + + @Override + public Matcher getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return "Creates the filesource instance on the UI thread"; + } + + @Override + public void perform(UiController uiController, View view) { + assertTrue(Looper.myLooper() == Looper.getMainLooper()); + if (activate) { + fileSource.activate(); + } else { + fileSource.deactivate(); + } + } + } +} \ No newline at end of file diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 4ef4ae2f7d..b1b9a065ad 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -38,6 +38,7 @@ ext { testRules : "com.android.support.test:rules:${versions.testRunner}", testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}", testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", + testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${versions.espresso}", supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}", supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}", diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 6a9d7badb0..728935c03f 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -81,6 +81,13 @@ void FileSource::pause(jni::JNIEnv&) { } } +jni::jboolean FileSource::isResumed(jni::JNIEnv&) { + if (activationCounter) { + return (jboolean) (activationCounter > 0); + } + return (jboolean) false; +} + jni::Class FileSource::javaClass; FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object jFileSource) { @@ -112,7 +119,8 @@ void FileSource::registerNative(jni::JNIEnv& env) { METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"), METHOD(&FileSource::setResourceTransform, "setResourceTransform"), METHOD(&FileSource::resume, "activate"), - METHOD(&FileSource::pause, "deactivate") + METHOD(&FileSource::pause, "deactivate"), + METHOD(&FileSource::isResumed, "isActivated") ); } diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp index 194f784622..e4295e1b84 100644 --- a/platform/android/src/file_source.hpp +++ b/platform/android/src/file_source.hpp @@ -45,6 +45,8 @@ public: void pause(jni::JNIEnv&); + jni::jboolean isResumed(jni::JNIEnv&); + static jni::Class javaClass; static FileSource* getNativePeer(jni::JNIEnv&, jni::Object); -- cgit v1.2.1 From e5810536bcedfbb83bc2b5aa48fcb7d2ada05e37 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 20 Feb 2018 15:22:44 +0100 Subject: [android] - decouple map padding from overlain views --- .../src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 3fd3e1220a..44f106bdd5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -950,12 +950,7 @@ public final class UiSettings { initMargins[3] = bottom; // convert initial margins with padding - int[] contentPadding = projection.getContentPadding(); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams(); - left += contentPadding[0]; - top += contentPadding[1]; - right += contentPadding[2]; - bottom += contentPadding[3]; layoutParams.setMargins(left, top, right, bottom); // support RTL -- cgit v1.2.1 From c6537adbd1704b81e3182b14ee468e682060fd64 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 20 Feb 2018 16:40:32 +0100 Subject: [android] - don't disable zoom button controller zooming whem zooming gestures are disabled --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index a8e065c45e..3c5b82c3b0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -22,6 +22,7 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; + import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -36,10 +37,7 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.services.android.telemetry.MapboxTelemetry; -import timber.log.Timber; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -48,6 +46,11 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import timber.log.Timber; + import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -1004,10 +1007,8 @@ public class MapView extends FrameLayout { // Called when user pushes a zoom button on the ZoomButtonController @Override public void onZoom(boolean zoomIn) { - if (uiSettings.isZoomGesturesEnabled()) { - cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_ANIMATION); - onZoom(zoomIn, mapGestureDetector.getFocalPoint()); - } + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_ANIMATION); + onZoom(zoomIn, mapGestureDetector.getFocalPoint()); } private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) { -- cgit v1.2.1 From c2d9d6a4235d5bce5c10b40b3f8cfaeb331d7878 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Wed, 21 Feb 2018 11:01:56 -0500 Subject: [android] jni clean up - missing a couple DeleteLocalRef --- platform/android/src/conversion/collection.hpp | 1 + platform/android/src/file_source.cpp | 5 ++++- platform/android/src/geojson/feature_collection.cpp | 1 - platform/android/src/geojson/point.cpp | 1 - platform/android/src/map/camera_position.cpp | 4 +++- platform/android/src/map/image.cpp | 4 +++- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp index 549121c7ef..2b953e73f4 100644 --- a/platform/android/src/conversion/collection.hpp +++ b/platform/android/src/conversion/collection.hpp @@ -28,6 +28,7 @@ inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray& array) { inline std::vector toVector(JNIEnv& env, jni::jarray& array) { std::vector vector; std::size_t len = jni::GetArrayLength(env, array); + vector.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jstring* jstr = reinterpret_cast(jni::GetObjectArrayElement(env, array, i)); diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 728935c03f..42c03b0974 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -132,8 +132,11 @@ jni::Class FileSource::ResourceTransformC std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::Object callback, int kind, std::string url_) { static auto method = FileSource::ResourceTransformCallback::javaClass.GetMethod(env, "onURL"); auto url = jni::Make(env, url_); + url = callback.Call(env, method, kind, url); - return jni::Make(env, url); + auto urlStr = jni::Make(env, url); + jni::DeleteLocalRef(env, url); + return urlStr; } } // namespace android diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp index 06f8f10b9a..18a41d48fa 100644 --- a/platform/android/src/geojson/feature_collection.cpp +++ b/platform/android/src/geojson/feature_collection.cpp @@ -1,4 +1,3 @@ -#include #include "feature_collection.hpp" #include "feature.hpp" diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp index d064547145..e95376cd2e 100644 --- a/platform/android/src/geojson/point.cpp +++ b/platform/android/src/geojson/point.cpp @@ -1,4 +1,3 @@ -#include #include "point.hpp" #include "../java/util.hpp" #include "../java_types.hpp" diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index 1fc5f9789f..01ffc6530b 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -33,7 +33,9 @@ mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Obje static auto tilt = CameraPosition::javaClass.GetField(env, "tilt"); static auto zoom = CameraPosition::javaClass.GetField(env, "zoom"); - auto center = LatLng::getLatLng(env, position.Get(env, target)); + auto jtarget = position.Get(env, target); + auto center = LatLng::getLatLng(env, jtarget); + jni::DeleteLocalRef(env, jtarget); return mbgl::CameraOptions { center, diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp index 5f5c90eddd..52e0e0d255 100644 --- a/platform/android/src/map/image.cpp +++ b/platform/android/src/map/image.cpp @@ -16,7 +16,9 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object image) { auto width = image.Get(env, widthField); auto pixelRatio = image.Get(env, pixelRatioField); auto pixels = image.Get(env, bufferField); - auto name = jni::Make(env, image.Get(env, nameField)); + auto jName = image.Get(env, nameField); + auto name = jni::Make(env, jName); + jni::DeleteLocalRef(env, jName); jni::NullCheck(env, &pixels); std::size_t size = pixels.Length(env); -- cgit v1.2.1 From ebb72b9002f9d497e696a08030fcec2f489c273c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 21 Feb 2018 11:36:40 -0500 Subject: [core] don't hide icons if text is an empty string --- platform/node/test/ignores.json | 1 - src/mbgl/text/placement.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 50e2aa1d61..42751da30f 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -34,7 +34,6 @@ "render-tests/regressions/mapbox-gl-js#5599": "https://github.com/mapbox/mapbox-gl-native/issues/10399", "render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619", - "render-tests/regressions/mapbox-gl-js#6160": "https://github.com/mapbox/mapbox-gl-native/pull/11206", "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357", "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847", "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index fc24c78902..54b2b7539b 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -95,9 +95,6 @@ void Placement::placeLayerBucket( auto partiallyEvaluatedTextSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom()); auto partiallyEvaluatedIconSize = bucket.iconSizeBinder->evaluateForZoom(state.getZoom()); - const bool iconWithoutText = !bucket.hasTextData() || bucket.layout.get(); - const bool textWithoutIcon = !bucket.hasIconData() || bucket.layout.get(); - for (auto& symbolInstance : bucket.symbolInstances) { if (seenCrossTileIDs.count(symbolInstance.crossTileID) == 0) { @@ -140,6 +137,9 @@ void Placement::placeLayerBucket( offscreen &= placed.second; } + const bool iconWithoutText = !symbolInstance.hasText || bucket.layout.get(); + const bool textWithoutIcon = !symbolInstance.hasIcon || bucket.layout.get(); + // combine placements for icon and text if (!iconWithoutText && !textWithoutIcon) { placeText = placeIcon = placeText && placeIcon; -- cgit v1.2.1 From fcf5fa6bbb6600c9c00d019b89c6d8c9da0960f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Thu, 22 Feb 2018 10:01:48 +0100 Subject: [android] expose ImageSource coordinates setter (#11262) --- .../java/com/mapbox/mapboxsdk/style/sources/ImageSource.java | 11 +++++++++++ platform/android/src/style/sources/image_source.cpp | 8 +++++++- platform/android/src/style/sources/image_source.hpp | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java index 84e5e96fa4..b7679b5a16 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java @@ -124,6 +124,15 @@ public class ImageSource extends Source { return nativeGetUrl(); } + /** + * Updates the latitude and longitude of the four corners of the image + * + * @param latLngQuad latitude and longitude of the four corners of the image + */ + public void setCoordinates(LatLngQuad latLngQuad) { + nativeSetCoordinates(latLngQuad); + } + protected native void initialize(String layerId, LatLngQuad payload); protected native void nativeSetUrl(String url); @@ -132,6 +141,8 @@ public class ImageSource extends Source { protected native void nativeSetImage(Bitmap bitmap); + protected native void nativeSetCoordinates(LatLngQuad latLngQuad); + @Override protected native void finalize() throws Throwable; } diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp index 0cd6995969..249387ea51 100644 --- a/platform/android/src/style/sources/image_source.cpp +++ b/platform/android/src/style/sources/image_source.cpp @@ -45,6 +45,11 @@ namespace android { source.as()->setImage(Bitmap::GetImage(env, bitmap)); } + void ImageSource::setCoordinates(jni::JNIEnv& env, jni::Object coordinatesObject) { + source.as()->setCoordinates( + LatLngQuad::getLatLngArray(env, coordinatesObject)); + } + jni::Class ImageSource::javaClass; jni::Object ImageSource::createJavaPeer(jni::JNIEnv& env) { @@ -66,7 +71,8 @@ namespace android { "finalize", METHOD(&ImageSource::setURL, "nativeSetUrl"), METHOD(&ImageSource::getURL, "nativeGetUrl"), - METHOD(&ImageSource::setImage, "nativeSetImage") + METHOD(&ImageSource::setImage, "nativeSetImage"), + METHOD(&ImageSource::setCoordinates, "nativeSetCoordinates") ); } diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp index f0af28d357..6021a03dc3 100644 --- a/platform/android/src/style/sources/image_source.hpp +++ b/platform/android/src/style/sources/image_source.hpp @@ -30,6 +30,8 @@ public: void setImage(jni::JNIEnv&, jni::Object); + void setCoordinates(jni::JNIEnv&, jni::Object); + private: jni::Object createJavaPeer(jni::JNIEnv&); -- cgit v1.2.1 From dfb9b26e675a152a925fcc5b84c3e14b8b9779d2 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 20 Dec 2017 16:44:21 +0100 Subject: [android] - port animated markers activity to symbol layer --- .../src/main/AndroidManifest.xml | 8 +- .../annotation/AnimatedMarkerActivity.java | 283 ------------- .../annotation/AnimatedSymbolLayerActivity.java | 449 +++++++++++++++++++++ .../main/res/layout/activity_animated_marker.xml | 6 +- .../src/main/res/values/descriptions.xml | 2 +- .../src/main/res/values/titles.xml | 2 +- platform/android/scripts/exclude-activity-gen.json | 2 +- 7 files changed, 459 insertions(+), 293 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedSymbolLayerActivity.java diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 5a0493e5bd..9d7e21024c 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -69,12 +69,12 @@ android:value=".activity.FeatureOverviewActivity"/> + android:name=".activity.annotation.AnimatedSymbolLayerActivity" + android:description="@string/description_animated_symbollayer" + android:label="@string/activity_animated_symbollayer"> + android:value="@string/category_style"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java deleted file mode 100644 index e6db071141..0000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.mapbox.mapboxsdk.testapp.activity.annotation; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.os.Bundle; -import android.support.annotation.DrawableRes; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; - -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewManager; -import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.testapp.R; -import com.mapbox.mapboxsdk.testapp.utils.IconUtils; -import com.mapbox.turf.TurfMeasurement; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Test activity showcasing animating MarkerViews. - */ -public class AnimatedMarkerActivity extends AppCompatActivity { - - private MapView mapView; - private MapboxMap mapboxMap; - - private LatLng dupontCircle = new LatLng(38.90962, -77.04341); - - private Marker passengerMarker = null; - private MarkerView carMarker = null; - - private Runnable animationRunnable; - - private List markerViews = new ArrayList<>(); - private boolean stopped; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_animated_marker); - - mapView = (MapView) findViewById(R.id.mapView); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(mapboxMap -> { - AnimatedMarkerActivity.this.mapboxMap = mapboxMap; - setupMap(); - - animationRunnable = () -> { - for (int i = 0; i < 10; i++) { - addRandomCar(); - } - addPassenger(); - addMainCar(); - }; - mapView.post(animationRunnable); - }); - } - - private void setupMap() { - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(dupontCircle) - .zoom(15) - .build(); - mapboxMap.setCameraPosition(cameraPosition); - } - - private void addPassenger() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (passengerMarker == null) { - Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_directions_run_black, - ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())); - passengerMarker = mapboxMap.addMarker(new MarkerViewOptions() - .position(randomLatLng) - .icon(icon)); - } else { - passengerMarker.setPosition(randomLatLng); - } - } - - private void addMainCar() { - if (isActivityStopped()) { - return; - } - - LatLng randomLatLng = getLatLngInBounds(); - - if (carMarker == null) { - carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top, - markerView -> { - // Make sure the car marker is selected so that it's always brought to the front (#5285) - mapboxMap.selectMarker(carMarker); - animateMoveToPassenger(carMarker); - }); - markerViews.add(carMarker); - } else { - carMarker.setPosition(randomLatLng); - } - } - - private void animateMoveToPassenger(final MarkerView car) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition()); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - addPassenger(); - animateMoveToPassenger(car); - } - }); - } - - protected void addRandomCar() { - markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top, - markerView -> randomlyMoveMarker(markerView))); - } - - private void randomlyMoveMarker(final MarkerView marker) { - if (isActivityStopped()) { - return; - } - - ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds()); - - // Add listener to restart animation on end - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - randomlyMoveMarker(marker); - } - }); - } - - private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) { - marker.setRotation((float) getBearing(marker.getPosition(), to)); - - final ValueAnimator markerAnimator = ObjectAnimator.ofObject( - marker, "position", new LatLngEvaluator(), marker.getPosition(), to); - markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to))); - markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); - - // Start - markerAnimator.start(); - - return markerAnimator; - } - - private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource, - MarkerViewManager.OnMarkerViewAddedListener listener) { - Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this) - .fromResource(carResource); - - // View Markers - return mapboxMap.addMarker(new MarkerViewOptions() - .position(start) - .icon(icon), listener); - - // GL Markers -// return mapboxMap.addMarker(new MarkerOptions() -// .position(start) -// .icon(icon)); - - } - - private LatLng getLatLngInBounds() { - LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; - Random generator = new Random(); - double randomLat = bounds.getLatSouth() + generator.nextDouble() - * (bounds.getLatNorth() - bounds.getLatSouth()); - double randomLon = bounds.getLonWest() + generator.nextDouble() - * (bounds.getLonEast() - bounds.getLonWest()); - return new LatLng(randomLat, randomLon); - } - - @Override - protected void onStart() { - super.onStart(); - mapView.onStart(); - } - - @Override - protected void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - protected void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onStop() { - super.onStop(); - - stopped = true; - - // Stop ongoing animations, prevent memory leaks - if (mapboxMap != null) { - MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager(); - for (MarkerView markerView : markerViews) { - View view = markerViewManager.getView(markerView); - if (view != null) { - view.animate().cancel(); - } - } - } - - // onStop - mapView.onStop(); - mapView.removeCallbacks(animationRunnable); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } - - /** - * Evaluator for LatLng pairs - */ - private static class LatLngEvaluator implements TypeEvaluator { - - private LatLng latLng = new LatLng(); - - @Override - public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { - latLng.setLatitude(startValue.getLatitude() - + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); - latLng.setLongitude(startValue.getLongitude() - + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); - return latLng; - } - } - - private double getBearing(LatLng from, LatLng to) { - return TurfMeasurement.bearing( - Point.fromLngLat(from.getLongitude(), from.getLatitude()), - Point.fromLngLat(to.getLongitude(), to.getLatitude()) - ); - } - - private boolean isActivityStopped() { - return stopped; - } -} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedSymbolLayerActivity.java new file mode 100644 index 0000000000..97957720fc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedSymbolLayerActivity.java @@ -0,0 +1,449 @@ +package com.mapbox.mapboxsdk.testapp.activity.annotation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.functions.stops.Stops; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.turf.TurfMeasurement; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static com.mapbox.mapboxsdk.style.functions.Function.property; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate; + +/** + * Test activity showcasing animating MarkerViews. + */ +public class AnimatedSymbolLayerActivity extends AppCompatActivity { + + private static final String PASSENGER = "passenger"; + private static final String PASSENGER_LAYER = "passenger-layer"; + private static final String PASSENGER_SOURCE = "passenger-source"; + private static final String TAXI = "taxi"; + private static final String TAXI_LAYER = "taxi-layer"; + private static final String TAXI_SOURCE = "taxi-source"; + private static final String RANDOM_CAR_LAYER = "random-car-layer"; + private static final String RANDOM_CAR_SOURCE = "random-car-source"; + private static final String RANDOM_CAR_IMAGE_ID = "random-car"; + private static final String PROPERTY_BEARING = "bearing"; + private static final String WATERWAY_LAYER_ID = "waterway-label"; + private static final int DURATION_RANDOM_MAX = 1500; + private static final int DURATION_BASE = 3000; + + private final Random random = new Random(); + + private MapView mapView; + private MapboxMap mapboxMap; + + private List randomCars = new ArrayList<>(); + private GeoJsonSource randomCarSource; + private Car taxi; + private GeoJsonSource taxiSource; + private LatLng passenger; + + private List animators = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_animated_marker); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + AnimatedSymbolLayerActivity.this.mapboxMap = mapboxMap; + setupCars(); + animateRandomRoutes(); + animateTaxi(); + }); + } + + private void setupCars() { + addRandomCars(); + addPassenger(); + addMainCar(); + } + + private void animateRandomRoutes() { + final Car longestDrive = getLongestDrive(); + final Random random = new Random(); + for (final Car car : randomCars) { + final boolean isLongestDrive = longestDrive.equals(car); + ValueAnimator valueAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), car.current, car.next); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + private LatLng latLng; + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + latLng = (LatLng) animation.getAnimatedValue(); + car.current = latLng; + if (isLongestDrive) { + updateRandomCarSource(); + } + } + }); + + if (isLongestDrive) { + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + updateRandomDestinations(); + animateRandomRoutes(); + } + }); + } + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + car.feature.properties().addProperty("bearing", Car.getBearing(car.current, car.next)); + } + }); + + int offset = random.nextInt(2) == 0 ? 0 : random.nextInt(1000) + 250; + valueAnimator.setStartDelay(offset); + valueAnimator.setDuration(car.duration - offset); + valueAnimator.setInterpolator(new LinearInterpolator()); + valueAnimator.start(); + + animators.add(valueAnimator); + } + } + + private void animateTaxi() { + ValueAnimator valueAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), taxi.current, taxi.next); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + private LatLng latLng; + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + latLng = (LatLng) animation.getAnimatedValue(); + taxi.current = latLng; + updateTaxiSource(); + } + }); + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + updatePassenger(); + animateTaxi(); + } + }); + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + taxi.feature.properties().addProperty("bearing", Car.getBearing(taxi.current, taxi.next)); + } + }); + + valueAnimator.setDuration((long) (7 * taxi.current.distanceTo(taxi.next))); + valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + valueAnimator.start(); + + animators.add(valueAnimator); + } + + private void updatePassenger() { + passenger = getLatLngInBounds(); + updatePassengerSource(); + taxi.setNext(passenger); + } + + private void updatePassengerSource() { + GeoJsonSource source = mapboxMap.getSourceAs(PASSENGER_SOURCE); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry( + Point.fromLngLat( + passenger.getLongitude(), + passenger.getLatitude() + ) + ) + }); + source.setGeoJson(featureCollection); + } + + private void updateTaxiSource() { + taxi.updateFeature(); + taxiSource.setGeoJson(taxi.feature); + } + + private void updateRandomDestinations() { + for (Car randomCar : randomCars) { + randomCar.setNext(getLatLngInBounds()); + } + } + + private Car getLongestDrive() { + Car longestDrive = null; + for (Car randomCar : randomCars) { + if (longestDrive == null) { + longestDrive = randomCar; + } else if (longestDrive.duration < randomCar.duration) { + longestDrive = randomCar; + } + } + return longestDrive; + } + + private void updateRandomCarSource() { + for (Car randomCarsRoute : randomCars) { + randomCarsRoute.updateFeature(); + } + randomCarSource.setGeoJson(featuresFromRoutes()); + } + + private FeatureCollection featuresFromRoutes() { + List features = new ArrayList<>(); + for (Car randomCarsRoute : randomCars) { + features.add(randomCarsRoute.feature); + } + return FeatureCollection.fromFeatures(features); + } + + private long getDuration() { + return random.nextInt(DURATION_RANDOM_MAX) + DURATION_BASE; + } + + private void addRandomCars() { + LatLng latLng; + LatLng next; + for (int i = 0; i < 10; i++) { + latLng = getLatLngInBounds(); + next = getLatLngInBounds(); + + JsonObject properties = new JsonObject(); + properties.addProperty(PROPERTY_BEARING, Car.getBearing(latLng, next)); + + Feature feature = Feature.fromGeometry( + Point.fromLngLat( + latLng.getLongitude(), + latLng.getLatitude() + ), properties); + + randomCars.add( + new Car(feature, next, getDuration()) + ); + } + + randomCarSource = new GeoJsonSource(RANDOM_CAR_SOURCE, featuresFromRoutes()); + mapboxMap.addSource(randomCarSource); + mapboxMap.addImage(RANDOM_CAR_IMAGE_ID, + ((BitmapDrawable) getResources().getDrawable(R.drawable.ic_car_top)).getBitmap()); + + SymbolLayer symbolLayer = new SymbolLayer(RANDOM_CAR_LAYER, RANDOM_CAR_SOURCE); + symbolLayer.withProperties( + iconImage(RANDOM_CAR_IMAGE_ID), + iconAllowOverlap(true), + iconRotate( + property( + PROPERTY_BEARING, + Stops.identity() + ) + ), + iconIgnorePlacement(true) + ); + + mapboxMap.addLayerBelow(symbolLayer, WATERWAY_LAYER_ID); + } + + private void addPassenger() { + passenger = getLatLngInBounds(); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry( + Point.fromLngLat( + passenger.getLongitude(), + passenger.getLatitude() + ) + ) + }); + + mapboxMap.addImage(PASSENGER, + ((BitmapDrawable) getResources().getDrawable(R.drawable.icon_burned)).getBitmap()); + + GeoJsonSource geoJsonSource = new GeoJsonSource(PASSENGER_SOURCE, featureCollection); + mapboxMap.addSource(geoJsonSource); + + SymbolLayer symbolLayer = new SymbolLayer(PASSENGER_LAYER, PASSENGER_SOURCE); + symbolLayer.withProperties( + iconImage(PASSENGER), + iconIgnorePlacement(true), + iconAllowOverlap(true) + ); + mapboxMap.addLayerBelow(symbolLayer, RANDOM_CAR_LAYER); + } + + private void addMainCar() { + LatLng latLng = getLatLngInBounds(); + JsonObject properties = new JsonObject(); + properties.addProperty(PROPERTY_BEARING, Car.getBearing(latLng, passenger)); + Feature feature = Feature.fromGeometry( + Point.fromLngLat( + latLng.getLongitude(), + latLng.getLatitude()), properties); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] {feature}); + + taxi = new Car(feature, passenger, getDuration()); + mapboxMap.addImage(TAXI, + ((BitmapDrawable) getResources().getDrawable(R.drawable.ic_taxi_top)).getBitmap()); + taxiSource = new GeoJsonSource(TAXI_SOURCE, featureCollection); + mapboxMap.addSource(taxiSource); + + SymbolLayer symbolLayer = new SymbolLayer(TAXI_LAYER, TAXI_SOURCE); + symbolLayer.withProperties( + iconImage(TAXI), + iconRotate( + property( + PROPERTY_BEARING, + Stops.identity() + ) + ), + iconAllowOverlap(true), + iconIgnorePlacement(true) + + ); + mapboxMap.addLayer(symbolLayer); + } + + private LatLng getLatLngInBounds() { + LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; + Random generator = new Random(); + double randomLat = bounds.getLatSouth() + generator.nextDouble() + * (bounds.getLatNorth() - bounds.getLatSouth()); + double randomLon = bounds.getLonWest() + generator.nextDouble() + * (bounds.getLonEast() - bounds.getLonWest()); + return new LatLng(randomLat, randomLon); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + for (Animator animator : animators) { + if (animator != null) { + animator.removeAllListeners(); + animator.cancel(); + } + } + + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + /** + * Evaluator for LatLng pairs + */ + private static class LatLngEvaluator implements TypeEvaluator { + + private LatLng latLng = new LatLng(); + + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + latLng.setLatitude(startValue.getLatitude() + + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); + latLng.setLongitude(startValue.getLongitude() + + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); + return latLng; + } + } + + + private static class Car { + private Feature feature; + private LatLng next; + private LatLng current; + private long duration; + + Car(Feature feature, LatLng next, long duration) { + this.feature = feature; + Point point = ((Point) feature.geometry()); + this.current = new LatLng(point.latitude(), point.longitude()); + this.duration = duration; + this.next = next; + } + + void setNext(LatLng next) { + this.next = next; + } + + void updateFeature() { + feature = Feature.fromGeometry(Point.fromLngLat( + current.getLongitude(), + current.getLatitude()) + ); + feature.properties().addProperty("bearing", getBearing(current, next)); + } + + private static float getBearing(LatLng from, LatLng to) { + return (float) TurfMeasurement.bearing( + Point.fromLngLat(from.getLongitude(), from.getLatitude()), + Point.fromLngLat(to.getLongitude(), to.getLatitude()) + ); + } + } +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml index 0566757d58..252af714e7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml @@ -10,9 +10,9 @@ android:id="@id/mapView" android:layout_width="match_parent" android:layout_height="wrap_content" - app:mapbox_cameraTargetLat="51.502615" - app:mapbox_cameraTargetLng="4.972326" - app:mapbox_cameraZoom="6" + app:mapbox_cameraTargetLat="38.90962" + app:mapbox_cameraTargetLng="-77.04341" + app:mapbox_cameraZoom="15" app:mapbox_styleUrl="@string/mapbox_style_light"/> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index d17ec20546..9d44ada937 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -24,7 +24,7 @@ Offline Map example Update metadata example Delete region example - Animate the position change of a marker + Animate the position change of a symbol layer Add a polyline to a map Add a polygon to a map Scroll with pixels in x,y direction diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 0323d7c428..352d7884a6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -4,7 +4,7 @@ Map Fragment Multiple Maps on Screen Add Markers In Bulk - Animated Markers + Animated SymbolLayer Dynamic Marker Polyline Polygon diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json index c1a6b5bb48..b2278c9d63 100644 --- a/platform/android/scripts/exclude-activity-gen.json +++ b/platform/android/scripts/exclude-activity-gen.json @@ -18,7 +18,7 @@ "LocationPickerActivity", "GeoJsonClusteringActivity", "RuntimeStyleTestActivity", - "AnimatedMarkerActivity", + "AnimatedSymbolLayerActivity", "ViewPagerActivity", "MapFragmentActivity", "SupportMapFragmentActivity", -- cgit v1.2.1 From 86c824ce807ee3b029792a2d324111fb893addcf Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:24:04 +0200 Subject: [core] check opengl error state after custom layer invocations --- src/mbgl/renderer/layers/render_custom_layer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index adafd8583f..a429b8d82e 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace mbgl { @@ -46,11 +47,11 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) if (context != impl().context || !initialized) { //If the context changed, deinitialize the previous one before initializing the new one. if (context && !contextDestroyed && impl().deinitializeFn) { - impl().deinitializeFn(context); + MBGL_CHECK_ERROR(impl().deinitializeFn(context)); } context = impl().context; assert(impl().initializeFn); - impl().initializeFn(impl().context); + MBGL_CHECK_ERROR(impl().initializeFn(impl().context)); initialized = true; } @@ -75,7 +76,7 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) parameters.fieldOfView = state.getFieldOfView(); assert(impl().renderFn); - impl().renderFn(context, parameters); + MBGL_CHECK_ERROR(impl().renderFn(context, parameters)); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. -- cgit v1.2.1 From 6eedccf35f31f78648e67a3028db07f8932daf22 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:33:15 +0200 Subject: [core] blacklist vao usage on mali t720 (sapphire 650) Avoids problems on (amongst others) Samsung Galaxy J3 --- src/mbgl/gl/context.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index f40cfa1f2c..ba44adb42b 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -281,10 +281,17 @@ UniqueTexture Context::createTexture() { bool Context::supportsVertexArrays() const { static bool blacklisted = []() { - // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); + + Log::Info(Event::General, "GPU Identifier: %s", renderer.c_str()); + + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data + // Blacklist ARM Mali-T720 (in some MT8163 chipsets) as it crashes on glBindVertexArray return renderer.find("Adreno (TM) 2") != std::string::npos - || renderer.find("Adreno (TM) 3") != std::string::npos; + || renderer.find("Adreno (TM) 3") != std::string::npos + || renderer.find("Mali-T720") != std::string::npos + || renderer.find("Sapphire 650") != std::string::npos; + }(); return !blacklisted && -- cgit v1.2.1 From 5fc3d4ab1e673b25a9f180fb247a216280475335 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:33:57 +0200 Subject: [android] custom layer example - fix fragment shader source for opengl es 2 phones --- platform/android/src/example_custom_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 6d0bd4de4b..01a4aaaeb1 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -6,7 +6,7 @@ #include static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; -static const GLchar * fragmentShaderSource = "uniform vec4 fill_color; void main() { gl_FragColor = fill_color; }"; +static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }"; class ExampleCustomLayer { public: -- cgit v1.2.1 From 9a3eb808cf4c31d9dbcedf6d9a4bfdfcc60bd520 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:34:32 +0200 Subject: [android] custom layer example - add error checking to debug issues more easily --- platform/android/src/example_custom_layer.cpp | 170 +++++++++++++++++++++----- 1 file changed, 142 insertions(+), 28 deletions(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 01a4aaaeb1..f315d14681 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -2,8 +2,111 @@ #include #include - +#include #include +#include + +// DEBUGGING + +const char* stringFromError(GLenum err) { + switch (err) { + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + +#ifdef GL_TABLE_TOO_LARGE + case GL_TABLE_TOO_LARGE: + return "GL_TABLE_TOO_LARGE"; +#endif + +#ifdef GL_STACK_OVERFLOW + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; +#endif + +#ifdef GL_STACK_UNDERFLOW + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; +#endif + +#ifdef GL_CONTEXT_LOST + case GL_CONTEXT_LOST: + return "GL_CONTEXT_LOST"; +#endif + + default: + return "GL_UNKNOWN"; + } +} + +struct Error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +void checkError(const char *cmd, const char *file, int line) { + + GLenum err = GL_NO_ERROR; + if ((err = glGetError()) != GL_NO_ERROR) { + std::string message = std::string(cmd) + ": Error " + stringFromError(err); + + // Check for further errors + while ((err = glGetError()) != GL_NO_ERROR) { + message += ", "; + message += stringFromError(err); + } + + mbgl::Log::Error(mbgl::Event::General, message + " at " + file + ":" + mbgl::util::toString(line)); + throw Error(message + " at " + file + ":" + mbgl::util::toString(line)); + } +} + +#ifndef NDEBUG +#define GL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() noexcept(false) { checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }()) +#else +#define GL_CHECK_ERROR(cmd) (cmd) +#endif + +void checkLinkStatus(GLuint program) { + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + GLchar infoLog[maxLength]; + glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); + mbgl::Log::Info(mbgl::Event::General, &infoLog[0]); + throw Error(infoLog); + } + +} + +void checkCompileStatus(GLuint shader) { + GLint isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) { + GLint maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + GLchar errorLog[maxLength]; + glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); + mbgl::Log::Error(mbgl::Event::General, &errorLog[0]); + throw Error(errorLog); + } +} + +// /DEBUGGING static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }"; @@ -24,37 +127,48 @@ public: void initialize() { mbgl::Log::Info(mbgl::Event::General, "Initialize"); - program = glCreateProgram(); - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); - glCompileShader(vertexShader); - glAttachShader(program, vertexShader); - glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); - glCompileShader(fragmentShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - a_pos = glGetAttribLocation(program, "a_pos"); - fill_color = glGetUniformLocation(program, "fill_color"); - - GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 }; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW); + + // Debug info + int maxAttrib; + GL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib)); + mbgl::Log::Info(mbgl::Event::General, "Max vertex attributes: %i", maxAttrib); + + program = GL_CHECK_ERROR(glCreateProgram()); + vertexShader = GL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); + fragmentShader = GL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); + + GL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(vertexShader)); + checkCompileStatus(vertexShader); + GL_CHECK_ERROR(glAttachShader(program, vertexShader)); + GL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(fragmentShader)); + checkCompileStatus(fragmentShader); + GL_CHECK_ERROR(glAttachShader(program, fragmentShader)); + GL_CHECK_ERROR(glLinkProgram(program)); + checkLinkStatus(program); + + a_pos = GL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + fill_color = GL_CHECK_ERROR(glGetUniformLocation(program, "fill_color")); + + GLfloat background[] = { -1, -1, 1, -1, -1, 1, 1, 1 }; + GL_CHECK_ERROR(glGenBuffers(1, &buffer)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW)); } void render() { mbgl::Log::Info(mbgl::Event::General, "Render"); - glUseProgram(program); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glEnableVertexAttribArray(a_pos); - glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DEPTH_TEST); - glUniform4fv(fill_color, 1, color); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + GL_CHECK_ERROR(glUseProgram(program)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + GL_CHECK_ERROR(glEnableVertexAttribArray(a_pos)); + GL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL)); + GL_CHECK_ERROR(glDisable(GL_STENCIL_TEST)); + GL_CHECK_ERROR(glDisable(GL_DEPTH_TEST)); + GL_CHECK_ERROR(glUniform4fv(fill_color, 1, color)); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } GLuint program = 0; -- cgit v1.2.1 From 7b38ae3cc1455a4fea113567f805db69e5266ea5 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Thu, 22 Feb 2018 16:00:14 +0200 Subject: [android] custom layer example - remove dependencies on mbgl logging and string headers --- platform/android/src/example_custom_layer.cpp | 46 ++++++++++++++------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index f315d14681..f7b425c40a 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -1,13 +1,13 @@ #include #include - -#include -#include +#include +#include #include -#include // DEBUGGING +const char* LOG_TAG = "Custom Layer Example"; + const char* stringFromError(GLenum err) { switch (err) { case GL_INVALID_ENUM: @@ -58,16 +58,17 @@ void checkError(const char *cmd, const char *file, int line) { GLenum err = GL_NO_ERROR; if ((err = glGetError()) != GL_NO_ERROR) { - std::string message = std::string(cmd) + ": Error " + stringFromError(err); + std::ostringstream message; + message << cmd << ": Error " << stringFromError(err); // Check for further errors while ((err = glGetError()) != GL_NO_ERROR) { - message += ", "; - message += stringFromError(err); + message << ", " << stringFromError(err); } - mbgl::Log::Error(mbgl::Event::General, message + " at " + file + ":" + mbgl::util::toString(line)); - throw Error(message + " at " + file + ":" + mbgl::util::toString(line)); + message << " at " << file << ":" << line; + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, message.str().c_str()); + throw Error(message.str()); } } @@ -85,7 +86,7 @@ void checkLinkStatus(GLuint program) { glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); GLchar infoLog[maxLength]; glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); - mbgl::Log::Info(mbgl::Event::General, &infoLog[0]); + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &infoLog[0]); throw Error(infoLog); } @@ -101,7 +102,7 @@ void checkCompileStatus(GLuint shader) { // The maxLength includes the NULL character GLchar errorLog[maxLength]; glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); - mbgl::Log::Error(mbgl::Event::General, &errorLog[0]); + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &errorLog[0]); throw Error(errorLog); } } @@ -114,7 +115,7 @@ static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; voi class ExampleCustomLayer { public: ~ExampleCustomLayer() { - mbgl::Log::Info(mbgl::Event::General, "~ExampleCustomLayer"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "~ExampleCustomLayer"); if (program) { glDeleteBuffers(1, &buffer); glDetachShader(program, vertexShader); @@ -126,12 +127,12 @@ public: } void initialize() { - mbgl::Log::Info(mbgl::Event::General, "Initialize"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize"); // Debug info int maxAttrib; GL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib)); - mbgl::Log::Info(mbgl::Event::General, "Max vertex attributes: %i", maxAttrib); + __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Max vertex attributes: %i", maxAttrib); program = GL_CHECK_ERROR(glCreateProgram()); vertexShader = GL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); @@ -158,7 +159,7 @@ public: } void render() { - mbgl::Log::Info(mbgl::Event::General, "Render"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render"); GL_CHECK_ERROR(glUseProgram(program)); GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); @@ -184,12 +185,12 @@ public: GLfloat ExampleCustomLayer::color[] = { 0.0f, 1.0f, 0.0f, 1.0f }; jlong JNICALL nativeCreateContext(JNIEnv*, jobject) { - mbgl::Log::Info(mbgl::Event::General, "nativeCreateContext"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeCreateContext"); return reinterpret_cast(new ExampleCustomLayer()); } void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) { - mbgl::Log::Info(mbgl::Event::General, "nativeSetColor: %.2f, %.2f, %.2f, %.2f", red, green, blue, alpha); + __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "nativeSetColor: %.2f, %.2f, %.2f, %.2f", red, green, blue, alpha); ExampleCustomLayer::color[0] = red; ExampleCustomLayer::color[1] = green; ExampleCustomLayer::color[2] = blue; @@ -197,26 +198,27 @@ void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat b } void nativeInitialize(void *context) { - mbgl::Log::Info(mbgl::Event::General, "nativeInitialize"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeInitialize"); reinterpret_cast(context)->initialize(); } void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& /*parameters*/) { - mbgl::Log::Info(mbgl::Event::General, "nativeRender"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeRender"); reinterpret_cast(context)->render(); } void nativeContextLost(void */*context*/) { - mbgl::Log::Info(mbgl::Event::General, "nativeContextLost"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeContextLost"); } void nativeDeinitialize(void *context) { - mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeDeinitialize"); delete reinterpret_cast(context); } extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { - mbgl::Log::Info(mbgl::Event::General, "OnLoad"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "OnLoad"); + JNIEnv *env = nullptr; vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); -- cgit v1.2.1 From 5de373fff0e71496b6aa11ecb6556f958a28d80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 21 Feb 2018 12:35:44 +0100 Subject: [core] continue loading style even if we mutate it When we load a stale style from cache, and the user immediately starts mutating it, we should continue loading the style so that we'll get a fresh copy of the data into our cache and avoid perpetually showing the stale style. --- src/mbgl/style/style_impl.cpp | 5 ----- test/map/map.test.cpp | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index d330b3120a..0c7f924917 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -55,11 +55,6 @@ void Style::Impl::loadURL(const std::string& url_) { url = url_; styleRequest = fileSource.request(Resource::style(url), [this](Response res) { - // Once we get a fresh style, or the style is mutated, stop revalidating. - if (res.isFresh() || mutated) { - styleRequest.reset(); - } - // Don't allow a loaded, mutated style to be overwritten with a new version. if (mutated && loaded) { return; diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9b34ea89b0..30c076ad89 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -252,7 +252,7 @@ TEST(Map, DoubleStyleLoad) { } TEST(Map, StyleFresh) { - // The map should not revalidate fresh styles. + // The map should continue to revalidate fresh styles. MapTest test; @@ -264,11 +264,11 @@ TEST(Map, StyleFresh) { response.expires = Timestamp::max(); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); } TEST(Map, StyleExpired) { - // The map should allow expired styles to be revalidated, so long as no mutations are made. + // The map should allow expired styles to be revalidated until we get a fresh style. using namespace std::chrono_literals; @@ -284,11 +284,22 @@ TEST(Map, StyleExpired) { test.fileSource.respond(Resource::Style, response); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Mutate layer. From now on, sending a response to the style won't overwrite it anymore, but + // we should continue to wait for a fresh response. test.map.getStyle().addLayer(std::make_unique("bg")); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Send another expired response, and confirm that we didn't overwrite the style, but continue + // to wait for a fresh response. + test.fileSource.respond(Resource::Style, response); + EXPECT_EQ(1u, test.fileSource.requests.size()); + EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); + + // Send a fresh response, and confirm that we didn't overwrite the style, but continue to wait + // for a fresh response. + response.expires = util::now() + 1h; test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); } @@ -352,7 +363,7 @@ TEST(Map, StyleEarlyMutation) { response.data = std::make_shared(util::read_file("test/fixtures/api/water.json")); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("water")); } -- cgit v1.2.1 From c1b17e251af33d0a14a668018f08a809d5d85bae Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Sun, 25 Feb 2018 11:08:56 +0200 Subject: [qt] Fix wrong signal being emitted Probably caused by a typo when refactoring the asynchronous rendering. --- platform/qt/src/qmapboxgl_map_observer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp index e180ed8fda..60c6b81841 100644 --- a/platform/qt/src/qmapboxgl_map_observer.cpp +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -65,7 +65,7 @@ void QMapboxGLMapObserver::onDidFinishRenderingFrame(mbgl::MapObserver::RenderMo void QMapboxGLMapObserver::onWillStartRenderingMap() { - emit mapChanged(QMapboxGL::MapChangeWillStartLoadingMap); + emit mapChanged(QMapboxGL::MapChangeWillStartRenderingMap); } void QMapboxGLMapObserver::onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) -- cgit v1.2.1 From 17334c74a0c56feaecf3493861bccddaa01c3801 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Sun, 25 Feb 2018 11:10:53 +0200 Subject: [core] Fix build with Qt + Android + GCC 4.9 Android uses and old libc when building with GCC 4.9 and some math functions are not on std::. --- src/mbgl/util/tiny_sdf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp index 60839357d5..6edcd83bc2 100644 --- a/src/mbgl/util/tiny_sdf.cpp +++ b/src/mbgl/util/tiny_sdf.cpp @@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do for (uint32_t i = 0; i < size; i++) { double distance = gridOuter[i] - gridInner[i]; - sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff)))); + sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff)))); } return sdf; -- cgit v1.2.1 From ba019b025681fc6f623ab07f62488b509f10bb4a Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Fri, 23 Feb 2018 11:16:41 +0100 Subject: [qt] Fix issue if resources not being found on the database Once again QVariant getting confused about its contents datatype. With this patch we use QString directly and copy the contents, which should be cheap with Qt implicity sharing. --- platform/qt/src/sqlite3.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 80efd6a326..eb4a798043 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -270,20 +270,15 @@ void Statement::bind(int offset, optional value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { +void Statement::bind(int offset, const char* value, std::size_t length, bool /* retain */) { assert(impl); if (length > std::numeric_limits::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } - // Qt SQLite driver treats QByteArray as blob: we need to explicitly - // declare the variant type as string. - QVariant text(QVariant::Type::String); - text.setValue(retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length)); - // Field numbering starts at 0. - impl->query.bindValue(offset - 1, std::move(text), QSql::In); + impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); checkQueryError(impl->query); } -- cgit v1.2.1 From 869799a716ba6463d5135d0b55d7ed27ea16fa41 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Thu, 22 Feb 2018 11:33:03 +0100 Subject: [tests] Added a test for getting resources from the database This test would have flagged the Qt regression. --- .gitignore | 4 ++- test/fixtures/offline_database/satellite_test.db | Bin 0 -> 114688 bytes test/storage/offline_database.test.cpp | 34 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/offline_database/satellite_test.db diff --git a/.gitignore b/.gitignore index 0f4fa77858..b32baaac09 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ xcuserdata /test/fixtures/api/2.png /test/fixtures/offline_database/offline.db /test/fixtures/offline_database/offline.db-* +/test/fixtures/offline_database/satellite.db +/test/fixtures/offline_database/satellite.db-* /test/fixtures/offline_database/invalid.db /test/fixtures/offline_database/invalid.db-* /test/fixtures/offline_database/migrated.db @@ -38,4 +40,4 @@ test/fixtures/storage/assets.zip # Generated list files from code generation /scripts/generate-cmake-files.list /scripts/generate-shaders.list -/scripts/generate-style-code.list \ No newline at end of file +/scripts/generate-style-code.list diff --git a/test/fixtures/offline_database/satellite_test.db b/test/fixtures/offline_database/satellite_test.db new file mode 100644 index 0000000000..95dd8617ff Binary files /dev/null and b/test/fixtures/offline_database/satellite_test.db differ diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 94daf59c02..23117173d1 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -38,6 +38,10 @@ void writeFile(const char* name, const std::string& data) { mbgl::util::write_file(name, data); } +void copyFile(const char* orig, const char* dest) { + mbgl::util::write_file(dest, mbgl::util::read_file(orig)); +} + } // namespace TEST(OfflineDatabase, TEST_REQUIRES_WRITE(Create)) { @@ -143,6 +147,36 @@ TEST(OfflineDatabase, PutResource) { EXPECT_EQ("second", *updateGetResult->data); } +TEST(OfflineDatabase, TEST_REQUIRES_WRITE(GetResourceFromOfflineRegion)) { + using namespace mbgl; + + createDir("test/fixtures/offline_database"); + deleteFile("test/fixtures/offline_database/satellite.db"); + copyFile("test/fixtures/offline_database/satellite_test.db", "test/fixtures/offline_database/satellite.db"); + + OfflineDatabase db("test/fixtures/offline_database/satellite.db", mapbox::sqlite::ReadOnly); + + Resource resource = Resource::style("mapbox://styles/mapbox/satellite-v9"); + ASSERT_TRUE(db.get(resource)); +} + +TEST(OfflineDatabase, PutAndGetResource) { + using namespace mbgl; + + OfflineDatabase db(":memory:"); + + Response response1; + response1.data = std::make_shared("foobar"); + + Resource resource = Resource::style("mapbox://example.com/style"); + + db.put(resource, response1); + + auto response2 = db.get(resource); + + ASSERT_EQ(*response1.data, *(*response2).data); +} + TEST(OfflineDatabase, PutTile) { using namespace mbgl; -- cgit v1.2.1 From 80fdae62ca1ba97d91eb4e907306b8d757f04cd0 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 26 Feb 2018 13:22:15 -0800 Subject: [core,node] Pin 'nan' to ~2.8 to avoid Callback::Call deprecation. Alternative fix to #11288, until we figure out a proper async_hooks implementation. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ff4b32735..977cd2b09c 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "^2.6.2", + "nan": "~2.8", "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, -- cgit v1.2.1 From d425ec72379791ea47f1088e0c28fdf238cfc736 Mon Sep 17 00:00:00 2001 From: Molly Lloyd Date: Mon, 26 Feb 2018 14:12:59 -0800 Subject: [ios, macos] add tileset encoding option to darwin sources (#11274) * add tileset encoding option to darwin sources * correct style guide * link out to dem encoding docs * markdownify comments --- .../darwin/docs/guides/For Style Authors.md.ejs | 1 + platform/darwin/src/MGLTileSource.h | 30 ++++++++++++++++++++++ platform/darwin/src/MGLTileSource.mm | 18 +++++++++++++ platform/darwin/test/MGLTileSetTests.mm | 17 ++++++++++++ platform/ios/docs/guides/For Style Authors.md | 1 + platform/macos/docs/guides/For Style Authors.md | 1 + 6 files changed, 68 insertions(+) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index c2eaf337e0..7648d6f7ac 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -205,6 +205,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index 3dc268db60..2d75fa14d8 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -117,6 +117,7 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos; */ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem; + /** Tile coordinate systems that determine how tile coordinates in tile URLs are interpreted. @@ -141,6 +142,35 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) { MGLTileCoordinateSystemTMS }; + +/** + An `NSNumber` object containing an unsigned integer that specifies the encoding + formula for raster-dem tilesets. The integer corresponds to one of + the constants described in `MGLDEMEncoding`. + + The default value for this option is `MGLDEMEncodingMapbox`. + + This option is not supported by the TileJSON spec. + */ +extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding; + +/** + The encoding formula used to generate the raster-dem tileset +*/ + +typedef NS_ENUM(NSUInteger, MGLDEMEncoding) { + + /** + Raster tiles generated with the [Mapbox encoding formula](https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb). + */ + MGLDEMEncodingMapbox = 0, + + /** + Raster tiles generated with the [Mapzen Terrarium encoding formula](https://aws.amazon.com/public-datasets/terrain/). + */ + MGLDEMEncodingTerrarium +}; + /** `MGLTileSource` is a map content source that supplies map tiles to be shown on the map. The location of and metadata about the tiles are defined either by an diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index bc985bd728..c37812ab8e 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -19,6 +19,7 @@ const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceO const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString"; const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos"; const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem"; +const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOptionDEMEncoding"; @implementation MGLTileSource @@ -129,5 +130,22 @@ mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTem } } + if (NSNumber *demEncodingNumber = options[MGLTileSourceOptionDEMEncoding]) { + if (![demEncodingNumber isKindOfClass:[NSValue class]]) { + [NSException raise:NSInvalidArgumentException + format:@"MGLTileSourceOptionDEMEncoding must be set to an NSValue or NSNumber."]; + } + MGLDEMEncoding demEncoding; + [demEncodingNumber getValue:&demEncoding]; + switch (demEncoding) { + case MGLDEMEncodingMapbox: + tileSet.encoding = mbgl::Tileset::DEMEncoding::Mapbox; + break; + case MGLDEMEncodingTerrarium: + tileSet.encoding = mbgl::Tileset::DEMEncoding::Terrarium; + break; + } + } + return tileSet; } diff --git a/platform/darwin/test/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm index 4d5e1fcd05..74c84184e1 100644 --- a/platform/darwin/test/MGLTileSetTests.mm +++ b/platform/darwin/test/MGLTileSetTests.mm @@ -102,6 +102,23 @@ // the scheme is reflected by the mbgl tileset XCTAssertEqual(tileSet.scheme, mbgl::Tileset::Scheme::TMS); + + // when the dem enciding is changed using an NSNumber + tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ + MGLTileSourceOptionDEMEncoding: @(MGLDEMEncodingTerrarium), + }); + + // the encoding is reflected by the mbgl tileset + XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium); + + // when the dem enciding is changed using an NSValue + MGLDEMEncoding terrarium = MGLDEMEncodingTerrarium; + tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{ + MGLTileSourceOptionDEMEncoding: [NSValue value:&terrarium withObjCType:@encode(MGLDEMEncoding)], + }); + + // the encoding is reflected by the mbgl tileset + XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium); } - (void)testInvalidTileSet { diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 68d59fbc70..c4aabb0410 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -154,6 +154,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index de838cd78f..636cfa7eb7 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -141,6 +141,7 @@ In style JSON | In TileJSON | In the SDK `tileSize` | — | `MGLTileSourceOptionTileSize` `attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security) `scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem` +`encoding` | – | `MGLTileSourceOptionDEMEncoding` ### Shape sources -- cgit v1.2.1 From 91f6e09dadd09926508b88083fc3f5d9a8da9226 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 21 Feb 2018 15:48:51 -0800 Subject: [core] Don't reuse heatmap render texture on viewport resize. Fixes issue #11228. --- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 0f9e3239ef..4f2e899220 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -67,7 +67,7 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { } } - if (!renderTexture) { + if (!parameters.context.supportsHalfFloatTextures || !renderTexture) { renderTexture = OffscreenTexture(parameters.context, size, gl::TextureType::UnsignedByte); renderTexture->bind(); } -- cgit v1.2.1 From 7097e04fb359e8351e330e248bb876bc84c4513b Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 23 Feb 2018 16:24:29 -0800 Subject: [core, node] Hold on to map handle during NodeMap::request. Avoids a potential crash if garbage collection happens in the middle of a call to NodeMap::request from a map that's eligible for GC. Fixes issue #11281 --- platform/node/src/node_map.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index ac14df0228..0fe69e8ac9 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -1154,6 +1154,10 @@ NodeMap::~NodeMap() { std::unique_ptr NodeMap::request(const mbgl::Resource& resource, mbgl::FileSource::Callback callback_) { Nan::HandleScope scope; + // Because this method may be called while this NodeMap is already eligible for garbage collection, + // we need to explicitly hold onto our own handle here so that GC during a v8 call doesn't destroy + // *this while we're still executing code. + handle(); v8::Local argv[] = { Nan::New(this), -- cgit v1.2.1 From df92af27d97850a9d7f39c49af85715194383ddc Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 26 Feb 2018 17:41:26 +0200 Subject: [core] Add space in OfflineDatabase::updateMetadata SQL query --- platform/default/mbgl/storage/offline_database.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 65c2097182..b996dc4dd9 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -593,7 +593,7 @@ OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& defin OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { // clang-format off Statement stmt = getStatement( - "UPDATE regions SET description = ?1" + "UPDATE regions SET description = ?1 " "WHERE id = ?2"); // clang-format on stmt->bindBlob(1, metadata); -- cgit v1.2.1 From 380f5dc88bb4103a528740e67d887a8695529528 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 26 Feb 2018 18:05:05 +0200 Subject: [qt] Rename Qt SDK to 'Mapbox Maps SDK for Qt' --- INSTALL.md | 2 +- README.md | 2 +- platform/qt/README.md | 4 ++-- platform/qt/config.qdocconf | 2 +- platform/qt/src/qmapbox.cpp | 14 +++++++------- platform/qt/src/qmapboxgl.cpp | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index a28348d15c..e5d059e1bb 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -47,7 +47,7 @@ See the relevant SDK documentation for next steps: * [Maps SDK for Android](platform/android/) * [Maps SDK for iOS](platform/ios/) * [Maps SDK for macOS](platform/macos/) -* [Mapbox Qt SDK](platform/qt/) +* [Maps SDK for Qt](platform/qt/) * [Mapbox GL Native on Linux](platform/linux/) * [node-mapbox-gl-native](platform/node/) diff --git a/README.md b/README.md index 32bfdf3b51..8e3e51785b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie | [Mapbox Maps SDK for iOS](platform/ios/) | Objective-C or Swift | [![Bitrise](https://www.bitrise.io/app/7514e4cf3da2cc57.svg?token=OwqZE5rSBR9MVWNr_lf4sA&branch=master)](https://www.bitrise.io/app/7514e4cf3da2cc57) | | [Mapbox Maps SDK for macOS](platform/macos/) | Objective-C, Swift, or AppleScript | [![Bitrise](https://www.bitrise.io/app/155ef7da24b38dcd.svg?token=4KSOw_gd6WxTnvGE2rMttg&branch=master)](https://www.bitrise.io/app/155ef7da24b38dcd) | | [node-mapbox-gl-native](platform/node/) | Node.js | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) | -| [Mapbox Qt SDK](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | +| [Mapbox Maps SDK for Qt](platform/qt) | C++03 | [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) | Additional Mapbox GL Native–based libraries for **hybrid applications** are developed outside of this repository: diff --git a/platform/qt/README.md b/platform/qt/README.md index 4d2d887828..018f8823b6 100644 --- a/platform/qt/README.md +++ b/platform/qt/README.md @@ -1,4 +1,4 @@ -# Mapbox Qt SDK +# Mapbox Maps SDK for Qt [![Circle CI build status](https://circleci.com/gh/mapbox/mapbox-gl-native.svg?style=shield)](https://circleci.com/gh/mapbox/workflows/mapbox-gl-native/tree/master) [![AppVeyor CI build status](https://ci.appveyor.com/api/projects/status/3q12kbcooc6df8uc?svg=true)](https://ci.appveyor.com/project/Mapbox/mapbox-gl-native) @@ -9,7 +9,7 @@ available in the Qt SDK since Qt 5.9. Use the [Qt bugtracker](https://bugreports to the plugin and this GitHub repository for bugs related to Mapbox GL Native and the Qt bindings. You should build this repository if you want to develop/contribute using the low level Qt C++ bindings or -want to contribute to the Mapbox GL Native projectusing the Qt port for debugging. +want to contribute to the Mapbox GL Native project using the Qt port for debugging. See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf index d6f0edbd32..9d3b8a7ef4 100644 --- a/platform/qt/config.qdocconf +++ b/platform/qt/config.qdocconf @@ -7,7 +7,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) Cpp.ignoretokens = Q_MAPBOXGL_EXPORT project = QMapboxGL -description = Mapbox Qt SDK +description = Mapbox Maps SDK for Qt language = Cpp outputdir = docs diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index ec76ebfe53..1386a4b7aa 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -24,7 +24,7 @@ namespace QMapbox { /*! \namespace QMapbox - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Contains miscellaneous Mapbox bindings used throughout QMapboxGL. */ @@ -74,7 +74,7 @@ namespace QMapbox { /*! \class QMapbox::Feature - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents \l {https://www.mapbox.com/help/define-features/}{map features} via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a @@ -94,7 +94,7 @@ namespace QMapbox { /*! \class QMapbox::ShapeAnnotationGeometry - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a shape annotation geometry. */ @@ -113,7 +113,7 @@ namespace QMapbox { /*! \class QMapbox::SymbolAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt A symbol annotation comprises of its geometry and an icon identifier. */ @@ -121,7 +121,7 @@ namespace QMapbox { /*! \class QMapbox::LineAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a line annotation object, along with its properties. @@ -131,7 +131,7 @@ namespace QMapbox { /*! \class QMapbox::FillAnnotation - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt Represents a fill annotation object, along with its properties. @@ -195,7 +195,7 @@ namespace QMapbox { /*! \class QMapbox::CustomLayerRenderParameters - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapbox::CustomLayerRenderParameters provides the data passed on each render pass for a custom layer. diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 414b65255c..982f4b3b35 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -136,7 +136,7 @@ std::unique_ptr toStyleImage(const QString &id, const QImage \class QMapboxGLSettings \brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL. - \inmodule Mapbox Qt SDK + \inmodule Mapbox Maps SDK for Qt QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. @@ -454,7 +454,7 @@ void QMapboxGLSettings::setResourceTransform(const std::function Date: Tue, 27 Feb 2018 15:51:22 +0100 Subject: [android ] - new gestures library (#11221) * [android] new gesture library - added SNAPSHOT dependency * [android] new gesture library - cleaned up redundant classes * [android] new gesture library - limiting scale when rotating * [android] new gesture library - shove gesture filtering * [android] new gesture library - increase rotation threshold when scaling * [android] new gesture library - minimum angular velocity * [android] new gesture library - exposed gestures execution listeners * [android] new gesture library - notifying new listeners tests * [android] new gesture library - removed tracking setting * [android] new gesture library - resetting focal point with every scale/rotate callback * [android] new gesture library - fixed camera change dispatcher callbacks * [android] new gesture library - cancel velocity animations in maps onStop() * [android] new gesture library - extracted telemetry pushes to a method * [android] new gesture library - deprecated onScrollListener * [android] new gesture library - unified shove listener name --- platform/android/MapboxGLAndroidSDK/build.gradle | 1 + .../gesturedetectors/BaseGestureDetector.java | 160 --- .../gesturedetectors/MoveGestureDetector.java | 185 ---- .../gesturedetectors/RotateGestureDetector.java | 180 ---- .../gesturedetectors/ShoveGestureDetector.java | 214 ---- .../gesturedetectors/TwoFingerGestureDetector.java | 224 ---- .../multitouch/gesturedetectors/package-info.java | 4 - .../mapboxsdk/constants/MapboxConstants.java | 29 +- .../mapbox/mapboxsdk/maps/MapGestureDetector.java | 1096 ++++++++++---------- .../com/mapbox/mapboxsdk/maps/MapKeyListener.java | 8 +- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 73 +- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 210 +++- .../java/com/mapbox/mapboxsdk/maps/Transform.java | 34 +- .../src/main/res/values/dimens.xml | 9 + .../mapboxsdk/maps/MapTouchListenersTest.java | 132 ++- .../com/mapbox/mapboxsdk/maps/MapboxMapTest.java | 2 +- platform/android/gradle/dependencies.gradle | 24 +- 17 files changed, 981 insertions(+), 1604 deletions(-) delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index e2e0881857..9063321648 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.library' dependencies { api dependenciesList.mapboxAndroidTelemetry api dependenciesList.mapboxJavaGeoJSON + api dependenciesList.mapboxAndroidGestures implementation dependenciesList.supportAnnotations implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java deleted file mode 100644 index b7bcb925a1..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public abstract class BaseGestureDetector { - protected final Context context; - protected boolean gestureInProgress; - - protected MotionEvent prevEvent; - protected MotionEvent currEvent; - - protected float currPressure; - protected float prevPressure; - protected long timeDelta; - - /** - * This value is the threshold ratio between the previous combined pressure - * and the current combined pressure. When pressure decreases rapidly - * between events the position values can often be imprecise, as it usually - * indicates that the user is in the process of lifting a pointer off of the - * device. This value was tuned experimentally. - */ - protected static final float PRESSURE_THRESHOLD = 0.67f; - - public BaseGestureDetector(Context context) { - this.context = context; - } - - /** - * All gesture detectors need to be called through this method to be able to - * detect gestures. This method delegates work to handler methods - * (handleStartProgressEvent, handleInProgressEvent) implemented in - * extending classes. - * - * @param event MotionEvent - * @return {@code true} as handled - */ - public boolean onTouchEvent(MotionEvent event) { - final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; - if (!gestureInProgress) { - handleStartProgressEvent(actionCode, event); - } else { - handleInProgressEvent(actionCode, event); - } - return true; - } - - /** - * Called when the current event occurred when NO gesture is in progress - * yet. The handling in this implementation may set the gesture in progress - * (via gestureInProgress) or out of progress - * - * @param actionCode Action Code from MotionEvent - * @param event MotionEvent - */ - protected abstract void handleStartProgressEvent(int actionCode, - MotionEvent event); - - /** - * Called when the current event occurred when a gesture IS in progress. The - * handling in this implementation may set the gesture out of progress (via - * gestureInProgress). - * - * @param actionCode Action Code from MotionEvent - * @param event MotionEvent - */ - protected abstract void handleInProgressEvent(int actionCode, - MotionEvent event); - - protected void updateStateByEvent(MotionEvent curr) { - final MotionEvent prev = prevEvent; - - // Reset currEvent - if (currEvent != null) { - currEvent.recycle(); - currEvent = null; - } - currEvent = MotionEvent.obtain(curr); - - // Delta time - timeDelta = curr.getEventTime() - prev.getEventTime(); - - // Pressure - currPressure = curr.getPressure(curr.getActionIndex()); - prevPressure = prev.getPressure(prev.getActionIndex()); - } - - protected void resetState() { - if (prevEvent != null) { - prevEvent.recycle(); - prevEvent = null; - } - if (currEvent != null) { - currEvent.recycle(); - currEvent = null; - } - gestureInProgress = false; - } - - /** - * Returns {@code true} if a gesture is currently in progress. - * - * @return {@code true} if a gesture is currently in progress, {@code false} - * otherwise. - */ - public boolean isInProgress() { - return gestureInProgress; - } - - /** - * Return the time difference in milliseconds between the previous accepted - * GestureDetector event and the current GestureDetector event. - * - * @return Time difference since the last move event in milliseconds. - */ - public long getTimeDelta() { - return timeDelta; - } - - /** - * Return the event time of the current GestureDetector event being - * processed. - * - * @return Current GestureDetector event time in milliseconds. - */ - public long getEventTime() { - return currEvent.getEventTime(); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java deleted file mode 100644 index bc7dda6159..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.graphics.PointF; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class MoveGestureDetector extends BaseGestureDetector { - - /** - * Listener which must be implemented which is used by MoveGestureDetector - * to perform callbacks to any implementing class which is registered to a - * MoveGestureDetector via the constructor. - * - * @see MoveGestureDetector.SimpleOnMoveGestureListener - */ - public interface OnMoveGestureListener { - public boolean onMove(MoveGestureDetector detector); - - public boolean onMoveBegin(MoveGestureDetector detector); - - public void onMoveEnd(MoveGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnMoveGestureListener. - */ - public static class SimpleOnMoveGestureListener implements - OnMoveGestureListener { - public boolean onMove(MoveGestureDetector detector) { - return false; - } - - public boolean onMoveBegin(MoveGestureDetector detector) { - return true; - } - - public void onMoveEnd(MoveGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private static final PointF FOCUS_DELTA_ZERO = new PointF(); - - private final OnMoveGestureListener listener; - - private PointF focusExternal = new PointF(); - private PointF focusDeltaExternal = new PointF(); - - public MoveGestureDetector(Context context, OnMoveGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_DOWN: - resetState(); // In case we missed an UP/CANCEL event - - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - break; - - case MotionEvent.ACTION_MOVE: - gestureInProgress = listener.onMoveBegin(this); - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - listener.onMoveEnd(this); - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. - if (currPressure / prevPressure > PRESSURE_THRESHOLD) { - final boolean updatePrevious = listener.onMove(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - - // Focus intenal - PointF currFocusInternal = determineFocalPoint(curr); - PointF prevFocusInternal = determineFocalPoint(prev); - - // Focus external - // - Prevent skipping of focus delta when a finger is added or removed - boolean skipNextMoveEvent = prev.getPointerCount() != curr - .getPointerCount(); - focusDeltaExternal = skipNextMoveEvent ? FOCUS_DELTA_ZERO - : new PointF(currFocusInternal.x - prevFocusInternal.x, - currFocusInternal.y - prevFocusInternal.y); - - // - Don't directly use mFocusInternal (or skipping will occur). Add - // unskipped delta values to focusExternal instead. - focusExternal.x += focusDeltaExternal.x; - focusExternal.y += focusDeltaExternal.y; - } - - /** - * Determine (multi)finger focal point (a.k.a. center point between all - * fingers) - * - * @param motionEvent a {@link MotionEvent} object. - * @return PointF focal point - */ - private PointF determineFocalPoint(MotionEvent motionEvent) { - // Number of fingers on screen - final int pCount = motionEvent.getPointerCount(); - float x = 0.0f; - float y = 0.0f; - - for (int i = 0; i < pCount; i++) { - x += motionEvent.getX(i); - y += motionEvent.getY(i); - } - - return new PointF(x / pCount, y / pCount); - } - - public float getFocusX() { - return focusExternal.x; - } - - public float getFocusY() { - return focusExternal.y; - } - - public PointF getFocusDelta() { - return focusDeltaExternal; - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java deleted file mode 100644 index 8c111a68df..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class RotateGestureDetector extends TwoFingerGestureDetector { - - /** - * Listener which must be implemented which is used by RotateGestureDetector - * to perform callbacks to any implementing class which is registered to a - * RotateGestureDetector via the constructor. - * - * @see RotateGestureDetector.SimpleOnRotateGestureListener - */ - public interface OnRotateGestureListener { - public boolean onRotate(RotateGestureDetector detector); - - public boolean onRotateBegin(RotateGestureDetector detector); - - public void onRotateEnd(RotateGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnRotateGestureListener. - */ - public static class SimpleOnRotateGestureListener implements - OnRotateGestureListener { - public boolean onRotate(RotateGestureDetector detector) { - return false; - } - - public boolean onRotateBegin(RotateGestureDetector detector) { - return true; - } - - public void onRotateEnd(RotateGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private final OnRotateGestureListener listener; - private boolean sloppyGesture; - - public RotateGestureDetector(Context context, - OnRotateGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_DOWN: - // At least the second finger is on screen now - - resetState(); // In case we missed an UP/CANCEL event - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - - // See if we have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start gesture now - gestureInProgress = listener.onRotateBegin(this); - } - break; - - case MotionEvent.ACTION_MOVE: - if (!sloppyGesture) { - break; - } - - // See if we still have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start normal gesture now - gestureInProgress = listener.onRotateBegin(this); - } - - break; - - case MotionEvent.ACTION_POINTER_UP: - if (!sloppyGesture) { - break; - } - - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_UP: - // Gesture ended but - updateStateByEvent(event); - - if (!sloppyGesture) { - listener.onRotateEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_CANCEL: - if (!sloppyGesture) { - listener.onRotateEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. - if (currPressure / prevPressure > PRESSURE_THRESHOLD) { - final boolean updatePrevious = listener.onRotate(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - @Override - protected void resetState() { - super.resetState(); - sloppyGesture = false; - } - - /** - * Return the rotation difference from the previous rotate event to the - * current event. - * - * @return The current rotation //difference in degrees. - */ - public float getRotationDegreesDelta() { - double diffRadians = Math.atan2(prevFingerDiffY, prevFingerDiffX) - - Math.atan2(currFingerDiffY, currFingerDiffX); - return (float) (diffRadians * 180.0 / Math.PI); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java deleted file mode 100644 index 9396578e48..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.view.MotionEvent; - -/** - * @author Robert Nordan (robert.nordan@norkart.no) - *

- * Copyright (c) 2013, Norkart AS - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public class ShoveGestureDetector extends TwoFingerGestureDetector { - - /** - * Listener which must be implemented which is used by ShoveGestureDetector - * to perform callbacks to any implementing class which is registered to a - * ShoveGestureDetector via the constructor. - * - * @see ShoveGestureDetector.SimpleOnShoveGestureListener - */ - public interface OnShoveGestureListener { - public boolean onShove(ShoveGestureDetector detector); - - public boolean onShoveBegin(ShoveGestureDetector detector); - - public void onShoveEnd(ShoveGestureDetector detector); - } - - /** - * Helper class which may be extended and where the methods may be - * implemented. This way it is not necessary to implement all methods of - * OnShoveGestureListener. - */ - public static class SimpleOnShoveGestureListener implements - OnShoveGestureListener { - public boolean onShove(ShoveGestureDetector detector) { - return false; - } - - public boolean onShoveBegin(ShoveGestureDetector detector) { - return true; - } - - public void onShoveEnd(ShoveGestureDetector detector) { - // Do nothing, overridden implementation may be used - } - } - - private float prevAverageY; - private float currAverageY; - - private final OnShoveGestureListener listener; - private boolean sloppyGesture; - - public ShoveGestureDetector(Context context, OnShoveGestureListener listener) { - super(context); - this.listener = listener; - } - - @Override - protected void handleStartProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_DOWN: - // At least the second finger is on screen now - - resetState(); // In case we missed an UP/CANCEL event - prevEvent = MotionEvent.obtain(event); - timeDelta = 0; - - updateStateByEvent(event); - - // See if we have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start gesture now - gestureInProgress = listener.onShoveBegin(this); - } - break; - - case MotionEvent.ACTION_MOVE: - if (!sloppyGesture) { - break; - } - - // See if we still have a sloppy gesture - sloppyGesture = isSloppyGesture(event); - if (!sloppyGesture) { - // No, start normal gesture now - gestureInProgress = listener.onShoveBegin(this); - } - - break; - - case MotionEvent.ACTION_POINTER_UP: - if (!sloppyGesture) { - break; - } - - break; - } - } - - @Override - protected void handleInProgressEvent(int actionCode, MotionEvent event) { - switch (actionCode) { - case MotionEvent.ACTION_POINTER_UP: - // Gesture ended but - updateStateByEvent(event); - - if (!sloppyGesture) { - listener.onShoveEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_CANCEL: - if (!sloppyGesture) { - listener.onShoveEnd(this); - } - - resetState(); - break; - - case MotionEvent.ACTION_MOVE: - updateStateByEvent(event); - - // Only accept the event if our relative pressure is within - // a certain limit. This can help filter shaky data as a - // finger is lifted. Also check that shove is meaningful. - if (currPressure / prevPressure > PRESSURE_THRESHOLD - && Math.abs(getShovePixelsDelta()) > 0.5f) { - final boolean updatePrevious = listener.onShove(this); - if (updatePrevious) { - prevEvent.recycle(); - prevEvent = MotionEvent.obtain(event); - } - } - break; - } - } - - @Override - protected void resetState() { - super.resetState(); - sloppyGesture = false; - prevAverageY = 0.0f; - currAverageY = 0.0f; - } - - @Override - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - float py0 = prev.getY(0); - float py1 = prev.getY(1); - prevAverageY = (py0 + py1) / 2.0f; - - float cy0 = curr.getY(0); - float cy1 = curr.getY(1); - currAverageY = (cy0 + cy1) / 2.0f; - } - - @Override - protected boolean isSloppyGesture(MotionEvent event) { - boolean sloppy = super.isSloppyGesture(event); - if (sloppy) { - return true; - } - - // If it's not traditionally sloppy, we check if the angle between - // fingers - // is acceptable. - double angle = Math.abs(Math.atan2(currFingerDiffY, currFingerDiffX)); - // about 20 degrees, left or right - return !((0.0f < angle && angle < 0.35f) || 2.79f < angle - && angle < Math.PI); - } - - /** - * Return the distance in pixels from the previous shove event to the - * current event. - * - * @return The current distance in pixels. - */ - public float getShovePixelsDelta() { - return currAverageY - prevAverageY; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java deleted file mode 100644 index db492b6556..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java +++ /dev/null @@ -1,224 +0,0 @@ -package com.almeros.android.multitouch.gesturedetectors; - -import android.content.Context; -import android.graphics.PointF; -import android.util.DisplayMetrics; -import android.view.MotionEvent; -import android.view.ViewConfiguration; - -/** - * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie - * (code.almeros.com) - *

- * All rights reserved. - *

- * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - *

- * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - *

- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -public abstract class TwoFingerGestureDetector extends BaseGestureDetector { - - private final float edgeSlop; - - protected float prevFingerDiffX; - protected float prevFingerDiffY; - protected float currFingerDiffX; - protected float currFingerDiffY; - - private float currLen; - private float prevLen; - - private PointF focus; - - public TwoFingerGestureDetector(Context context) { - super(context); - - ViewConfiguration config = ViewConfiguration.get(context); - - edgeSlop = config.getScaledEdgeSlop(); - } - - @Override - protected abstract void handleStartProgressEvent(int actionCode, - MotionEvent event); - - @Override - protected abstract void handleInProgressEvent(int actionCode, - MotionEvent event); - - protected void updateStateByEvent(MotionEvent curr) { - super.updateStateByEvent(curr); - - final MotionEvent prev = prevEvent; - - currLen = -1; - prevLen = -1; - - // Previous - final float px0 = prev.getX(0); - final float py0 = prev.getY(0); - final float px1 = prev.getX(1); - final float py1 = prev.getY(1); - final float pvx = px1 - px0; - final float pvy = py1 - py0; - prevFingerDiffX = pvx; - prevFingerDiffY = pvy; - - // Current - final float cx0 = curr.getX(0); - final float cy0 = curr.getY(0); - final float cx1 = curr.getX(1); - final float cy1 = curr.getY(1); - final float cvx = cx1 - cx0; - final float cvy = cy1 - cy0; - currFingerDiffX = cvx; - currFingerDiffY = cvy; - focus = determineFocalPoint(curr); - } - - /** - * Return the current distance between the two pointers forming the gesture - * in progress. - * - * @return Distance between pointers in pixels. - */ - public float getCurrentSpan() { - if (currLen == -1) { - final float cvx = currFingerDiffX; - final float cvy = currFingerDiffY; - currLen = (float) Math.sqrt(cvx * cvx + cvy * cvy); - } - return currLen; - } - - /** - * Return the previous distance between the two pointers forming the gesture - * in progress. - * - * @return Previous distance between pointers in pixels. - */ - public float getPreviousSpan() { - if (prevLen == -1) { - final float pvx = prevFingerDiffX; - final float pvy = prevFingerDiffY; - prevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy); - } - return prevLen; - } - - /** - * MotionEvent has no getRawX(int) method; simulate it pending future API - * approval. - * - * @param event Motion Event - * @param pointerIndex Pointer Index - * @return Raw x value or 0 - */ - protected static float getRawX(MotionEvent event, int pointerIndex) { - float offset = event.getRawX() - event.getX(); - if (pointerIndex < event.getPointerCount()) { - return event.getX(pointerIndex) + offset; - } - return 0.0f; - } - - /** - * MotionEvent has no getRawY(int) method; simulate it pending future API - * approval. - * - * @param event Motion Event - * @param pointerIndex Pointer Index - * @return Raw y value or 0 - */ - protected static float getRawY(MotionEvent event, int pointerIndex) { - float offset = event.getRawY() - event.getY(); - if (pointerIndex < event.getPointerCount()) { - return event.getY(pointerIndex) + offset; - } - return 0.0f; - } - - /** - * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge - * of the user's hand is touching the screen, for example. - * - * @param event Motion Event - * @return {@code true} if is sloppy gesture, {@code false} if not - */ - protected boolean isSloppyGesture(MotionEvent event) { - // As orientation can change, query the metrics in touch down - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - float rightSlopEdge = metrics.widthPixels - edgeSlop; - float bottomSlopEdge = metrics.heightPixels - edgeSlop; - - final float edgeSlop = this.edgeSlop; - - final float x0 = event.getRawX(); - final float y0 = event.getRawY(); - final float x1 = getRawX(event, 1); - final float y1 = getRawY(event, 1); - - boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlopEdge - || y0 > bottomSlopEdge; - boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlopEdge - || y1 > bottomSlopEdge; - - if (p0sloppy && p1sloppy) { - return true; - } else if (p0sloppy) { - return true; - } else if (p1sloppy) { - return true; - } - return false; - } - - /** - * Determine (multi)finger focal point (a.k.a. center point between all - * fingers) - * - * @param motionEvent Motion Event - * @return PointF focal point - */ - public static PointF determineFocalPoint(MotionEvent motionEvent) { - // Number of fingers on screen - final int pCount = motionEvent.getPointerCount(); - float x = 0.0f; - float y = 0.0f; - - for (int i = 0; i < pCount; i++) { - x += motionEvent.getX(i); - y += motionEvent.getY(i); - } - - return new PointF(x / pCount, y / pCount); - } - - public float getFocusX() { - return focus.x; - } - - public float getFocusY() { - return focus.y; - } - -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java deleted file mode 100644 index cff2f086dc..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Do not use this package. Internal use only. - */ -package com.almeros.android.multitouch.gesturedetectors; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 60362dd2e9..6f263e4635 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -47,6 +47,31 @@ public class MapboxConstants { */ public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000; + /** + * Value by which the default rotation threshold will be increased when scaling + */ + public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f; + + /** + * Time within which user needs to lift fingers for velocity animation to start. + */ + public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L; + + /** + * Minimum angular velocity for rotation animation + */ + public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f; + + /** + * Maximum angular velocity for rotation animation + */ + public static final float MAXIMUM_ANGULAR_VELOCITY = 20f; + + /** + * Factor to calculate tilt change based on pixel change during shove gesture. + */ + public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f; + /** * The currently supported minimum zoom level. */ @@ -78,14 +103,14 @@ public class MapboxConstants { public static final double MINIMUM_DIRECTION = 0; /** - * The currently used minimun scale factor to clamp to when a quick zoom gesture occurs + * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs */ public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f; /** * The currently used maximum scale factor to clamp to when a quick zoom gesture occurs */ - public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f; + public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f; /** * Fragment Argument Key for MapboxMapOptions diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 8047e19809..5f5a10d0d0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -5,26 +5,31 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; +import android.os.Handler; import android.support.annotation.Nullable; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.ScaleGestureDetectorCompat; -import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.InputDevice; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; - -import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import android.view.animation.DecelerateInterpolator; + +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.MultiFingerTapGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.android.telemetry.Event; import com.mapbox.android.telemetry.MapEventFactory; import com.mapbox.android.telemetry.MapState; +import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.utils.MathUtils; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION; @@ -32,24 +37,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE /** * Manages gestures events on a MapView. - *

- * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors. - *

*/ final class MapGestureDetector { private final Transform transform; private final Projection projection; private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; - private GestureDetectorCompat gestureDetector; - private ScaleGestureDetector scaleGestureDetector; - private RotateGestureDetector rotateGestureDetector; - private ShoveGestureDetector shoveGestureDetector; - // deprecated map touch API private MapboxMap.OnMapClickListener onMapClickListener; private MapboxMap.OnMapLongClickListener onMapLongClickListener; @@ -69,43 +65,73 @@ final class MapGestureDetector { private final CopyOnWriteArrayList onScrollListenerList = new CopyOnWriteArrayList<>(); - private PointF focalPoint; + private final CopyOnWriteArrayList onMoveListenerList + = new CopyOnWriteArrayList<>(); - private boolean twoTap; - private boolean quickZoom; - private boolean tiltGestureOccurred; - private boolean scrollGestureOccurred; + private final CopyOnWriteArrayList onRotateListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onScaleListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList onShoveListenerList + = new CopyOnWriteArrayList<>(); + + /** + * User-set focal point. + */ + private PointF focalPoint; - private boolean scaleGestureOccurred; - private boolean recentScaleGestureOccurred; - private long scaleBeginTime; + private AndroidGesturesManager gesturesManager; + private boolean executeDoubleTap; - private boolean scaleAnimating; - private boolean rotateAnimating; + private Animator scaleAnimator; + private Animator rotateAnimator; + private final List scheduledAnimators = new ArrayList<>(); - private VelocityTracker velocityTracker; - private boolean wasZoomingIn; - private boolean wasClockwiseRotating; - private boolean rotateGestureOccurred; + /** + * Cancels scheduled velocity animations if user doesn't lift fingers within + * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT} + */ + private Handler animationsTimeoutHandler = new Handler(); MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, - TrackingSettings trackingSettings, AnnotationManager annotationManager, - CameraChangeDispatcher cameraChangeDispatcher) { + AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) { this.annotationManager = annotationManager; this.transform = transform; this.projection = projection; this.uiSettings = uiSettings; - this.trackingSettings = trackingSettings; this.cameraChangeDispatcher = cameraChangeDispatcher; - // Touch gesture detectors + // Checking for context != null for testing purposes if (context != null) { - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + gesturesManager = new AndroidGesturesManager(context); + + Set shoveScaleSet = new HashSet<>(); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + + Set shoveRotateSet = new HashSet<>(); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE); + + Set ScaleLongPressSet = new HashSet<>(); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS); + + gesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet); + + gesturesManager.setStandardGestureListener(new StandardGestureListener()); + gesturesManager.setMoveGestureListener(new MoveGestureListener()); + gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity) + )); + gesturesManager.setRotateGestureListener(new RotateGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating), + context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity) + )); + gesturesManager.setShoveGestureListener(new ShoveGestureListener()); + gesturesManager.setMultiFingerTapGestureListener(new TapGestureListener()); } } @@ -132,8 +158,9 @@ final class MapGestureDetector { /** * Get the current active gesture focal point. *

- * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point - * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}. + * This could be either the user provided focal point in + * {@link UiSettings#setFocalPoint(PointF)}or null. + * If it's null, gestures will use focal pointed returned by the detector. *

* * @return the current active gesture focal point. @@ -149,118 +176,79 @@ final class MapGestureDetector { * Forwards event to the related gesture detectors. *

* - * @param event the MotionEvent + * @param motionEvent the MotionEvent * @return True if touch event is handled */ - boolean onTouchEvent(MotionEvent event) { - // framework can return null motion events in edge cases #9432 - if (event == null) { + boolean onTouchEvent(MotionEvent motionEvent) { + // Framework can return null motion events in edge cases #9432 + if (motionEvent == null) { return false; } // Check and ignore non touch or left clicks - if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { return false; } - // Check two finger gestures first - scaleGestureDetector.onTouchEvent(event); - rotateGestureDetector.onTouchEvent(event); - shoveGestureDetector.onTouchEvent(event); + boolean result = gesturesManager.onTouchEvent(motionEvent); - // Handle two finger tap - switch (event.getActionMasked()) { + switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if (velocityTracker == null) { - velocityTracker = VelocityTracker.obtain(); - } else { - velocityTracker.clear(); - } - velocityTracker.addMovement(event); - // First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666 - recentScaleGestureOccurred = false; + cancelAnimators(); transform.setGestureInProgress(true); break; + case MotionEvent.ACTION_UP: + transform.setGestureInProgress(false); - case MotionEvent.ACTION_POINTER_DOWN: - // Second pointer down - twoTap = event.getPointerCount() == 2 - && uiSettings.isZoomGesturesEnabled(); - if (twoTap) { - // Confirmed 2nd Finger Down - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); - MapState twoFingerTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - twoFingerTap.setGesture(Events.TWO_FINGER_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, twoFingerTap)); - } + // Start all awaiting velocity animations + animationsTimeoutHandler.removeCallbacksAndMessages(null); + for (Animator animator : scheduledAnimators) { + animator.start(); } + scheduledAnimators.clear(); break; - case MotionEvent.ACTION_POINTER_UP: - // Second pointer up + case MotionEvent.ACTION_CANCEL: + scheduledAnimators.clear(); + transform.setGestureInProgress(false); break; + } - case MotionEvent.ACTION_UP: - // First pointer up - long tapInterval = event.getEventTime() - event.getDownTime(); - boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); - boolean inProgress = rotateGestureDetector.isInProgress() - || scaleGestureDetector.isInProgress() - || shoveGestureDetector.isInProgress(); - - if (twoTap && isTap && !inProgress) { - if (focalPoint != null) { - transform.zoom(false, focalPoint); - } else { - PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); - transform.zoom(false, focalPoint); - } - twoTap = false; - return true; - } - - // Scroll / Pan Has Stopped - if (scrollGestureOccurred) { - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(event.getX(), event.getY())); - MapState dragend = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_DRAGEND, dragend)); - } - scrollGestureOccurred = false; + return result; + } - if (!scaleAnimating && !rotateAnimating) { - cameraChangeDispatcher.onCameraIdle(); - } - } + void cancelAnimators() { + if (scaleAnimator != null) { + scaleAnimator.cancel(); + } + if (rotateAnimator != null) { + rotateAnimator.cancel(); + } - twoTap = false; - transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); - } - velocityTracker = null; - break; + animationsTimeoutHandler.removeCallbacksAndMessages(null); + scheduledAnimators.clear(); + } - case MotionEvent.ACTION_CANCEL: - twoTap = false; - transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); - } - velocityTracker = null; - break; - case MotionEvent.ACTION_MOVE: - if (velocityTracker != null) { - velocityTracker.addMovement(event); - velocityTracker.computeCurrentVelocity(1000); - } - break; + /** + * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed. + */ + private Runnable cancelAnimatorsRunnable = new Runnable() { + @Override + public void run() { + cancelAnimators(); } + }; - return gestureDetector.onTouchEvent(event); + /** + * Schedules a velocity animator to be executed when user lift fingers, + * unless canceled by the {@link #cancelAnimatorsRunnable}. + * + * @param animator animator ot be scheduled + */ + private void scheduleAnimator(Animator animator) { + scheduledAnimators.add(animator); + animationsTimeoutHandler.removeCallbacksAndMessages(null); + animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT); } /** @@ -269,7 +257,7 @@ final class MapGestureDetector { * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. *

* - * @param event The MotionEvent occured + * @param event The MotionEvent occurred * @return True is the event is handled */ boolean onGenericMotionEvent(MotionEvent event) { @@ -291,7 +279,7 @@ final class MapGestureDetector { float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); // Scale the map by the appropriate power of two factor - transform.zoomBy(scrollDist, event.getX(), event.getY()); + transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY())); return true; @@ -305,61 +293,14 @@ final class MapGestureDetector { return false; } - /** - * Responsible for handling one finger gestures. - */ - private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener { - + private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener { @Override - public boolean onDown(MotionEvent event) { - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) { - return false; - } - - switch (e.getAction()) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_MOVE: - break; - case MotionEvent.ACTION_UP: - if (quickZoom) { - cameraChangeDispatcher.onCameraIdle(); - quickZoom = false; - break; - } - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - // Single finger double tap - if (focalPoint != null) { - // User provided focal point - transform.zoom(true, focalPoint); - } else { - // Zoom in on gesture - transform.zoom(true, new PointF(e.getX(), e.getY())); - } - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(e.getX(), e.getY())); - MapState doubleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - doubleTap.setGesture(Events.DOUBLE_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, doubleTap)); - } - break; - } - + public boolean onDown(MotionEvent motionEvent) { return true; } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { - // Cancel any animation transform.cancelTransitions(); return true; } @@ -378,31 +319,51 @@ final class MapGestureDetector { notifyOnMapClickListeners(tapPoint); } - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(motionEvent.getX(), motionEvent.getY())); - MapState singleTap = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - singleTap.setGesture(Events.SINGLE_TAP); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, singleTap)); - } + sendTelemetryEvent(Events.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); return true; } @Override - public void onLongPress(MotionEvent motionEvent) { - PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + public boolean onDoubleTapEvent(MotionEvent motionEvent) { + int action = motionEvent.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + executeDoubleTap = true; + } + if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { + if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - if (!quickZoom) { - notifyOnMapLongClickListeners(longClickPoint); + // Single finger double tap + if (focalPoint != null) { + // User provided focal point + transform.zoomIn(focalPoint); + } else { + // Zoom in on gesture + transform.zoomIn(new PointF(motionEvent.getX(), motionEvent.getY())); + } + + sendTelemetryEvent(Events.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); + + return true; } + return super.onDoubleTapEvent(motionEvent); + } + + @Override + public void onLongPress(MotionEvent motionEvent) { + PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + notifyOnMapLongClickListeners(longClickPoint); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) { + if ((!uiSettings.isScrollGesturesEnabled())) { // don't allow a fling is scroll is disabled - // and ignore when a scale gesture has occurred return false; } @@ -415,11 +376,7 @@ final class MapGestureDetector { return false; } - trackingSettings.resetTrackingModesIfRequired(true, false, false); - - // cancel any animation transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); // tilt results in a bigger translation, limiting input for #5281 @@ -435,230 +392,149 @@ final class MapGestureDetector { transform.moveBy(offsetX, offsetY, animationTime); notifyOnFlingListeners(); + return true; } + } - // Called for drags + private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener { @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + public boolean onMoveBegin(MoveGestureDetector detector) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } - if (tiltGestureOccurred) { - return false; - } + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - if (!scrollGestureOccurred) { - scrollGestureOccurred = true; + sendTelemetryEvent(Events.PAN, detector.getFocalPoint()); - // Cancel any animation - if (!scaleGestureOccurred) { - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } + notifyOnMoveBeginListeners(detector); - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(e1.getX(), e1.getY())); - MapState pan = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pan.setGesture(Events.PAN); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pan)); - } - } + return true; + } - // reset tracking if needed - trackingSettings.resetTrackingModesIfRequired(true, false, false); + @Override + public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); notifyOnScrollListeners(); + notifyOnMoveListeners(detector); return true; } - } - - void notifyOnMapClickListeners(PointF tapPoint) { - // deprecated API - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); - } - // new API - for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { - listener.onMapClick(projection.fromScreenLocation(tapPoint)); + @Override + public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + notifyOnMoveEndListeners(detector); } } - void notifyOnMapLongClickListeners(PointF longClickPoint) { - // deprecated API - if (onMapLongClickListener != null) { - onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); - } + private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { - // new API - for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { - listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); - } - } + private final float minimumVelocity; - void notifyOnFlingListeners() { - // deprecated API - if (onFlingListener != null) { - onFlingListener.onFling(); - } + private PointF scaleFocalPoint; + private boolean quickZoom; - // new API - for (MapboxMap.OnFlingListener listener : onFlingListenerList) { - listener.onFling(); + ScaleGestureListener(float minimumVelocity) { + this.minimumVelocity = minimumVelocity; } - } - void notifyOnScrollListeners() { - //deprecated API - if (onScrollListener != null) { - onScrollListener.onScroll(); - } - - // new API - for (MapboxMap.OnScrollListener listener : onScrollListenerList) { - listener.onScroll(); - } - } - - /** - * Responsible for handling two finger gestures and double-tap drag gestures. - */ - private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { - - private static final int ANIMATION_TIME_MULTIPLIER = 77; - private static final double ZOOM_DISTANCE_DIVIDER = 5; - - private float scaleFactor = 1.0f; - private PointF scalePointBegin; - - // Called when two fingers first touch the screen @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { + public boolean onScaleBegin(StandardScaleGestureDetector detector) { if (!uiSettings.isZoomGesturesEnabled()) { return false; } - recentScaleGestureOccurred = true; - scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); - scaleBeginTime = detector.getEventTime(); - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState pinch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pinch.setGesture(Events.PINCH); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pinch)); + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + quickZoom = detector.getPointersCount() == 1; + if (quickZoom) { + // when quickzoom, dismiss double tap and disable move gesture + executeDoubleTap = false; + gesturesManager.getMoveGestureDetector().setEnabled(false); } + + // increase rotate angle threshold when scale is detected first + gesturesManager.getRotateGestureDetector().setAngleThreshold( + gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold() + + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING + ); + + // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd() + setScaleFocalPoint(detector); + + sendTelemetryEvent(Events.PINCH, scaleFocalPoint); + + notifyOnScaleBeginListeners(detector); + return true; } - // Called each time a finger moves - // Called for pinch zooms and quickzooms/quickscales @Override - public boolean onScale(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return super.onScale(detector); - } + public boolean onScale(StandardScaleGestureDetector detector) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0; - if (tiltGestureOccurred) { - return false; - } - - // Ignore short touches in case it is a tap - // Also ignore small scales - long time = detector.getEventTime(); - long interval = time - scaleBeginTime; - if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } - - // If scale is large enough ignore a tap - scaleFactor *= detector.getScaleFactor(); - if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) { - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - scaleGestureOccurred = true; - } + setScaleFocalPoint(detector); - if (!scaleGestureOccurred) { - return false; - } + float scaleFactor = detector.getScaleFactor(); + double zoomBy = getNewZoom(scaleFactor, quickZoom); + transform.zoomBy(zoomBy, scaleFocalPoint); - // Gesture is a quickzoom if there aren't two fingers - if (!quickZoom && !twoTap) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } - quickZoom = !twoTap; + notifyOnScaleListeners(detector); - // make an assumption here; if the zoom center is specified by the gesture, it's NOT going - // to be in the center of the map. Therefore the zoom will translate the map center, so tracking - // should be disabled. - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); - // Scale the map - if (focalPoint != null) { - // arround user provided focal point - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y); - } else if (quickZoom) { - cameraChangeDispatcher.onCameraMove(); - // clamp scale factors we feed to core #7514 - float scaleFactor = detector.getScaleFactor(); - // around center map - double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); - boolean negative = zoomBy < 0; - zoomBy = MathUtils.clamp(Math.abs(zoomBy), - MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, - MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); - transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - recentScaleGestureOccurred = true; - } else { - // around gesture - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), - scalePointBegin.x, scalePointBegin.y); - } return true; } - // Called when fingers leave screen @Override - public void onScaleEnd(final ScaleGestureDetector detector) { - if (velocityTracker == null) { - return; - } + public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); - if (rotateGestureOccurred || quickZoom) { - reset(); - return; + if (quickZoom) { + //if quickzoom, re-enabling move gesture detector + gesturesManager.getMoveGestureDetector().setEnabled(true); } - double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity()); - if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) { - scaleAnimating = true; - double zoomAddition = calculateScale(velocityXY); + // resetting default angle threshold + gesturesManager.getRotateGestureDetector().setAngleThreshold( + gesturesManager.getRotateGestureDetector().getDefaultAngleThreshold() + ); + + float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); + if (velocityXY > minimumVelocity) { + double zoomAddition = calculateScale(velocityXY, detector.isScalingOut()); double currentZoom = transform.getRawZoom(); - long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER); - createScaleAnimator(currentZoom, zoomAddition, animationTime).start(); - } else if (!scaleAnimating) { - reset(); + long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4); + scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime); + scheduleAnimator(scaleAnimator); } + + notifyOnScaleEndListeners(detector); } - private void reset() { - scaleAnimating = false; - scaleGestureOccurred = false; - scaleBeginTime = 0; - scaleFactor = 1.0f; - cameraChangeDispatcher.onCameraIdle(); + private void setScaleFocalPoint(StandardScaleGestureDetector detector) { + if (focalPoint != null) { + // around user provided focal point + scaleFocalPoint = focalPoint; + } else if (quickZoom) { + // around center + scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + } else { + // around gesture + scaleFocalPoint = detector.getFocalPoint(); + } } - private double calculateScale(double velocityXY) { - double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER); - if (!wasZoomingIn) { + private double calculateScale(double velocityXY, boolean isScalingOut) { + double zoomAddition = (float) Math.log(velocityXY / 1000 + 1); + if (isScalingOut) { zoomAddition = -zoomAddition; } return zoomAddition; @@ -667,272 +543,387 @@ final class MapGestureDetector { private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) { ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); animator.setDuration(animationTime); - animator.setInterpolator(new FastOutSlowInInterpolator()); + animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin, 0, true); + transform.setZoom((Float) animation.getAnimatedValue(), scaleFocalPoint, 0); } }); + animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); } @Override public void onAnimationCancel(Animator animation) { - reset(); + transform.cancelTransitions(); } @Override public void onAnimationEnd(Animator animation) { - reset(); + cameraChangeDispatcher.onCameraIdle(); } }); return animator; } - } - /** - * Responsible for handling rotation gestures. - */ - private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { + private double getNewZoom(float scaleFactor, boolean quickZoom) { + double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); + if (quickZoom) { + // clamp scale factors we feed to core #7514 + boolean negative = zoomBy < 0; + zoomBy = MathUtils.clamp(Math.abs(zoomBy), + MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, + MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); + return negative ? -zoomBy : zoomBy; + } + return zoomBy; + } + } - private static final float ROTATE_INVOKE_ANGLE = 15.30f; - private static final float ROTATE_LIMITATION_ANGLE = 3.35f; - private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f; + private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { + private PointF rotateFocalPoint; + private final float minimumScaleSpanWhenRotating; + private final float minimumAngularVelocity; - private long beginTime = 0; - private boolean started = false; + RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity) { + this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating; + this.minimumAngularVelocity = minimumAngularVelocity; + } - // Called when two fingers first touch the screen @Override public boolean onRotateBegin(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled()) { + if (!uiSettings.isRotateGesturesEnabled()) { return false; } - // notify camera change listener + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - beginTime = detector.getEventTime(); - return true; - } + // when rotation starts, interrupting scale and increasing the threshold + // to make rotation without scaling easier + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating); + gesturesManager.getStandardScaleGestureDetector().interrupt(); - // Called each time one of the two fingers moves - // Called for rotation - @Override - public boolean onRotate(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) { - return false; - } + // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd() + setRotateFocalPoint(detector); - // If rotate is large enough ignore a tap - // Also is zoom already started, don't rotate - float angle = detector.getRotationDegreesDelta(); - if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState rotation = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - rotation.setGesture(Events.ROTATION); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, rotation)); - } - started = true; - } + sendTelemetryEvent(Events.ROTATION, rotateFocalPoint); - if (!started) { - return false; - } + notifyOnRotateBeginListeners(detector); - wasClockwiseRotating = detector.getRotationDegreesDelta() > 0; - if (scaleBeginTime != 0) { - rotateGestureOccurred = true; - } + return true; + } - // rotation constitutes translation of anything except the center of - // rotation, so cancel both location and bearing tracking if required - trackingSettings.resetTrackingModesIfRequired(true, true, false); + @Override + public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast, + float rotationDegreesSinceFirst) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + setRotateFocalPoint(detector); // Calculate map bearing value - double bearing = transform.getRawBearing() + angle; + double bearing = transform.getRawBearing() + rotationDegreesSinceLast; // Rotate the map - if (focalPoint != null) { - // User provided focal point - transform.setBearing(bearing, focalPoint.x, focalPoint.y); - } else { - // around gesture - transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); - } + transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y); + + notifyOnRotateListeners(detector); + return true; } - // Called when the fingers leave the screen @Override - public void onRotateEnd(RotateGestureDetector detector) { - long interval = detector.getEventTime() - beginTime; - if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) { - reset(); + public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) { + cameraChangeDispatcher.onCameraIdle(); + + // resetting default scale threshold values + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold( + gesturesManager.getStandardScaleGestureDetector().getDefaultSpanSinceStartThreshold()); + + if (Math.abs(angularVelocity) < minimumAngularVelocity) { return; } - double angularVelocity = calculateVelocityVector(detector); - if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) { - animateRotateVelocity(); - } else if (!rotateAnimating) { - reset(); - } - } + boolean negative = angularVelocity < 0; + angularVelocity = (float) Math.pow(angularVelocity, 2); + angularVelocity = MathUtils.clamp( + angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY); - private void reset() { - beginTime = 0; - started = false; - rotateAnimating = false; - rotateGestureOccurred = false; + long animationTime = (long) (Math.log(angularVelocity + 1) * 500); - if (!twoTap) { - cameraChangeDispatcher.onCameraIdle(); + if (negative) { + angularVelocity = -angularVelocity; } - } - private void animateRotateVelocity() { - rotateAnimating = true; - double currentRotation = transform.getRawBearing(); - double rotateAdditionDegrees = calculateVelocityInDegrees(); - createAnimator(currentRotation, rotateAdditionDegrees).start(); - } + rotateAnimator = createRotateAnimator(angularVelocity, animationTime); + scheduleAnimator(rotateAnimator); - private double calculateVelocityVector(RotateGestureDetector detector) { - return ((detector.getFocusX() * velocityTracker.getYVelocity()) - + (detector.getFocusY() * velocityTracker.getXVelocity())) - / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2)); + notifyOnRotateEndListeners(detector); } - private double calculateVelocityInDegrees() { - double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity()); - double angle = angleRadians / (Math.PI / 180); - if (angle <= 0) { - angle += 360; - } - - // limit the angle - angle = angle / ROTATE_LIMITATION_ANGLE; - - // correct direction - if (!wasClockwiseRotating) { - angle = -angle; + private void setRotateFocalPoint(RotateGestureDetector detector) { + if (focalPoint != null) { + // User provided focal point + rotateFocalPoint = focalPoint; + } else { + // around gesture + rotateFocalPoint = detector.getFocalPoint(); } - - return angle; } - private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) { - ValueAnimator animator = ValueAnimator.ofFloat( - (float) currentRotation, - (float) (currentRotation + rotateAdditionDegrees) - ); - animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION)); + private Animator createRotateAnimator(float angularVelocity, long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f); + animator.setDuration(animationTime); + animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - transform.setBearing((Float) animation.getAnimatedValue()); + transform.setBearing( + transform.getRawBearing() + (float) animation.getAnimatedValue(), + rotateFocalPoint.x, rotateFocalPoint.y, + 0L + ); } }); + animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); } @Override public void onAnimationCancel(Animator animation) { - reset(); + cameraChangeDispatcher.onCameraIdle(); } @Override public void onAnimationEnd(Animator animation) { - reset(); + cameraChangeDispatcher.onCameraIdle(); } }); + return animator; } } - /** - * Responsible for handling 2 finger shove gestures. - */ - private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { - - private long beginTime = 0; - private float totalDelta = 0.0f; - + private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener { @Override public boolean onShoveBegin(ShoveGestureDetector detector) { if (!uiSettings.isTiltGesturesEnabled()) { return false; } - // notify camera change listener + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + sendTelemetryEvent(Events.PITCH, detector.getFocalPoint()); + + // disabling move gesture during shove + gesturesManager.getMoveGestureDetector().setEnabled(false); + + notifyOnShoveBeginListeners(detector); + return true; } @Override - public void onShoveEnd(ShoveGestureDetector detector) { - beginTime = 0; - totalDelta = 0.0f; - tiltGestureOccurred = false; + public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + // Get tilt value (scale and clamp) + double pitch = transform.getTilt(); + pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast; + pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT); + + // Tilt the map + transform.setTilt(pitch); + + notifyOnShoveListeners(detector); + + return true; } @Override - public boolean onShove(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } + public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); - // Ignore short touches in case it is a tap - // Also ignore small tilt - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } + // re-enabling move gesture + gesturesManager.getMoveGestureDetector().setEnabled(true); - // If tilt is large enough ignore a tap - // Also if zoom already started, don't tilt - totalDelta += detector.getShovePixelsDelta(); - if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { - tiltGestureOccurred = true; - beginTime = detector.getEventTime(); - if (isZoomValid(transform)) { - MapEventFactory mapEventFactory = new MapEventFactory(); - LatLng latLng = projection.fromScreenLocation(new PointF(detector.getFocusX(), detector.getFocusY())); - MapState pitch = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); - pitch.setGesture(Events.PITCH); - Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, pitch)); - } - } + notifyOnShoveEndListeners(detector); + } + } - if (!tiltGestureOccurred) { + private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener { + @Override + public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) { + if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) { return false; } - // Get tilt value (scale and clamp) - double pitch = transform.getTilt(); - pitch -= 0.1 * detector.getShovePixelsDelta(); - pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch)); + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + if (focalPoint != null) { + transform.zoomOut(focalPoint); + } else { + transform.zoomOut(detector.getFocalPoint()); + } - // Tilt the map - transform.setTilt(pitch); return true; } } + private void sendTelemetryEvent(String eventType, PointF focalPoint) { + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(focalPoint); + MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + state.setGesture(eventType); + Events.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state)); + } + } + + private boolean isZoomValid(Transform transform) { + if (transform == null) { + return false; + } + double mapZoom = transform.getZoom(); + return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM; + } + + void notifyOnMapClickListeners(PointF tapPoint) { + // deprecated API + if (onMapClickListener != null) { + onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + + // new API + for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { + listener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + } + + void notifyOnMapLongClickListeners(PointF longClickPoint) { + // deprecated API + if (onMapLongClickListener != null) { + onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + + // new API + for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { + listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } + } + + void notifyOnFlingListeners() { + // deprecated API + if (onFlingListener != null) { + onFlingListener.onFling(); + } + + // new API + for (MapboxMap.OnFlingListener listener : onFlingListenerList) { + listener.onFling(); + } + } + + void notifyOnScrollListeners() { + //deprecated API + if (onScrollListener != null) { + onScrollListener.onScroll(); + } + + // new API + for (MapboxMap.OnScrollListener listener : onScrollListenerList) { + listener.onScroll(); + } + } + + void notifyOnMoveBeginListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveBegin(detector); + } + } + + void notifyOnMoveListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMove(detector); + } + } + + void notifyOnMoveEndListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveEnd(detector); + } + } + + void notifyOnRotateBeginListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateBegin(detector); + } + } + + void notifyOnRotateListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotate(detector); + } + } + + void notifyOnRotateEndListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateEnd(detector); + } + } + + void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleBegin(detector); + } + } + + void notifyOnScaleListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScale(detector); + } + } + + void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleEnd(detector); + } + } + + void notifyOnShoveBeginListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveBegin(detector); + } + } + + void notifyOnShoveListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShove(detector); + } + } + + void notifyOnShoveEndListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveEnd(detector); + } + } + void setOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { this.onMapClickListener = onMapClickListener; } @@ -981,14 +972,43 @@ final class MapGestureDetector { onScrollListenerList.remove(onScrollListener); } - private boolean isZoomValid(Transform transform) { - if (transform == null) { - return false; - } - double mapZoom = transform.getZoom(); - if (mapZoom < MapboxConstants.MINIMUM_ZOOM || mapZoom > MapboxConstants.MAXIMUM_ZOOM) { - return false; - } - return true; + void addOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.add(listener); + } + + void removeOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.remove(listener); + } + + void addOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.add(listener); + } + + void removeOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.remove(listener); + } + + void addOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.add(listener); + } + + void removeOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.remove(listener); + } + + void addShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.add(listener); + } + + void removeShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.remove(listener); + } + + AndroidGesturesManager getGesturesManager() { + return gesturesManager; + } + + void setGesturesManager(AndroidGesturesManager gesturesManager) { + this.gesturesManager = gesturesManager; } -} +} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java index d1f01a30f7..9bd9499fff 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java @@ -128,7 +128,7 @@ final class MapKeyListener { // Zoom out PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, focalPoint); + transform.zoomOut(focalPoint); return true; default: @@ -164,7 +164,7 @@ final class MapKeyListener { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + transform.zoomIn(focalPoint); return true; } @@ -219,7 +219,7 @@ final class MapKeyListener { if (currentTrackballLongPressTimeOut != null) { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + transform.zoomIn(focalPoint); } return true; @@ -261,7 +261,7 @@ final class MapKeyListener { if (!cancelled) { // Zoom out PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, pointF); + transform.zoomOut(pointF); // Ensure the up action is not run currentTrackballLongPressTimeOut = null; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 990c56cb51..90feb228ab 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -23,6 +23,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; +import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.telemetry.AppUserTurnstile; import com.mapbox.android.telemetry.Event; import com.mapbox.android.telemetry.MapEventFactory; @@ -42,8 +43,6 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -52,6 +51,9 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + import timber.log.Timber; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; @@ -149,7 +151,7 @@ public class MapView extends FrameLayout { focalPointInvalidator.addListener(createFocalPointChangeListener()); // callback for registering touch listeners - RegisterTouchListener registerTouchListener = new RegisterTouchListener(); + GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener(); // callback for zooming in the camera CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); @@ -184,7 +186,7 @@ public class MapView extends FrameLayout { mapCallback.attachMapboxMap(mapboxMap); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, annotationManager, cameraChangeDispatcher); mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); @@ -388,6 +390,7 @@ public class MapView extends FrameLayout { public void onStop() { if (mapboxMap != null) { // map was destroyed before it was started + mapGestureDetector.cancelAnimators(); mapboxMap.onStop(); } @@ -921,7 +924,7 @@ public class MapView extends FrameLayout { } } - private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { + private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener { @Override public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) { @@ -982,6 +985,56 @@ public class MapView extends FrameLayout { public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) { mapGestureDetector.removeOnFlingListener(listener); } + + @Override + public void onAddMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.addOnMoveListener(listener); + } + + @Override + public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.removeOnMoveListener(listener); + } + + @Override + public void onAddRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.addOnRotateListener(listener); + } + + @Override + public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.removeOnRotateListener(listener); + } + + @Override + public void onAddScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.addOnScaleListener(listener); + } + + @Override + public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.removeOnScaleListener(listener); + } + + @Override + public void onAddShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.addShoveListener(listener); + } + + @Override + public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.removeShoveListener(listener); + } + + @Override + public AndroidGesturesManager getGesturesManager() { + return mapGestureDetector.getGesturesManager(); + } + + @Override + public void setGesturesManager(AndroidGesturesManager gesturesManager) { + mapGestureDetector.setGesturesManager(gesturesManager); + } } private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener { @@ -1019,11 +1072,13 @@ public class MapView extends FrameLayout { } private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) { - if (focalPoint != null) { - transform.zoom(zoomIn, focalPoint); + if (focalPoint == null) { + focalPoint = new PointF(mapWidth / 2, mapHeight / 2); + } + if (zoomIn) { + transform.zoomIn(focalPoint); } else { - PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2); - transform.zoom(zoomIn, centerPoint); + transform.zoomOut(focalPoint); } } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 2fd9a9010c..cbd3842a02 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -16,6 +16,12 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.mapbox.android.core.location.LocationEngine; +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.geojson.Feature; import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; @@ -43,7 +49,6 @@ import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.android.core.location.LocationEngine; import java.lang.reflect.ParameterizedType; import java.util.HashMap; @@ -73,13 +78,13 @@ public final class MapboxMap { private final MyLocationViewSettings myLocationViewSettings; private final CameraChangeDispatcher cameraChangeDispatcher; - private final OnRegisterTouchListener onRegisterTouchListener; + private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; private MapboxMap.OnFpsChangedListener onFpsChangedListener; private PointF focalPoint; MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, - MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, + MyLocationViewSettings myLocationView, Projection projection, OnGesturesManagerInteractionListener listener, AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) { this.nativeMapView = map; this.uiSettings = ui; @@ -88,7 +93,7 @@ public final class MapboxMap { this.myLocationViewSettings = myLocationView; this.annotationManager = annotations.bind(this); this.transform = transform; - this.onRegisterTouchListener = listener; + this.onGesturesManagerInteractionListener = listener; this.cameraChangeDispatcher = cameraChangeDispatcher; } @@ -1882,12 +1887,11 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} instead. */ @Deprecated public void setOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onSetScrollListener(listener); + onGesturesManagerInteractionListener.onSetScrollListener(listener); } /** @@ -1895,10 +1899,9 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void addOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onAddScrollListener(listener); + onGesturesManagerInteractionListener.onAddScrollListener(listener); } /** @@ -1906,10 +1909,9 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void removeOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onRemoveScrollListener(listener); + onGesturesManagerInteractionListener.onRemoveScrollListener(listener); } /** @@ -1917,12 +1919,11 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. - * * @deprecated Use {@link #addOnFlingListener(OnFlingListener)} instead. */ @Deprecated public void setOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onSetFlingListener(listener); + onGesturesManagerInteractionListener.onSetFlingListener(listener); } /** @@ -1932,7 +1933,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onAddFlingListener(listener); + onGesturesManagerInteractionListener.onAddFlingListener(listener); } /** @@ -1942,7 +1943,98 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onRemoveFlingListener(listener); + onGesturesManagerInteractionListener.onRemoveFlingListener(listener); + } + + /** + * Adds a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void addOnMoveListener(OnMoveListener listener) { + onGesturesManagerInteractionListener.onAddMoveListener(listener); + } + + /** + * Removes a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void removeOnMoveListener(OnMoveListener listener) { + onGesturesManagerInteractionListener.onRemoveMoveListener(listener); + } + + /** + * Adds a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void addOnRotateListener(OnRotateListener listener) { + onGesturesManagerInteractionListener.onAddRotateListener(listener); + } + + /** + * Removes a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void removeOnRotateListener(OnRotateListener listener) { + onGesturesManagerInteractionListener.onRemoveRotateListener(listener); + } + + /** + * Adds a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void addOnScaleListener(OnScaleListener listener) { + onGesturesManagerInteractionListener.onAddScaleListener(listener); + } + + /** + * Removes a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void removeOnScaleListener(OnScaleListener listener) { + onGesturesManagerInteractionListener.onRemoveScaleListener(listener); + } + + /** + * Adds a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void addOnShoveListener(OnShoveListener listener) { + onGesturesManagerInteractionListener.onAddShoveListener(listener); + } + + /** + * Remove a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void removeOnShoveListener(OnShoveListener listener) { + onGesturesManagerInteractionListener.onRemoveShoveListener(listener); + } + + /** + * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s registered by the map. + * + * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events. + * @see mapbox-gestures-android library + */ + public void setGesturesManager(AndroidGesturesManager androidGesturesManager) { + onGesturesManagerInteractionListener.setGesturesManager(androidGesturesManager); + } + + /** + * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s registered by the map. + * + * @return Current gestures manager. + */ + public AndroidGesturesManager getGesturesManager() { + return onGesturesManagerInteractionListener.getGesturesManager(); } /** @@ -1950,12 +2042,11 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapClickListener(OnMapClickListener)} instead. */ @Deprecated public void setOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onSetMapClickListener(listener); + onGesturesManagerInteractionListener.onSetMapClickListener(listener); } /** @@ -1965,7 +2056,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onAddMapClickListener(listener); + onGesturesManagerInteractionListener.onAddMapClickListener(listener); } /** @@ -1975,7 +2066,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onRemoveMapClickListener(listener); + onGesturesManagerInteractionListener.onRemoveMapClickListener(listener); } /** @@ -1983,12 +2074,11 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapLongClickListener(OnMapLongClickListener)} instead. */ @Deprecated public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onSetMapLongClickListener(listener); + onGesturesManagerInteractionListener.onSetMapLongClickListener(listener); } /** @@ -1998,7 +2088,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onAddMapLongClickListener(listener); + onGesturesManagerInteractionListener.onAddMapLongClickListener(listener); } /** @@ -2008,7 +2098,7 @@ public final class MapboxMap { * To unset the callback, use null. */ public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onRemoveMapLongClickListener(listener); + onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener); } /** @@ -2267,7 +2357,9 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when the map is scrolled. * * @see MapboxMap#setOnScrollListener(OnScrollListener) + * @deprecated Use {@link OnMoveListener} instead. */ + @Deprecated public interface OnScrollListener { /** * Called when the map is scrolled. @@ -2275,6 +2367,58 @@ public final class MapboxMap { void onScroll(); } + /** + * Interface definition for a callback to be invoked when the map is moved. + * + * @see MapboxMap#addOnMoveListener(OnMoveListener) + */ + public interface OnMoveListener { + void onMoveBegin(MoveGestureDetector detector); + + void onMove(MoveGestureDetector detector); + + void onMoveEnd(MoveGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is rotated. + * + * @see MapboxMap#addOnRotateListener(OnRotateListener) + */ + public interface OnRotateListener { + void onRotateBegin(RotateGestureDetector detector); + + void onRotate(RotateGestureDetector detector); + + void onRotateEnd(RotateGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is scaled. + * + * @see MapboxMap#addOnScaleListener(OnScaleListener) + */ + public interface OnScaleListener { + void onScaleBegin(StandardScaleGestureDetector detector); + + void onScale(StandardScaleGestureDetector detector); + + void onScaleEnd(StandardScaleGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is tilted. + * + * @see MapboxMap#addOnShoveListener(OnShoveListener) + */ + public interface OnShoveListener { + void onShoveBegin(ShoveGestureDetector detector); + + void onShove(ShoveGestureDetector detector); + + void onShoveEnd(ShoveGestureDetector detector); + } + /** * Interface definition for a callback to be invoked when the camera changes position. * @@ -2377,7 +2521,7 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when a user registers an listener that is * related to touch and click events. */ - interface OnRegisterTouchListener { + interface OnGesturesManagerInteractionListener { void onSetMapClickListener(OnMapClickListener listener); void onAddMapClickListener(OnMapClickListener listener); @@ -2401,6 +2545,26 @@ public final class MapboxMap { void onAddFlingListener(OnFlingListener listener); void onRemoveFlingListener(OnFlingListener listener); + + void onAddMoveListener(OnMoveListener listener); + + void onRemoveMoveListener(OnMoveListener listener); + + void onAddRotateListener(OnRotateListener listener); + + void onRemoveRotateListener(OnRotateListener listener); + + void onAddScaleListener(OnScaleListener listener); + + void onRemoveScaleListener(OnScaleListener listener); + + void onAddShoveListener(OnShoveListener listener); + + void onRemoveShoveListener(OnShoveListener listener); + + AndroidGesturesManager getGesturesManager(); + + void setGesturesManager(AndroidGesturesManager gesturesManager); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 84a601039f..43c943a16f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -205,6 +205,8 @@ final class Transform implements MapView.OnMapChangedListener { // cancel ongoing transitions mapView.cancelTransitions(); + + cameraChangeDispatcher.onCameraIdle(); } @UiThread @@ -235,39 +237,37 @@ final class Transform implements MapView.OnMapChangedListener { return mapView.getZoom(); } - void zoom(boolean zoomIn, @NonNull PointF focalPoint) { + void zoomIn(@NonNull PointF focalPoint) { CameraPosition cameraPosition = invalidateCameraPosition(); if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1)); - setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); + int newZoom = (int) Math.round(cameraPosition.zoom + 1); + setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION); } } - void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) { + void zoomOut(@NonNull PointF focalPoint) { CameraPosition cameraPosition = invalidateCameraPosition(); if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition); - setZoom(newZoom, focalPoint, duration, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); + int newZoom = (int) Math.round(cameraPosition.zoom - 1); + setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION); } } + void zoomBy(double zoomAddition, @NonNull PointF focalPoint) { + setZoom(mapView.getZoom() + zoomAddition, focalPoint, 0); + } + void setZoom(double zoom, @NonNull PointF focalPoint) { - setZoom(zoom, focalPoint, 0, false); + setZoom(zoom, focalPoint, 0); } - void setZoom(double zoom, @NonNull PointF focalPoint, long duration, final boolean isAnimator) { + void setZoom(double zoom, @NonNull PointF focalPoint, long duration) { if (mapView != null) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @Override public void onMapChanged(int change) { if (change == MapView.REGION_DID_CHANGE_ANIMATED) { - if (!isAnimator) { + if (duration > 0) { cameraChangeDispatcher.onCameraIdle(); } mapView.removeOnMapChangedListener(this); @@ -361,10 +361,6 @@ final class Transform implements MapView.OnMapChangedListener { } } - void zoomBy(double z, float x, float y) { - mapView.setZoom(mapView.getZoom() + z, new PointF(x, y), 0); - } - void moveBy(double offsetX, double offsetY, long duration) { if (duration > 0) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 1c6a265587..00fc05cf6d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -6,4 +6,13 @@ 8dp 92dp 18dp + + + 150dp + + + 100dp + + + 0.025dp diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java index eeb00355bd..5de55f47c9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java @@ -2,8 +2,13 @@ package com.mapbox.mapboxsdk.maps; import android.graphics.PointF; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.mapboxsdk.geometry.LatLng; +import org.junit.Before; import org.junit.Test; import static org.mockito.Mockito.mock; @@ -13,16 +18,23 @@ import static org.mockito.Mockito.when; public class MapTouchListenersTest { - @Test - public void onMapClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); + private MapGestureDetector mapGestureDetector; + private LatLng latLng; + private PointF pointF; + + @Before + public void setUp() throws Exception { + latLng = new LatLng(); + pointF = new PointF(); Projection projection = mock(Projection.class); when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); + mapGestureDetector = new MapGestureDetector(null, + null, projection, null, null, null); + } + @Test + public void onMapClickListenerTest() throws Exception { MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class); mapGestureDetector.addOnMapClickListener(listener); mapGestureDetector.notifyOnMapClickListeners(pointF); @@ -35,14 +47,6 @@ public class MapTouchListenersTest { @Test public void onMapLongClickListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class); mapGestureDetector.addOnMapLongClickListener(listener); mapGestureDetector.notifyOnMapLongClickListeners(pointF); @@ -55,14 +59,6 @@ public class MapTouchListenersTest { @Test public void onFlingListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class); mapGestureDetector.addOnFlingListener(listener); mapGestureDetector.notifyOnFlingListeners(); @@ -75,14 +71,6 @@ public class MapTouchListenersTest { @Test public void onScrollListenerTest() throws Exception { - LatLng latLng = new LatLng(); - PointF pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - MapGestureDetector mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null, null); - MapboxMap.OnScrollListener listener = mock(MapboxMap.OnScrollListener.class); mapGestureDetector.addOnScrollListener(listener); mapGestureDetector.notifyOnScrollListeners(); @@ -92,4 +80,88 @@ public class MapTouchListenersTest { mapGestureDetector.notifyOnScrollListeners(); verify(listener, times(1)).onScroll(); } + + @Test + public void onMoveListenerTest() throws Exception { + MapboxMap.OnMoveListener listener = mock(MapboxMap.OnMoveListener.class); + MoveGestureDetector detector = mock(MoveGestureDetector.class); + mapGestureDetector.addOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + + mapGestureDetector.removeOnMoveListener(listener); + mapGestureDetector.notifyOnMoveBeginListeners(detector); + mapGestureDetector.notifyOnMoveListeners(detector); + mapGestureDetector.notifyOnMoveEndListeners(detector); + verify(listener, times(1)).onMoveBegin(detector); + verify(listener, times(1)).onMove(detector); + verify(listener, times(1)).onMoveEnd(detector); + } + + @Test + public void onRotateListenerTest() throws Exception { + MapboxMap.OnRotateListener listener = mock(MapboxMap.OnRotateListener.class); + RotateGestureDetector detector = mock(RotateGestureDetector.class); + mapGestureDetector.addOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + + mapGestureDetector.removeOnRotateListener(listener); + mapGestureDetector.notifyOnRotateBeginListeners(detector); + mapGestureDetector.notifyOnRotateListeners(detector); + mapGestureDetector.notifyOnRotateEndListeners(detector); + verify(listener, times(1)).onRotateBegin(detector); + verify(listener, times(1)).onRotate(detector); + verify(listener, times(1)).onRotateEnd(detector); + } + + @Test + public void onScaleListenerTest() throws Exception { + MapboxMap.OnScaleListener listener = mock(MapboxMap.OnScaleListener.class); + StandardScaleGestureDetector detector = mock(StandardScaleGestureDetector.class); + mapGestureDetector.addOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + + mapGestureDetector.removeOnScaleListener(listener); + mapGestureDetector.notifyOnScaleBeginListeners(detector); + mapGestureDetector.notifyOnScaleListeners(detector); + mapGestureDetector.notifyOnScaleEndListeners(detector); + verify(listener, times(1)).onScaleBegin(detector); + verify(listener, times(1)).onScale(detector); + verify(listener, times(1)).onScaleEnd(detector); + } + + @Test + public void onShoveListenerTest() throws Exception { + MapboxMap.OnShoveListener listener = mock(MapboxMap.OnShoveListener.class); + ShoveGestureDetector detector = mock(ShoveGestureDetector.class); + mapGestureDetector.addShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + + mapGestureDetector.removeShoveListener(listener); + mapGestureDetector.notifyOnShoveBeginListeners(detector); + mapGestureDetector.notifyOnShoveListeners(detector); + mapGestureDetector.notifyOnShoveEndListeners(detector); + verify(listener, times(1)).onShoveBegin(detector); + verify(listener, times(1)).onShove(detector); + verify(listener, times(1)).onShoveEnd(detector); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java index 5e9f94db28..d61947f00e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java @@ -23,7 +23,7 @@ public class MapboxMapTest { mock(TrackingSettings.class), mock(MyLocationViewSettings.class), mock(Projection.class), - mock(MapboxMap.OnRegisterTouchListener.class), + mock(MapboxMap.OnGesturesManagerInteractionListener.class), mock(AnnotationManager.class), mock(CameraChangeDispatcher.class)); } diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index b1b9a065ad..695cca3a29 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,24 +8,26 @@ ext { ] versions = [ - mapboxServices: '3.0.0-beta.2', + mapboxServices : '3.0.0-beta.2', mapboxTelemetry: '3.0.0-beta.1', - supportLib : '25.4.0', - espresso : '3.0.1', - testRunner : '1.0.1', - leakCanary : '1.5.1', - lost : '3.0.4', - junit : '4.12', - mockito : '2.10.0', - robolectric : '3.5.1', - timber : '4.5.1', - okhttp : '3.9.1' + mapboxGestures : '0.1.0-20180227.133736-12', + supportLib : '25.4.0', + espresso : '3.0.1', + testRunner : '1.0.1', + leakCanary : '1.5.1', + lost : '3.0.4', + junit : '4.12', + mockito : '2.10.0', + robolectric : '3.5.1', + timber : '4.5.1', + okhttp : '3.9.1' ] dependenciesList = [ mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}", mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}", mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}", + mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}@aar", // for testApp mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}", -- cgit v1.2.1 From ca17625f1ced08afed6767345470b96f902faa88 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 26 Feb 2018 13:22:15 -0800 Subject: [core,node] Pin 'nan' to ~2.8 to avoid Callback::Call deprecation. Alternative fix to #11288, until we figure out a proper async_hooks implementation. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fb62bc63ed..1ebad0bca4 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "^2.4.0", - "node-pre-gyp": "^0.6.36", + "nan": "~2.8", + "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, "devDependencies": { -- cgit v1.2.1 From aacd55c64b0bfdba457af5444a6965386aad6045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Wed, 21 Feb 2018 12:35:44 +0100 Subject: [core] continue loading style even if we mutate it When we load a stale style from cache, and the user immediately starts mutating it, we should continue loading the style so that we'll get a fresh copy of the data into our cache and avoid perpetually showing the stale style. --- src/mbgl/style/style_impl.cpp | 5 ----- test/map/map.test.cpp | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/mbgl/style/style_impl.cpp b/src/mbgl/style/style_impl.cpp index 3214c6316e..39e1c17722 100644 --- a/src/mbgl/style/style_impl.cpp +++ b/src/mbgl/style/style_impl.cpp @@ -53,11 +53,6 @@ void Style::Impl::loadURL(const std::string& url_) { url = url_; styleRequest = fileSource.request(Resource::style(url), [this](Response res) { - // Once we get a fresh style, or the style is mutated, stop revalidating. - if (res.isFresh() || mutated) { - styleRequest.reset(); - } - // Don't allow a loaded, mutated style to be overwritten with a new version. if (mutated && loaded) { return; diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 9358175297..9b7f0b5d58 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -252,7 +252,7 @@ TEST(Map, DoubleStyleLoad) { } TEST(Map, StyleFresh) { - // The map should not revalidate fresh styles. + // The map should continue to revalidate fresh styles. MapTest test; @@ -264,11 +264,11 @@ TEST(Map, StyleFresh) { response.expires = Timestamp::max(); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); } TEST(Map, StyleExpired) { - // The map should allow expired styles to be revalidated, so long as no mutations are made. + // The map should allow expired styles to be revalidated until we get a fresh style. using namespace std::chrono_literals; @@ -284,11 +284,22 @@ TEST(Map, StyleExpired) { test.fileSource.respond(Resource::Style, response); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Mutate layer. From now on, sending a response to the style won't overwrite it anymore, but + // we should continue to wait for a fresh response. test.map.getStyle().addLayer(std::make_unique("bg")); EXPECT_EQ(1u, test.fileSource.requests.size()); + // Send another expired response, and confirm that we didn't overwrite the style, but continue + // to wait for a fresh response. + test.fileSource.respond(Resource::Style, response); + EXPECT_EQ(1u, test.fileSource.requests.size()); + EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); + + // Send a fresh response, and confirm that we didn't overwrite the style, but continue to wait + // for a fresh response. + response.expires = util::now() + 1h; test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("bg")); } @@ -352,7 +363,7 @@ TEST(Map, StyleEarlyMutation) { response.data = std::make_shared(util::read_file("test/fixtures/api/water.json")); test.fileSource.respond(Resource::Style, response); - EXPECT_EQ(0u, test.fileSource.requests.size()); + EXPECT_EQ(1u, test.fileSource.requests.size()); EXPECT_NE(nullptr, test.map.getStyle().getLayer("water")); } -- cgit v1.2.1 From 6d2cbb0a403b9faada81a756afa111e35618500b Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Tue, 27 Feb 2018 12:04:20 -0500 Subject: [ios, macos] Fix a memory leak when creating an EAGLContext in MGLMapSnapshotter. (#11193) --- platform/darwin/src/headless_backend_eagl.mm | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/platform/darwin/src/headless_backend_eagl.mm b/platform/darwin/src/headless_backend_eagl.mm index 1daaeaf54c..f291c0805a 100644 --- a/platform/darwin/src/headless_backend_eagl.mm +++ b/platform/darwin/src/headless_backend_eagl.mm @@ -7,9 +7,13 @@ namespace mbgl { struct EAGLImpl : public HeadlessBackend::Impl { - EAGLImpl(EAGLContext* glContext_) : glContext(glContext_) { - [reinterpret_cast(glContext) retain]; - reinterpret_cast(glContext).multiThreaded = YES; + EAGLImpl() { + glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + + if (glContext == nil) { + throw std::runtime_error("Error creating GL context object"); + } + glContext.multiThreaded = YES; } ~EAGLImpl() { @@ -45,12 +49,9 @@ bool HeadlessBackend::hasDisplay() { } void HeadlessBackend::createContext() { - EAGLContext* glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - if (glContext == nil) { - throw std::runtime_error("Error creating GL context object"); - } - - impl.reset(new EAGLImpl(glContext)); + impl.reset(); + impl = std::make_unique(); + } } // namespace mbgl -- cgit v1.2.1 From b74930c5fcf6b2a7652436fec9bcab4e12a49669 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 27 Feb 2018 15:46:28 -0500 Subject: [ios] Improved heading indicator arrow visibility --- platform/ios/CHANGELOG.md | 4 ++++ platform/ios/src/MGLUserLocationHeadingArrowLayer.m | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7d23a3fbaf..e2052abb89 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## 3.7.6 + +* Improved the visibility of the heading indicator arrow. ([#11337](https://github.com/mapbox/mapbox-gl-native/pull/11337)) + ## 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)) diff --git a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m index 912ce30c35..d81cb5a09a 100644 --- a/platform/ios/src/MGLUserLocationHeadingArrowLayer.m +++ b/platform/ios/src/MGLUserLocationHeadingArrowLayer.m @@ -3,7 +3,7 @@ #import "MGLFaux3DUserLocationAnnotationView.h" #import "MGLGeometry.h" -const CGFloat MGLUserLocationHeadingArrowSize = 6; +const CGFloat MGLUserLocationHeadingArrowSize = 8; @implementation MGLUserLocationHeadingArrowLayer -- cgit v1.2.1 From 98470e242d2b030789125bdc84ac63a1de423ae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Thu, 22 Feb 2018 16:40:03 -0800 Subject: [doc] Updated link to NativeScript plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old URL has become a redirect to the plugin marketplace’s homepage. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e3e51785b..8815e506a7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Additional Mapbox GL Native–based libraries for **hybrid applications** are de | ---------------------------------------- | --------|-----|------------ | | [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | | [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik | -| [NativeScript](http://plugins.telerik.com/nativescript/plugin/mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | +| [NativeScript](https://market.nativescript.org/plugins/nativescript-mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | | [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin | If your platform or hybrid application framework isn’t listed here, consider embedding [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) using the standard Web capabilities on your platform. -- cgit v1.2.1 From 48744d51d8f460de3dc32ad4ef3e0ee12f80a149 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Tue, 27 Feb 2018 17:27:36 +0200 Subject: [test] Update Map.PrefetchTiles --- cmake/test-files.cmake | 1 + test/fixtures/map/prefetch/expected.png | Bin 2198 -> 0 bytes test/fixtures/map/prefetch/tile.png | Bin 0 -> 659 bytes test/fixtures/map/prefetch/tile_green.png | Bin 659 -> 0 bytes test/fixtures/map/prefetch/tile_red.png | Bin 659 -> 0 bytes test/map/map.test.cpp | 46 +++------------------------- test/map/prefetch.test.cpp | 40 ++++++++++++------------ test/src/mbgl/test/stub_map_observer.hpp | 49 ++++++++++++++++++++++++++++++ 8 files changed, 75 insertions(+), 61 deletions(-) delete mode 100644 test/fixtures/map/prefetch/expected.png create mode 100644 test/fixtures/map/prefetch/tile.png delete mode 100644 test/fixtures/map/prefetch/tile_green.png delete mode 100644 test/fixtures/map/prefetch/tile_red.png create mode 100644 test/src/mbgl/test/stub_map_observer.hpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index 790198a55e..2aadfa8a7f 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -104,6 +104,7 @@ set(MBGL_TEST_FILES test/src/mbgl/test/stub_file_source.hpp test/src/mbgl/test/stub_geometry_tile_feature.hpp test/src/mbgl/test/stub_layer_observer.hpp + test/src/mbgl/test/stub_map_observer.hpp test/src/mbgl/test/stub_render_source_observer.hpp test/src/mbgl/test/stub_style_observer.hpp test/src/mbgl/test/stub_tile_observer.hpp diff --git a/test/fixtures/map/prefetch/expected.png b/test/fixtures/map/prefetch/expected.png deleted file mode 100644 index e1111b37f7..0000000000 Binary files a/test/fixtures/map/prefetch/expected.png and /dev/null differ diff --git a/test/fixtures/map/prefetch/tile.png b/test/fixtures/map/prefetch/tile.png new file mode 100644 index 0000000000..553cd10cd1 Binary files /dev/null and b/test/fixtures/map/prefetch/tile.png differ diff --git a/test/fixtures/map/prefetch/tile_green.png b/test/fixtures/map/prefetch/tile_green.png deleted file mode 100644 index 553cd10cd1..0000000000 Binary files a/test/fixtures/map/prefetch/tile_green.png and /dev/null differ diff --git a/test/fixtures/map/prefetch/tile_red.png b/test/fixtures/map/prefetch/tile_red.png deleted file mode 100644 index 5fa561fb92..0000000000 Binary files a/test/fixtures/map/prefetch/tile_red.png and /dev/null differ diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 30c076ad89..f95e26fd82 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -23,45 +24,6 @@ using namespace mbgl; using namespace mbgl::style; using namespace std::literals::string_literals; -class StubMapObserver : public MapObserver { -public: - void onWillStartLoadingMap() final { - if (onWillStartLoadingMapCallback) { - onWillStartLoadingMapCallback(); - } - } - - void onDidFinishLoadingMap() final { - if (onDidFinishLoadingMapCallback) { - onDidFinishLoadingMapCallback(); - } - } - - void onDidFailLoadingMap(std::exception_ptr) final { - if (didFailLoadingMapCallback) { - didFailLoadingMapCallback(); - } - } - - void onDidFinishLoadingStyle() final { - if (didFinishLoadingStyleCallback) { - didFinishLoadingStyleCallback(); - } - } - - void onDidFinishRenderingFrame(RenderMode mode) final { - if (didFinishRenderingFrame) { - didFinishRenderingFrame(mode); - } - } - - std::function onWillStartLoadingMapCallback; - std::function onDidFinishLoadingMapCallback; - std::function didFailLoadingMapCallback; - std::function didFinishLoadingStyleCallback; - std::function didFinishRenderingFrame; -}; - template class MapTest { public: @@ -371,7 +333,7 @@ TEST(Map, MapLoadingSignal) { MapTest<> test; bool emitted = false; - test.observer.onWillStartLoadingMapCallback = [&]() { + test.observer.willStartLoadingMapCallback = [&]() { emitted = true; }; test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json")); @@ -381,7 +343,7 @@ TEST(Map, MapLoadingSignal) { TEST(Map, MapLoadedSignal) { MapTest<> test { 1, MapMode::Continuous }; - test.observer.onDidFinishLoadingMapCallback = [&]() { + test.observer.didFinishLoadingMapCallback = [&]() { test.runLoop.stop(); }; @@ -607,7 +569,7 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) { HeadlessFrontend frontend(pixelRatio, fileSource, threadPool); StubMapObserver observer; - observer.didFinishRenderingFrame = [&] (MapObserver::RenderMode) { + observer.didFinishRenderingFrameCallback = [&] (MapObserver::RenderMode) { // Start a timer that ends the test one second from now. If we are continuing to render // indefinitely, the timer will be constantly restarted and never trigger. Instead, the // emergency shutoff above will trigger, failing the test. diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp index 4c82b2c965..9b61224027 100644 --- a/test/map/prefetch.test.cpp +++ b/test/map/prefetch.test.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -17,34 +18,38 @@ using namespace mbgl; using namespace mbgl::style; using namespace std::literals::string_literals; +using namespace std::chrono_literals; TEST(Map, PrefetchTiles) { util::RunLoop runLoop; ThreadPool threadPool(4); StubFileSource fileSource; + + util::Timer emergencyShutoff; + emergencyShutoff.start(10s, 0s, [&] { + runLoop.stop(); + FAIL() << "Did not stop rendering"; + }); + + StubMapObserver observer; + observer.didFinishLoadingMapCallback = [&] () { + runLoop.stop(); + }; + HeadlessFrontend frontend { { 512, 512 }, 1, fileSource, threadPool }; - Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Static); + Map map(frontend, observer, frontend.getSize(), 1, fileSource, threadPool, MapMode::Continuous); std::vector tiles; fileSource.response = [&] (const Resource& res) -> optional { - Response response; + static std::string tile = util::read_file("test/fixtures/map/prefetch/tile.png"); auto zoom = std::stoi(res.url); tiles.push_back(zoom); - // Return a red tile for prefetched tiles or green to the actual tile. - // The end rendering result should be all green because the map is only - // considered fully rendered when only ideal tiles are shown. - if (zoom == int(map.getZoom()) + 1) { - response.data = std::make_shared( - util::read_file("test/fixtures/map/prefetch/tile_green.png")); - } else { - response.data = std::make_shared( - util::read_file("test/fixtures/map/prefetch/tile_red.png")); - } - - return { std::move(response) }; + Response response; + response.data = std::make_shared(tile); + return response; }; auto checkTilesForZoom = [&](int zoom, const std::vector& expected) { @@ -53,14 +58,11 @@ TEST(Map, PrefetchTiles) { // Force tile reloading. map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/empty.json")); map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/style.json")); - map.setLatLngZoom({ 40.726989, -73.992857 }, zoom); // Manhattan + runLoop.run(); - // Should always render the ideal tiles (i.e. a green map) - test::checkImage("test/fixtures/map/prefetch", frontend.render(map)); - + ASSERT_EQ(tiles.size(), expected.size()); ASSERT_TRUE(std::is_permutation(tiles.begin(), tiles.end(), expected.begin())); - ASSERT_FALSE(tiles.empty()); }; // Check defaults, should be 4. diff --git a/test/src/mbgl/test/stub_map_observer.hpp b/test/src/mbgl/test/stub_map_observer.hpp new file mode 100644 index 0000000000..1371577473 --- /dev/null +++ b/test/src/mbgl/test/stub_map_observer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include + +namespace mbgl { + +class StubMapObserver : public MapObserver { +public: + void onWillStartLoadingMap() final { + if (willStartLoadingMapCallback) { + willStartLoadingMapCallback(); + } + } + + void onDidFinishLoadingMap() final { + if (didFinishLoadingMapCallback) { + didFinishLoadingMapCallback(); + } + } + + void onDidFailLoadingMap(std::exception_ptr) final { + if (didFailLoadingMapCallback) { + didFailLoadingMapCallback(); + } + } + + void onDidFinishLoadingStyle() final { + if (didFinishLoadingStyleCallback) { + didFinishLoadingStyleCallback(); + } + } + + void onDidFinishRenderingFrame(RenderMode mode) final { + if (didFinishRenderingFrameCallback) { + didFinishRenderingFrameCallback(mode); + } + } + + std::function willStartLoadingMapCallback; + std::function didFinishLoadingMapCallback; + std::function didFailLoadingMapCallback; + std::function didFinishLoadingStyleCallback; + std::function didFinishRenderingFrameCallback; +}; + + +} // namespace mbgl -- cgit v1.2.1 From 20c79d9e71a660a6d3b4e377b033f0d68a5327fa Mon Sep 17 00:00:00 2001 From: Cameron Mace Date: Wed, 28 Feb 2018 14:38:26 -0500 Subject: upgraded telem version (#11338) * upgraded telem version * use telem method instead of Okhttp * use telem util in test --- .../src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java | 6 +++--- .../test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java | 5 ++--- platform/android/dependencies.gradle | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index caee493e6f..129e75965e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -1,6 +1,5 @@ package com.mapbox.mapboxsdk.http; - import android.content.Context; import android.content.pm.PackageInfo; import android.os.Build; @@ -27,9 +26,10 @@ import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import okhttp3.internal.Util; import timber.log.Timber; +import static com.mapbox.services.android.telemetry.utils.TelemetryUtils.toHumanReadableAscii; + import static android.util.Log.DEBUG; import static android.util.Log.INFO; import static android.util.Log.WARN; @@ -204,7 +204,7 @@ class HTTPRequest implements Callback { private String getUserAgent() { if (USER_AGENT_STRING == null) { - return USER_AGENT_STRING = Util.toHumanReadableAscii( + return USER_AGENT_STRING = toHumanReadableAscii( String.format("%s %s (%s) Android/%s (%s)", getApplicationIdentifier(), BuildConfig.MAPBOX_VERSION_STRING, diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java index 94a6dc2194..5b54496329 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java @@ -2,8 +2,7 @@ package com.mapbox.mapboxsdk.telemetry; import org.junit.Test; -import okhttp3.internal.Util; - +import static com.mapbox.services.android.telemetry.utils.TelemetryUtils.toHumanReadableAscii; import static junit.framework.Assert.assertEquals; public class HttpTransportTest { @@ -15,6 +14,6 @@ public class HttpTransportTest { final String asciiVersion = "Sveriges Fj?ll/1.0/1 MapboxEventsAndroid/4.0.0-SNAPSHOT"; assertEquals("asciiVersion and swedishUserAgent should match", asciiVersion, - Util.toHumanReadableAscii(swedishUserAgent)); + toHumanReadableAscii(swedishUserAgent)); } } diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index 541b3c5236..f3ec2f5a25 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -7,7 +7,7 @@ ext { versionCode = 11 versionName = "5.0.0" - mapboxServicesVersion = "2.2.9" + mapboxServicesVersion = "2.2.10" supportLibVersion = "25.4.0" espressoVersion = '3.0.1' testRunnerVersion = '1.0.1' -- cgit v1.2.1 From 7623ed99bcf8cd6a1034ffac6cdef87e2268762a Mon Sep 17 00:00:00 2001 From: osana Date: Mon, 19 Feb 2018 22:55:43 -0500 Subject: [android] jni clean up - missing a couple DeleteLocalRef --- platform/android/src/conversion/collection.hpp | 1 + platform/android/src/file_source.cpp | 5 ++++- platform/android/src/map/camera_position.cpp | 4 +++- platform/android/src/map/image.cpp | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp index 549121c7ef..2b953e73f4 100644 --- a/platform/android/src/conversion/collection.hpp +++ b/platform/android/src/conversion/collection.hpp @@ -28,6 +28,7 @@ inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray& array) { inline std::vector toVector(JNIEnv& env, jni::jarray& array) { std::vector vector; std::size_t len = jni::GetArrayLength(env, array); + vector.reserve(len); for (std::size_t i = 0; i < len; i++) { jni::jstring* jstr = reinterpret_cast(jni::GetObjectArrayElement(env, array, i)); diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 6a9d7badb0..612619a30b 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -124,8 +124,11 @@ jni::Class FileSource::ResourceTransformC std::string FileSource::ResourceTransformCallback::onURL(jni::JNIEnv& env, jni::Object callback, int kind, std::string url_) { static auto method = FileSource::ResourceTransformCallback::javaClass.GetMethod(env, "onURL"); auto url = jni::Make(env, url_); + url = callback.Call(env, method, kind, url); - return jni::Make(env, url); + auto urlStr = jni::Make(env, url); + jni::DeleteLocalRef(env, url); + return urlStr; } } // namespace android diff --git a/platform/android/src/map/camera_position.cpp b/platform/android/src/map/camera_position.cpp index 1fc5f9789f..01ffc6530b 100644 --- a/platform/android/src/map/camera_position.cpp +++ b/platform/android/src/map/camera_position.cpp @@ -33,7 +33,9 @@ mbgl::CameraOptions CameraPosition::getCameraOptions(jni::JNIEnv& env, jni::Obje static auto tilt = CameraPosition::javaClass.GetField(env, "tilt"); static auto zoom = CameraPosition::javaClass.GetField(env, "zoom"); - auto center = LatLng::getLatLng(env, position.Get(env, target)); + auto jtarget = position.Get(env, target); + auto center = LatLng::getLatLng(env, jtarget); + jni::DeleteLocalRef(env, jtarget); return mbgl::CameraOptions { center, diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp index 5f5c90eddd..52e0e0d255 100644 --- a/platform/android/src/map/image.cpp +++ b/platform/android/src/map/image.cpp @@ -16,7 +16,9 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object image) { auto width = image.Get(env, widthField); auto pixelRatio = image.Get(env, pixelRatioField); auto pixels = image.Get(env, bufferField); - auto name = jni::Make(env, image.Get(env, nameField)); + auto jName = image.Get(env, nameField); + auto name = jni::Make(env, jName); + jni::DeleteLocalRef(env, jName); jni::NullCheck(env, &pixels); std::size_t size = pixels.Length(env); -- cgit v1.2.1 From 0fe1219bfd5dc93f6cf256cb247f58496022d2c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Thu, 22 Feb 2018 10:01:48 +0100 Subject: [android] expose ImageSource coordinates setter (#11262) --- .../java/com/mapbox/mapboxsdk/style/sources/ImageSource.java | 11 +++++++++++ platform/android/src/style/sources/image_source.cpp | 8 +++++++- platform/android/src/style/sources/image_source.hpp | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java index 84e5e96fa4..b7679b5a16 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java @@ -124,6 +124,15 @@ public class ImageSource extends Source { return nativeGetUrl(); } + /** + * Updates the latitude and longitude of the four corners of the image + * + * @param latLngQuad latitude and longitude of the four corners of the image + */ + public void setCoordinates(LatLngQuad latLngQuad) { + nativeSetCoordinates(latLngQuad); + } + protected native void initialize(String layerId, LatLngQuad payload); protected native void nativeSetUrl(String url); @@ -132,6 +141,8 @@ public class ImageSource extends Source { protected native void nativeSetImage(Bitmap bitmap); + protected native void nativeSetCoordinates(LatLngQuad latLngQuad); + @Override protected native void finalize() throws Throwable; } diff --git a/platform/android/src/style/sources/image_source.cpp b/platform/android/src/style/sources/image_source.cpp index d46b367c53..e28a7862f8 100644 --- a/platform/android/src/style/sources/image_source.cpp +++ b/platform/android/src/style/sources/image_source.cpp @@ -43,6 +43,11 @@ namespace android { source.as()->setImage(Bitmap::GetImage(env, bitmap)); } + void ImageSource::setCoordinates(jni::JNIEnv& env, jni::Object coordinatesObject) { + source.as()->setCoordinates( + LatLngQuad::getLatLngArray(env, coordinatesObject)); + } + jni::Class ImageSource::javaClass; jni::jobject* ImageSource::createJavaPeer(jni::JNIEnv& env) { @@ -64,7 +69,8 @@ namespace android { "finalize", METHOD(&ImageSource::setURL, "nativeSetUrl"), METHOD(&ImageSource::getURL, "nativeGetUrl"), - METHOD(&ImageSource::setImage, "nativeSetImage") + METHOD(&ImageSource::setImage, "nativeSetImage"), + METHOD(&ImageSource::setCoordinates, "nativeSetCoordinates") ); } diff --git a/platform/android/src/style/sources/image_source.hpp b/platform/android/src/style/sources/image_source.hpp index 9787a7294f..c600580119 100644 --- a/platform/android/src/style/sources/image_source.hpp +++ b/platform/android/src/style/sources/image_source.hpp @@ -29,6 +29,7 @@ public: jni::String getURL(jni::JNIEnv&); void setImage(jni::JNIEnv&, jni::Object); + void setCoordinates(jni::JNIEnv&, jni::Object); jni::jobject* createJavaPeer(jni::JNIEnv&); -- cgit v1.2.1 From 54c3fcdec86a46649fa22af169d88fc377554157 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 19 Feb 2018 19:08:07 +0100 Subject: [android] - check if hosting Activity isn't finishing before showing an dialog --- .../com/mapbox/mapboxsdk/maps/AttributionDialogManager.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index 2956d864e6..48de768e64 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.maps; +import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; @@ -49,7 +50,17 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface. @Override public void onClick(View view) { attributionSet = new AttributionBuilder(mapboxMap).build(); - showAttributionDialog(); + + boolean isActivityFinishing = false; + if (context instanceof Activity) { + isActivityFinishing = ((Activity) context).isFinishing(); + } + + // check is hosting activity isn't finishing + // https://github.com/mapbox/mapbox-gl-native/issues/11238 + if (!isActivityFinishing) { + showAttributionDialog(); + } } private void showAttributionDialog() { -- cgit v1.2.1 From b00e92fa94bd2e23cf31e4244c1a9db9c7a462ea Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:24:04 +0200 Subject: [core] check opengl error state after custom layer invocations --- src/mbgl/renderer/layers/render_custom_layer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index adafd8583f..a429b8d82e 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace mbgl { @@ -46,11 +47,11 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) if (context != impl().context || !initialized) { //If the context changed, deinitialize the previous one before initializing the new one. if (context && !contextDestroyed && impl().deinitializeFn) { - impl().deinitializeFn(context); + MBGL_CHECK_ERROR(impl().deinitializeFn(context)); } context = impl().context; assert(impl().initializeFn); - impl().initializeFn(impl().context); + MBGL_CHECK_ERROR(impl().initializeFn(impl().context)); initialized = true; } @@ -75,7 +76,7 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) parameters.fieldOfView = state.getFieldOfView(); assert(impl().renderFn); - impl().renderFn(context, parameters); + MBGL_CHECK_ERROR(impl().renderFn(context, parameters)); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. -- cgit v1.2.1 From eec0db49e40f40e1545c8711cf8e394042862df3 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:33:15 +0200 Subject: [core] blacklist vao usage on mali t720 (sapphire 650) Avoids problems on (amongst others) Samsung Galaxy J3 --- src/mbgl/gl/context.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index d1a37a861a..4b77954d12 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -249,10 +249,17 @@ UniqueTexture Context::createTexture() { bool Context::supportsVertexArrays() const { static bool blacklisted = []() { - // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data const std::string renderer = reinterpret_cast(glGetString(GL_RENDERER)); + + Log::Info(Event::General, "GPU Identifier: %s", renderer.c_str()); + + // Blacklist Adreno 2xx, 3xx as it crashes on glBuffer(Sub)Data + // Blacklist ARM Mali-T720 (in some MT8163 chipsets) as it crashes on glBindVertexArray return renderer.find("Adreno (TM) 2") != std::string::npos - || renderer.find("Adreno (TM) 3") != std::string::npos; + || renderer.find("Adreno (TM) 3") != std::string::npos + || renderer.find("Mali-T720") != std::string::npos + || renderer.find("Sapphire 650") != std::string::npos; + }(); return !blacklisted && -- cgit v1.2.1 From c84670e683f2e58aaa74fe3c236f6368950e2dab Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:33:57 +0200 Subject: [android] custom layer example - fix fragment shader source for opengl es 2 phones --- platform/android/src/example_custom_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 1ed68d0835..4467cd5e23 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -6,7 +6,7 @@ #include static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; -static const GLchar * fragmentShaderSource = "uniform vec4 fill_color; void main() { gl_FragColor = fill_color; }"; +static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }"; class ExampleCustomLayer { public: -- cgit v1.2.1 From 7fc676ed417ab509374f59a1e081605495cadcb3 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Mon, 19 Feb 2018 18:34:32 +0200 Subject: [android] custom layer example - add error checking to debug issues more easily --- platform/android/src/example_custom_layer.cpp | 170 +++++++++++++++++++++----- 1 file changed, 142 insertions(+), 28 deletions(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 4467cd5e23..4d1954f095 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -2,8 +2,111 @@ #include #include - +#include #include +#include + +// DEBUGGING + +const char* stringFromError(GLenum err) { + switch (err) { + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + +#ifdef GL_TABLE_TOO_LARGE + case GL_TABLE_TOO_LARGE: + return "GL_TABLE_TOO_LARGE"; +#endif + +#ifdef GL_STACK_OVERFLOW + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; +#endif + +#ifdef GL_STACK_UNDERFLOW + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; +#endif + +#ifdef GL_CONTEXT_LOST + case GL_CONTEXT_LOST: + return "GL_CONTEXT_LOST"; +#endif + + default: + return "GL_UNKNOWN"; + } +} + +struct Error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +void checkError(const char *cmd, const char *file, int line) { + + GLenum err = GL_NO_ERROR; + if ((err = glGetError()) != GL_NO_ERROR) { + std::string message = std::string(cmd) + ": Error " + stringFromError(err); + + // Check for further errors + while ((err = glGetError()) != GL_NO_ERROR) { + message += ", "; + message += stringFromError(err); + } + + mbgl::Log::Error(mbgl::Event::General, message + " at " + file + ":" + mbgl::util::toString(line)); + throw Error(message + " at " + file + ":" + mbgl::util::toString(line)); + } +} + +#ifndef NDEBUG +#define GL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() noexcept(false) { checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }()) +#else +#define GL_CHECK_ERROR(cmd) (cmd) +#endif + +void checkLinkStatus(GLuint program) { + GLint isLinked = 0; + glGetProgramiv(program, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) { + GLint maxLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); + GLchar infoLog[maxLength]; + glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); + mbgl::Log::Info(mbgl::Event::General, &infoLog[0]); + throw Error(infoLog); + } + +} + +void checkCompileStatus(GLuint shader) { + GLint isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) { + GLint maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + GLchar errorLog[maxLength]; + glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); + mbgl::Log::Error(mbgl::Event::General, &errorLog[0]); + throw Error(errorLog); + } +} + +// /DEBUGGING static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }"; @@ -24,37 +127,48 @@ public: void initialize() { mbgl::Log::Info(mbgl::Event::General, "Initialize"); - program = glCreateProgram(); - vertexShader = glCreateShader(GL_VERTEX_SHADER); - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); - glCompileShader(vertexShader); - glAttachShader(program, vertexShader); - glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); - glCompileShader(fragmentShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - a_pos = glGetAttribLocation(program, "a_pos"); - fill_color = glGetUniformLocation(program, "fill_color"); - - GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 }; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW); + + // Debug info + int maxAttrib; + GL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib)); + mbgl::Log::Info(mbgl::Event::General, "Max vertex attributes: %i", maxAttrib); + + program = GL_CHECK_ERROR(glCreateProgram()); + vertexShader = GL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); + fragmentShader = GL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); + + GL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(vertexShader)); + checkCompileStatus(vertexShader); + GL_CHECK_ERROR(glAttachShader(program, vertexShader)); + GL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr)); + GL_CHECK_ERROR(glCompileShader(fragmentShader)); + checkCompileStatus(fragmentShader); + GL_CHECK_ERROR(glAttachShader(program, fragmentShader)); + GL_CHECK_ERROR(glLinkProgram(program)); + checkLinkStatus(program); + + a_pos = GL_CHECK_ERROR(glGetAttribLocation(program, "a_pos")); + fill_color = GL_CHECK_ERROR(glGetUniformLocation(program, "fill_color")); + + GLfloat background[] = { -1, -1, 1, -1, -1, 1, 1, 1 }; + GL_CHECK_ERROR(glGenBuffers(1, &buffer)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW)); } void render() { mbgl::Log::Info(mbgl::Event::General, "Render"); - glUseProgram(program); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glEnableVertexAttribArray(a_pos); - glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DEPTH_TEST); - glUniform4fv(fill_color, 1, color); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + GL_CHECK_ERROR(glUseProgram(program)); + GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); + GL_CHECK_ERROR(glEnableVertexAttribArray(a_pos)); + GL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL)); + GL_CHECK_ERROR(glDisable(GL_STENCIL_TEST)); + GL_CHECK_ERROR(glDisable(GL_DEPTH_TEST)); + GL_CHECK_ERROR(glUniform4fv(fill_color, 1, color)); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + } GLuint program = 0; -- cgit v1.2.1 From 4b32228cc2fbf7f6dc720edd32a17bc4c7f44071 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Thu, 22 Feb 2018 16:00:14 +0200 Subject: [android] custom layer example - remove dependencies on mbgl logging and string headers --- platform/android/src/example_custom_layer.cpp | 49 ++++++++++++++------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 4d1954f095..46fcd96ffa 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -1,13 +1,13 @@ #include #include - -#include -#include +#include +#include #include -#include // DEBUGGING +const char* LOG_TAG = "Custom Layer Example"; + const char* stringFromError(GLenum err) { switch (err) { case GL_INVALID_ENUM: @@ -58,16 +58,17 @@ void checkError(const char *cmd, const char *file, int line) { GLenum err = GL_NO_ERROR; if ((err = glGetError()) != GL_NO_ERROR) { - std::string message = std::string(cmd) + ": Error " + stringFromError(err); + std::ostringstream message; + message << cmd << ": Error " << stringFromError(err); // Check for further errors while ((err = glGetError()) != GL_NO_ERROR) { - message += ", "; - message += stringFromError(err); + message << ", " << stringFromError(err); } - mbgl::Log::Error(mbgl::Event::General, message + " at " + file + ":" + mbgl::util::toString(line)); - throw Error(message + " at " + file + ":" + mbgl::util::toString(line)); + message << " at " << file << ":" << line; + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, message.str().c_str()); + throw Error(message.str()); } } @@ -85,7 +86,7 @@ void checkLinkStatus(GLuint program) { glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); GLchar infoLog[maxLength]; glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); - mbgl::Log::Info(mbgl::Event::General, &infoLog[0]); + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &infoLog[0]); throw Error(infoLog); } @@ -101,7 +102,7 @@ void checkCompileStatus(GLuint shader) { // The maxLength includes the NULL character GLchar errorLog[maxLength]; glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]); - mbgl::Log::Error(mbgl::Event::General, &errorLog[0]); + __android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &errorLog[0]); throw Error(errorLog); } } @@ -114,7 +115,7 @@ static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; voi class ExampleCustomLayer { public: ~ExampleCustomLayer() { - mbgl::Log::Info(mbgl::Event::General, "~ExampleCustomLayer"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "~ExampleCustomLayer"); if (program) { glDeleteBuffers(1, &buffer); glDetachShader(program, vertexShader); @@ -126,12 +127,12 @@ public: } void initialize() { - mbgl::Log::Info(mbgl::Event::General, "Initialize"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize"); // Debug info int maxAttrib; GL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib)); - mbgl::Log::Info(mbgl::Event::General, "Max vertex attributes: %i", maxAttrib); + __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Max vertex attributes: %i", maxAttrib); program = GL_CHECK_ERROR(glCreateProgram()); vertexShader = GL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); @@ -158,7 +159,7 @@ public: } void render() { - mbgl::Log::Info(mbgl::Event::General, "Render"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render"); GL_CHECK_ERROR(glUseProgram(program)); GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer)); @@ -184,12 +185,12 @@ public: GLfloat ExampleCustomLayer::color[] = { 0.0f, 1.0f, 0.0f, 1.0f }; jlong JNICALL nativeCreateContext(JNIEnv*, jobject) { - mbgl::Log::Info(mbgl::Event::General, "nativeCreateContext"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeCreateContext"); return reinterpret_cast(new ExampleCustomLayer()); } void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) { - mbgl::Log::Info(mbgl::Event::General, "nativeSetColor: %.2f, %.2f, %.2f, %.2f", red, green, blue, alpha); + __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "nativeSetColor: %.2f, %.2f, %.2f, %.2f", red, green, blue, alpha); ExampleCustomLayer::color[0] = red; ExampleCustomLayer::color[1] = green; ExampleCustomLayer::color[2] = blue; @@ -197,26 +198,28 @@ void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat b } void nativeInitialize(void *context) { - mbgl::Log::Info(mbgl::Event::General, "nativeInitialize"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeInitialize"); reinterpret_cast(context)->initialize(); } void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& /*parameters*/) { - mbgl::Log::Info(mbgl::Event::General, "nativeRender"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeRender"); reinterpret_cast(context)->render(); } void nativeContextLost(void */*context*/) { - mbgl::Log::Info(mbgl::Event::General, "nativeContextLost"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeContextLost"); } -void nativeDenitialize(void *context) { - mbgl::Log::Info(mbgl::Event::General, "nativeDeinitialize"); + +void nativeDeinitialize(void *context) { + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeDeinitialize"); delete reinterpret_cast(context); } extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { - mbgl::Log::Info(mbgl::Event::General, "OnLoad"); + __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "OnLoad"); + JNIEnv *env = nullptr; vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); -- cgit v1.2.1 From 90cce8d9ce09ccbd828341e924b94caaf8f30899 Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Mon, 19 Feb 2018 10:25:11 -0500 Subject: [android] incorrect latlngBounds in the VisibleRegion with map is rotated smallest bounding box for 4 points cannot (#11226) be created using LatLngBounds.fromLatLngs() as the order matters in that method and that does not work for rotated map --- .../src/main/java/com/mapbox/mapboxsdk/maps/Projection.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 16c73b1ca5..ae559189ad 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -104,11 +104,12 @@ public class Projection { LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - LatLngBounds.from( - topRight.getLatitude(), - topRight.getLongitude(), - bottomLeft.getLatitude(), - bottomLeft.getLongitude()) + new LatLngBounds.Builder() + .include(topRight) + .include(bottomLeft) + .include(bottomRight) + .include(topLeft) + .build() ); } -- cgit v1.2.1 From a8b8c5defac6e6c08ef161b32f52496148573d01 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 1 Mar 2018 10:06:48 +0100 Subject: [android] - update changelog for v5.5.0 --- platform/android/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 7827bc129f..d0d29eabf6 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,18 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.0 - March 1, 2018 + - TileJSON Bounds allows values inclusive of world extents [#11178](https://github.com/mapbox/mapbox-gl-native/pull/11178) + - LatLngBounds returned by VisibleRegion when map is rotated [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226) + - Custom Layer fixes & black list VAO on mali t720 [#11239](https://github.com/mapbox/mapbox-gl-native/pull/11239) + - Check if Activity isn't finishing before showing dialog [#11244](https://github.com/mapbox/mapbox-gl-native/pull/11244) + - Decouple MapPadding from overlain views [#11258](https://github.com/mapbox/mapbox-gl-native/pull/11258) + - Don't disable zoom button controller zooming with gesture disabled zoom [#11259](https://github.com/mapbox/mapbox-gl-native/pull/11259) + - Expose ImageSource coordinates setter [#11262](https://github.com/mapbox/mapbox-gl-native/pull/11262) + - Add missing DeleteLocalRefs [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) + - Continue loading style even if we mutate it [#11294](https://github.com/mapbox/mapbox-gl-native/pull/11294) + - Update telemetry version for OkHttp [#11338](https://github.com/mapbox/mapbox-gl-native/pull/11338) + ## 5.4.1 - February 9, 2018 - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) -- cgit v1.2.1 From 6e4846044531128b8984f351d367a7185325565d Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 1 Mar 2018 10:10:25 +0100 Subject: [android] - release android v5.5.0 --- circle.yml | 2 +- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index d76ab4a8ab..bb9392f395 100644 --- a/circle.yml +++ b/circle.yml @@ -355,7 +355,7 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "release-agua" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "release-android-v5.5.0" ]; then make run-android-upload-archives ; fi # ------------------------------------------------------------------------------ diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 8839f21d8f..36252fc90e 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.4.2-SNAPSHOT +VERSION_NAME=5.5.0 POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 72393707d34fd7c4c0fa81b66b40805f29411aed Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 1 Mar 2018 10:44:29 +0100 Subject: [android] - fix custom layer example --- platform/android/src/example_custom_layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 46fcd96ffa..be8e830412 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -249,7 +249,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { env->SetStaticLongField(customLayerClass, env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"), - reinterpret_cast(nativeDenitialize)); + reinterpret_cast(nativeDeinitialize)); return JNI_VERSION_1_6; } -- cgit v1.2.1 From 40039f6b430b4730aed8c52c28de3a855b8425ea Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 1 Mar 2018 11:25:18 +0100 Subject: [android] - reset release configuration --- circle.yml | 2 +- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index bb9392f395..d76ab4a8ab 100644 --- a/circle.yml +++ b/circle.yml @@ -355,7 +355,7 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "release-android-v5.5.0" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "release-agua" ]; then make run-android-upload-archives ; fi # ------------------------------------------------------------------------------ diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index 36252fc90e..f5dc431d40 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.5.0 +VERSION_NAME=5.5.1-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 9412f058f900f6ca0d1ea833f39eda2983b51259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Fri, 2 Mar 2018 11:04:02 +0100 Subject: [android] updated changelog to reflect 6.0.0-beta.3 changes (cherry picked from commit 7c0c884) --- platform/android/CHANGELOG.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index fa9b1d29cc..60d3dc6b70 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,10 +2,29 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## master - - - HeatmapLayer [#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046) - +## 6.0.0-beta.3 - March 2, 2018 + - Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) + - Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201) + - Fix UTF-8 encoding, add missing package-info.java files [#11261](https://github.com/mapbox/mapbox-gl-native/pull/11261) + - Rework expression api [#11210](https://github.com/mapbox/mapbox-gl-native/pull/11210) + - LatLngBounds fixes [#11333](https://github.com/mapbox/mapbox-gl-native/pull/11333), [#11307](https://github.com/mapbox/mapbox-gl-native/pull/11307), [#11308](https://github.com/mapbox/mapbox-gl-native/pull/11308), [#11309](https://github.com/mapbox/mapbox-gl-native/pull/11309), [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226) + - New gestures library [#11221](https://github.com/mapbox/mapbox-gl-native/pull/11221) + - Expose ImageSource coordinates setter [#11262](https://github.com/mapbox/mapbox-gl-native/pull/11262) + - Add heatmap color property [#11220](https://github.com/mapbox/mapbox-gl-native/pull/11220) + - Add support for mapzen terrarium raster-dem encoding [#11339](https://github.com/mapbox/mapbox-gl-native/pull/11339) + +## 5.5.0 - March 1, 2018 + - TileJSON Bounds allows values inclusive of world extents [#11178](https://github.com/mapbox/mapbox-gl-native/pull/11178) + - LatLngBounds returned by VisibleRegion when map is rotated [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226) + - Custom Layer fixes & black list VAO on mali t720 [#11239](https://github.com/mapbox/mapbox-gl-native/pull/11239) + - Check if Activity isn't finishing before showing dialog [#11244](https://github.com/mapbox/mapbox-gl-native/pull/11244) + - Decouple MapPadding from overlain views [#11258](https://github.com/mapbox/mapbox-gl-native/pull/11258) + - Don't disable zoom button controller zooming with gesture disabled zoom [#11259](https://github.com/mapbox/mapbox-gl-native/pull/11259) + - Expose ImageSource coordinates setter [#11262](https://github.com/mapbox/mapbox-gl-native/pull/11262) + - Add missing DeleteLocalRefs [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) + - Continue loading style even if we mutate it [#11294](https://github.com/mapbox/mapbox-gl-native/pull/11294) + - Update telemetry version for OkHttp [#11338](https://github.com/mapbox/mapbox-gl-native/pull/11338) + ## 6.0.0-beta.2 - February 13, 2018 - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) -- cgit v1.2.1 From b0a840e44f0291ee448178995ad2a9ff9b514a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 12 Feb 2018 11:31:07 +0100 Subject: [core] improve SQLite error logging --- platform/default/sqlite3.cpp | 82 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 2e08354fdf..ba7a1f6446 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -69,14 +69,84 @@ public: template using optional = std::experimental::optional; +static const char* codeToString(const int err) { + switch (err) { + case SQLITE_OK: return "SQLITE_OK"; + case SQLITE_ERROR: return "SQLITE_ERROR"; + case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; + case SQLITE_PERM: return "SQLITE_PERM"; + case SQLITE_ABORT: return "SQLITE_ABORT"; + case SQLITE_BUSY: return "SQLITE_BUSY"; + case SQLITE_LOCKED: return "SQLITE_LOCKED"; + case SQLITE_NOMEM: return "SQLITE_NOMEM"; + case SQLITE_READONLY: return "SQLITE_READONLY"; + case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; + case SQLITE_IOERR: return "SQLITE_IOERR"; + case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; + case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; + case SQLITE_FULL: return "SQLITE_FULL"; + case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; + case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; + case SQLITE_EMPTY: return "SQLITE_EMPTY"; + case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; + case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; + case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; + case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; + case SQLITE_MISUSE: return "SQLITE_MISUSE"; + case SQLITE_NOLFS: return "SQLITE_NOLFS"; + case SQLITE_AUTH: return "SQLITE_AUTH"; + case SQLITE_FORMAT: return "SQLITE_FORMAT"; + case SQLITE_RANGE: return "SQLITE_RANGE"; + case SQLITE_NOTADB: return "SQLITE_NOTADB"; + case SQLITE_NOTICE: return "SQLITE_NOTICE"; + case SQLITE_WARNING: return "SQLITE_WARNING"; + case SQLITE_ROW: return "SQLITE_ROW"; + case SQLITE_DONE: return "SQLITE_DONE"; + default: return ""; + } +} + static void errorLogCallback(void *, const int err, const char *msg) { - if (err == SQLITE_ERROR) { - mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else if (err == SQLITE_WARNING) { - mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err); - } else { - mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err); + auto severity = mbgl::EventSeverity::Info; + + switch (err) { + case SQLITE_ERROR: // Generic error + case SQLITE_INTERNAL: // Internal logic error in SQLite + case SQLITE_PERM: // Access permission denied + case SQLITE_ABORT: // Callback routine requested an abort + case SQLITE_BUSY: // The database file is locked + case SQLITE_LOCKED: // A table in the database is locked + case SQLITE_NOMEM: // A malloc() failed + case SQLITE_READONLY: // Attempt to write a readonly database + case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt( + case SQLITE_IOERR: // Some kind of disk I/O error occurred + case SQLITE_CORRUPT: // The database disk image is malformed + case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control() + case SQLITE_FULL: // Insertion failed because database is full + case SQLITE_CANTOPEN: // Unable to open the database file + case SQLITE_PROTOCOL: // Database lock protocol error + case SQLITE_EMPTY: // Internal use only + case SQLITE_SCHEMA: // The database schema changed + case SQLITE_TOOBIG: // String or BLOB exceeds size limit + case SQLITE_CONSTRAINT: // Abort due to constraint violation + case SQLITE_MISMATCH: // Data type mismatch + case SQLITE_MISUSE: // Library used incorrectly + case SQLITE_NOLFS: // Uses OS features not supported on host + case SQLITE_AUTH: // Authorization denied + case SQLITE_FORMAT: // Not used + case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range + case SQLITE_NOTADB: // File opened that is not a database file + severity = mbgl::EventSeverity::Error; + break; + case SQLITE_WARNING: // Warnings from sqlite3_log() + severity = mbgl::EventSeverity::Warning; + break; + case SQLITE_NOTICE: // Notifications from sqlite3_log() + default: + break; } + + mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err)); } const static bool sqliteVersionCheck __attribute__((unused)) = []() { -- cgit v1.2.1 From 5ca38bbde93d273a2a4febb42ff5de53b90e1350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 13 Feb 2018 14:52:49 +0100 Subject: [core] refactor SQLite error/status codes --- platform/default/mbgl/storage/offline_database.cpp | 6 +-- platform/default/sqlite3.hpp | 51 ++++++++++++++++++---- platform/qt/src/sqlite3.cpp | 10 ++--- test/storage/sqlite.test.cpp | 2 +- 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index b996dc4dd9..05e1a13fc0 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -28,7 +28,7 @@ OfflineDatabase::~OfflineDatabase() { statements.clear(); db.reset(); } catch (mapbox::sqlite::Exception& ex) { - Log::Error(Event::Database, ex.code, ex.what()); + Log::Error(Event::Database, (int)ex.code, ex.what()); } } @@ -57,13 +57,13 @@ void OfflineDatabase::ensureSchema() { removeExisting(); connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code != mapbox::sqlite::ResultCode::CantOpen && ex.code != mapbox::sqlite::ResultCode::NotADB) { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); throw; } try { - if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) { + if (ex.code == mapbox::sqlite::ResultCode::NotADB) { removeExisting(); } connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 82e3ceff6d..4080d33950 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -19,16 +19,49 @@ enum OpenFlag : int { PrivateCache = 0x00040000, }; -struct Exception : std::runtime_error { - enum Code : int { - OK = 0, - CANTOPEN = 14, - NOTADB = 26 - }; +enum class ResultCode : int { + OK = 0, + Error = 1, + Internal = 2, + Perm = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMem = 7, + ReadOnly = 8, + Interrupt = 9, + IOErr = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CantOpen = 14, + Protocol = 15, + Schema = 17, + TooBig = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NoLFS = 22, + Auth = 23, + Range = 25, + NotADB = 26 +}; - Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {} - Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {} - const int code = OK; +class Exception : public std::runtime_error { +public: + Exception(int err, const char* msg) + : std::runtime_error(msg), code(static_cast(err)) { + } + Exception(ResultCode err, const char* msg) + : std::runtime_error(msg), code(err) { + } + Exception(int err, const std::string& msg) + : std::runtime_error(msg), code(static_cast(err)) { + } + Exception(ResultCode err, const std::string& msg) + : std::runtime_error(msg), code(err) { + } + const ResultCode code = ResultCode::OK; }; class DatabaseImpl; diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index eb4a798043..09a3a16002 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -24,11 +24,11 @@ namespace mapbox { namespace sqlite { // https://www.sqlite.org/rescode.html#ok -static_assert(mbgl::underlying_type(Exception::OK) == 0, "error"); +static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error"); // https://www.sqlite.org/rescode.html#cantopen -static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error"); +static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error"); // https://www.sqlite.org/rescode.html#notadb -static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error"); +static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error"); void checkQueryError(const QSqlQuery& query) { QSqlError lastError = query.lastError(); @@ -57,7 +57,7 @@ void checkDatabaseOpenError(const QSqlDatabase &db) { // always returns -1 for `nativeErrorCode()` on database errors. QSqlError lastError = db.lastError(); if (lastError.type() != QSqlError::NoError) { - throw Exception { Exception::Code::CANTOPEN, "Error opening the database." }; + throw Exception { ResultCode::CantOpen, "Error opening the database." }; } } @@ -74,7 +74,7 @@ public: : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter()) { if (!QSqlDatabase::drivers().contains("QSQLITE")) { - throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." }; + throw Exception { ResultCode::CantOpen, "SQLite driver not found." }; } assert(!QSqlDatabase::contains(connectionName)); diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 36715a2fd0..7f33174c0d 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -33,6 +33,6 @@ TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { mapbox::sqlite::Database("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); FAIL(); } catch (mapbox::sqlite::Exception& ex) { - ASSERT_EQ(ex.code, mapbox::sqlite::Exception::Code::CANTOPEN); + ASSERT_EQ(ex.code, mapbox::sqlite::ResultCode::CantOpen); } } -- cgit v1.2.1 From 136e536159a1e22aa4a92c4e6463893600b809d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 6 Feb 2018 17:55:50 +0100 Subject: [core, qt] move self-resetting Statement/Query object to shared header --- platform/default/mbgl/storage/offline_database.cpp | 500 ++++++++++----------- platform/default/mbgl/storage/offline_database.hpp | 20 +- platform/default/sqlite3.cpp | 238 +++++----- platform/default/sqlite3.hpp | 50 ++- platform/qt/src/sqlite3.cpp | 198 ++++---- test/storage/offline_database.test.cpp | 47 +- test/storage/sqlite.test.cpp | 30 +- 7 files changed, 550 insertions(+), 533 deletions(-) diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 05e1a13fc0..4611e69f43 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -10,11 +10,6 @@ namespace mbgl { -OfflineDatabase::Statement::~Statement() { - stmt.reset(); - stmt.clearBindings(); -} - OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_) : path(std::move(path_)), maximumCacheSize(maximumCacheSize_) { @@ -92,9 +87,7 @@ void OfflineDatabase::ensureSchema() { } int OfflineDatabase::userVersion() { - auto stmt = db->prepare("PRAGMA user_version"); - stmt.run(); - return stmt.get(0); + return static_cast(getPragma("PRAGMA user_version")); } void OfflineDatabase::removeExisting() { @@ -135,14 +128,12 @@ void OfflineDatabase::migrateToVersion6() { transaction.commit(); } -OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) { +mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) { auto it = statements.find(sql); - - if (it != statements.end()) { - return Statement(*it->second); + if (it == statements.end()) { + it = statements.emplace(sql, std::make_unique(*db, sql)).first; } - - return Statement(*statements.emplace(sql, std::make_unique(db->prepare(sql))).first->second); + return *it->second; } optional OfflineDatabase::get(const Resource& resource) { @@ -209,41 +200,40 @@ std::pair OfflineDatabase::putInternal(const Resource& resource, } optional> OfflineDatabase::getResource(const Resource& resource) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE resources SET accessed = ?1 WHERE url = ?2"); - // clang-format on - - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, resource.url); - accessedStmt->run(); + // Update accessed timestamp used for LRU eviction. + { + mapbox::sqlite::Query accessedQuery{ getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2") }; + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, resource.url); + accessedQuery.run(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2 3 4 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM resources " - "WHERE url = ?"); + "WHERE url = ?") }; // clang-format on - stmt->bind(1, resource.url); + query.bind(1, resource.url); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get>(0); - response.expires = stmt->get>(1); - response.mustRevalidate = stmt->get(2); - response.modified = stmt->get>(3); + response.etag = query.get>(0); + response.expires = query.get>(1); + response.mustRevalidate = query.get(2); + response.modified = query.get>(3); - optional data = stmt->get>(4); + auto data = query.get>(4); if (!data) { response.noContent = true; - } else if (stmt->get(5)) { + } else if (query.get(5)) { response.data = std::make_shared(util::decompress(*data)); size = data->length(); } else { @@ -255,16 +245,13 @@ optional> OfflineDatabase::getResource(const Resou } optional OfflineDatabase::hasResource(const Resource& resource) { - // clang-format off - Statement stmt = getStatement("SELECT length(data) FROM resources WHERE url = ?"); - // clang-format on - - stmt->bind(1, resource.url); - if (!stmt->run()) { + mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") }; + query.bind(1, resource.url); + if (!query.run()) { return {}; } - return stmt->get>(0); + return query.get>(0); } bool OfflineDatabase::putResource(const Resource& resource, @@ -273,19 +260,19 @@ bool OfflineDatabase::putResource(const Resource& resource, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE resources " "SET accessed = ?1, " " expires = ?2, " " must_revalidate = ?3 " - "WHERE url = ?4 "); + "WHERE url = ?4 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, resource.url); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, resource.url); + notModifiedQuery.run(); return false; } @@ -296,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE resources " "SET kind = ?1, " " etag = ?2, " @@ -306,81 +293,83 @@ bool OfflineDatabase::putResource(const Resource& resource, " accessed = ?6, " " data = ?7, " " compressed = ?8 " - "WHERE url = ?9 "); + "WHERE url = ?9 ") }; // clang-format on - update->bind(1, int(resource.kind)); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, response.modified); - update->bind(6, util::now()); - update->bind(9, resource.url); + updateQuery.bind(1, int(resource.kind)); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, response.modified); + updateQuery.bind(6, util::now()); + updateQuery.bind(9, resource.url); if (response.noContent) { - update->bind(7, nullptr); - update->bind(8, false); + updateQuery.bind(7, nullptr); + updateQuery.bind(8, false); } else { - update->bindBlob(7, data.data(), data.size(), false); - update->bind(8, compressed); + updateQuery.bindBlob(7, data.data(), data.size(), false); + updateQuery.bind(8, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) "); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ") }; // clang-format on - insert->bind(1, resource.url); - insert->bind(2, int(resource.kind)); - insert->bind(3, response.etag); - insert->bind(4, response.expires); - insert->bind(5, response.mustRevalidate); - insert->bind(6, response.modified); - insert->bind(7, util::now()); + insertQuery.bind(1, resource.url); + insertQuery.bind(2, int(resource.kind)); + insertQuery.bind(3, response.etag); + insertQuery.bind(4, response.expires); + insertQuery.bind(5, response.mustRevalidate); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, util::now()); if (response.noContent) { - insert->bind(8, nullptr); - insert->bind(9, false); + insertQuery.bind(8, nullptr); + insertQuery.bind(9, false); } else { - insert->bindBlob(8, data.data(), data.size(), false); - insert->bind(9, compressed); + insertQuery.bindBlob(8, data.data(), data.size(), false); + insertQuery.bind(9, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } optional> OfflineDatabase::getTile(const Resource::TileData& tile) { - // clang-format off - Statement accessedStmt = getStatement( - "UPDATE tiles " - "SET accessed = ?1 " - "WHERE url_template = ?2 " - " AND pixel_ratio = ?3 " - " AND x = ?4 " - " AND y = ?5 " - " AND z = ?6 "); - // clang-format on + { + // clang-format off + mapbox::sqlite::Query accessedQuery{ getStatement( + "UPDATE tiles " + "SET accessed = ?1 " + "WHERE url_template = ?2 " + " AND pixel_ratio = ?3 " + " AND x = ?4 " + " AND y = ?5 " + " AND z = ?6 ") }; + // clang-format on - accessedStmt->bind(1, util::now()); - accessedStmt->bind(2, tile.urlTemplate); - accessedStmt->bind(3, tile.pixelRatio); - accessedStmt->bind(4, tile.x); - accessedStmt->bind(5, tile.y); - accessedStmt->bind(6, tile.z); - accessedStmt->run(); + accessedQuery.bind(1, util::now()); + accessedQuery.bind(2, tile.urlTemplate); + accessedQuery.bind(3, tile.pixelRatio); + accessedQuery.bind(4, tile.x); + accessedQuery.bind(5, tile.y); + accessedQuery.bind(6, tile.z); + accessedQuery.run(); + } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( // 0 1 2, 3, 4, 5 "SELECT etag, expires, must_revalidate, modified, data, compressed " "FROM tiles " @@ -388,31 +377,31 @@ optional> OfflineDatabase::getTile(const Resource: " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + query.bind(1, tile.urlTemplate); + query.bind(2, tile.pixelRatio); + query.bind(3, tile.x); + query.bind(4, tile.y); + query.bind(5, tile.z); - if (!stmt->run()) { + if (!query.run()) { return {}; } Response response; uint64_t size = 0; - response.etag = stmt->get>(0); - response.expires = stmt->get>(1); - response.mustRevalidate = stmt->get(2); - response.modified = stmt->get>(3); + response.etag = query.get>(0); + response.expires = query.get>(1); + response.mustRevalidate = query.get(2); + response.modified = query.get>(3); - optional data = stmt->get>(4); + optional data = query.get>(4); if (!data) { response.noContent = true; - } else if (stmt->get(5)) { + } else if (query.get(5)) { response.data = std::make_shared(util::decompress(*data)); size = data->length(); } else { @@ -425,27 +414,27 @@ optional> OfflineDatabase::getTile(const Resource: optional OfflineDatabase::hasTile(const Resource::TileData& tile) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query size{ getStatement( "SELECT length(data) " "FROM tiles " "WHERE url_template = ?1 " " AND pixel_ratio = ?2 " " AND x = ?3 " " AND y = ?4 " - " AND z = ?5 "); + " AND z = ?5 ") }; // clang-format on - stmt->bind(1, tile.urlTemplate); - stmt->bind(2, tile.pixelRatio); - stmt->bind(3, tile.x); - stmt->bind(4, tile.y); - stmt->bind(5, tile.z); + size.bind(1, tile.urlTemplate); + size.bind(2, tile.pixelRatio); + size.bind(3, tile.x); + size.bind(4, tile.y); + size.bind(5, tile.z); - if (!stmt->run()) { + if (!size.run()) { return {}; } - return stmt->get>(0); + return size.get>(0); } bool OfflineDatabase::putTile(const Resource::TileData& tile, @@ -454,7 +443,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, bool compressed) { if (response.notModified) { // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query notModifiedQuery{ getStatement( "UPDATE tiles " "SET accessed = ?1, " " expires = ?2, " @@ -463,18 +452,18 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?5 " " AND x = ?6 " " AND y = ?7 " - " AND z = ?8 "); + " AND z = ?8 ") }; // clang-format on - update->bind(1, util::now()); - update->bind(2, response.expires); - update->bind(3, response.mustRevalidate); - update->bind(4, tile.urlTemplate); - update->bind(5, tile.pixelRatio); - update->bind(6, tile.x); - update->bind(7, tile.y); - update->bind(8, tile.z); - update->run(); + notModifiedQuery.bind(1, util::now()); + notModifiedQuery.bind(2, response.expires); + notModifiedQuery.bind(3, response.mustRevalidate); + notModifiedQuery.bind(4, tile.urlTemplate); + notModifiedQuery.bind(5, tile.pixelRatio); + notModifiedQuery.bind(6, tile.x); + notModifiedQuery.bind(7, tile.y); + notModifiedQuery.bind(8, tile.z); + notModifiedQuery.run(); return false; } @@ -485,7 +474,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate); // clang-format off - Statement update = getStatement( + mapbox::sqlite::Query updateQuery{ getStatement( "UPDATE tiles " "SET modified = ?1, " " etag = ?2, " @@ -498,78 +487,75 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile, " AND pixel_ratio = ?9 " " AND x = ?10 " " AND y = ?11 " - " AND z = ?12 "); + " AND z = ?12 ") }; // clang-format on - update->bind(1, response.modified); - update->bind(2, response.etag); - update->bind(3, response.expires); - update->bind(4, response.mustRevalidate); - update->bind(5, util::now()); - update->bind(8, tile.urlTemplate); - update->bind(9, tile.pixelRatio); - update->bind(10, tile.x); - update->bind(11, tile.y); - update->bind(12, tile.z); + updateQuery.bind(1, response.modified); + updateQuery.bind(2, response.etag); + updateQuery.bind(3, response.expires); + updateQuery.bind(4, response.mustRevalidate); + updateQuery.bind(5, util::now()); + updateQuery.bind(8, tile.urlTemplate); + updateQuery.bind(9, tile.pixelRatio); + updateQuery.bind(10, tile.x); + updateQuery.bind(11, tile.y); + updateQuery.bind(12, tile.z); if (response.noContent) { - update->bind(6, nullptr); - update->bind(7, false); + updateQuery.bind(6, nullptr); + updateQuery.bind(7, false); } else { - update->bindBlob(6, data.data(), data.size(), false); - update->bind(7, compressed); + updateQuery.bindBlob(6, data.data(), data.size(), false); + updateQuery.bind(7, compressed); } - update->run(); - if (update->changes() != 0) { + updateQuery.run(); + if (updateQuery.changes() != 0) { transaction.commit(); return false; } // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) " - "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)"); + "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") }; // clang-format on - insert->bind(1, tile.urlTemplate); - insert->bind(2, tile.pixelRatio); - insert->bind(3, tile.x); - insert->bind(4, tile.y); - insert->bind(5, tile.z); - insert->bind(6, response.modified); - insert->bind(7, response.mustRevalidate); - insert->bind(8, response.etag); - insert->bind(9, response.expires); - insert->bind(10, util::now()); + insertQuery.bind(1, tile.urlTemplate); + insertQuery.bind(2, tile.pixelRatio); + insertQuery.bind(3, tile.x); + insertQuery.bind(4, tile.y); + insertQuery.bind(5, tile.z); + insertQuery.bind(6, response.modified); + insertQuery.bind(7, response.mustRevalidate); + insertQuery.bind(8, response.etag); + insertQuery.bind(9, response.expires); + insertQuery.bind(10, util::now()); if (response.noContent) { - insert->bind(11, nullptr); - insert->bind(12, false); + insertQuery.bind(11, nullptr); + insertQuery.bind(12, false); } else { - insert->bindBlob(11, data.data(), data.size(), false); - insert->bind(12, compressed); + insertQuery.bindBlob(11, data.data(), data.size(), false); + insertQuery.bind(12, compressed); } - insert->run(); + insertQuery.run(); transaction.commit(); return true; } std::vector OfflineDatabase::listRegions() { - // clang-format off - Statement stmt = getStatement( - "SELECT id, definition, description FROM regions"); - // clang-format on + mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") }; std::vector result; - while (stmt->run()) { + while (query.run()) { result.push_back(OfflineRegion( - stmt->get(0), - decodeOfflineRegionDefinition(stmt->get(1)), - stmt->get>(2))); + query.get(0), + decodeOfflineRegionDefinition(query.get(1)), + query.get>(2))); } return result; @@ -578,39 +564,37 @@ std::vector OfflineDatabase::listRegions() { OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "INSERT INTO regions (definition, description) " - "VALUES (?1, ?2) "); + "VALUES (?1, ?2) ") }; // clang-format on - stmt->bind(1, encodeOfflineRegionDefinition(definition)); - stmt->bindBlob(2, metadata); - stmt->run(); + query.bind(1, encodeOfflineRegionDefinition(definition)); + query.bindBlob(2, metadata); + query.run(); - return OfflineRegion(stmt->lastInsertRowId(), definition, metadata); + return OfflineRegion(query.lastInsertRowId(), definition, metadata); } OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "UPDATE regions SET description = ?1 " - "WHERE id = ?2"); + "WHERE id = ?2") }; // clang-format on - stmt->bindBlob(1, metadata); - stmt->bind(2, regionID); - stmt->run(); + query.bindBlob(1, metadata); + query.bind(2, regionID); + query.run(); return metadata; } void OfflineDatabase::deleteRegion(OfflineRegion&& region) { - // clang-format off - Statement stmt = getStatement( - "DELETE FROM regions WHERE id = ?"); - // clang-format on - - stmt->bind(1, region.getID()); - stmt->run(); + { + mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") }; + query.bind(1, region.getID()); + query.run(); + } evict(0); db->exec("PRAGMA incremental_vacuum"); @@ -656,7 +640,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { if (resource.kind == Resource::Kind::Tile) { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_tiles (region_id, tile_id) " "SELECT ?1, tiles.id " "FROM tiles " @@ -664,24 +648,24 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND pixel_ratio = ?3 " " AND x = ?4 " " AND y = ?5 " - " AND z = ?6 "); + " AND z = ?6 ") }; // clang-format on const Resource::TileData& tile = *resource.tileData; - insert->bind(1, regionID); - insert->bind(2, tile.urlTemplate); - insert->bind(3, tile.pixelRatio); - insert->bind(4, tile.x); - insert->bind(5, tile.y); - insert->bind(6, tile.z); - insert->run(); - - if (insert->changes() == 0) { + insertQuery.bind(1, regionID); + insertQuery.bind(2, tile.urlTemplate); + insertQuery.bind(3, tile.pixelRatio); + insertQuery.bind(4, tile.x); + insertQuery.bind(5, tile.y); + insertQuery.bind(6, tile.z); + insertQuery.run(); + + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_tiles, tiles " "WHERE region_id != ?1 " @@ -690,58 +674,54 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) { " AND x = ?4 " " AND y = ?5 " " AND z = ?6 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, tile.urlTemplate); - select->bind(3, tile.pixelRatio); - select->bind(4, tile.x); - select->bind(5, tile.y); - select->bind(6, tile.z); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, tile.urlTemplate); + selectQuery.bind(3, tile.pixelRatio); + selectQuery.bind(4, tile.x); + selectQuery.bind(5, tile.y); + selectQuery.bind(6, tile.z); + return !selectQuery.run(); } else { // clang-format off - Statement insert = getStatement( + mapbox::sqlite::Query insertQuery{ getStatement( "INSERT OR IGNORE INTO region_resources (region_id, resource_id) " "SELECT ?1, resources.id " "FROM resources " - "WHERE resources.url = ?2 "); + "WHERE resources.url = ?2 ") }; // clang-format on - insert->bind(1, regionID); - insert->bind(2, resource.url); - insert->run(); + insertQuery.bind(1, regionID); + insertQuery.bind(2, resource.url); + insertQuery.run(); - if (insert->changes() == 0) { + if (insertQuery.changes() == 0) { return false; } // clang-format off - Statement select = getStatement( + mapbox::sqlite::Query selectQuery{ getStatement( "SELECT region_id " "FROM region_resources, resources " "WHERE region_id != ?1 " " AND resources.url = ?2 " - "LIMIT 1 "); + "LIMIT 1 ") }; // clang-format on - select->bind(1, regionID); - select->bind(2, resource.url); - return !select->run(); + selectQuery.bind(1, regionID); + selectQuery.bind(2, resource.url); + return !selectQuery.run(); } } OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) { - // clang-format off - Statement stmt = getStatement( - "SELECT definition FROM regions WHERE id = ?1"); - // clang-format on - - stmt->bind(1, regionID); - stmt->run(); + mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") }; + query.bind(1, regionID); + query.run(); - return decodeOfflineRegionDefinition(stmt->get(0)); + return decodeOfflineRegionDefinition(query.get(0)); } OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) { @@ -760,35 +740,35 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) std::pair OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_resources, resources " "WHERE region_id = ?1 " - "AND resource_id = resources.id "); + "AND resource_id = resources.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get(0), stmt->get(1) }; + query.bind(1, regionID); + query.run(); + return { query.get(0), query.get(1) }; } std::pair OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) { // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(*), SUM(LENGTH(data)) " "FROM region_tiles, tiles " "WHERE region_id = ?1 " - "AND tile_id = tiles.id "); + "AND tile_id = tiles.id ") }; // clang-format on - stmt->bind(1, regionID); - stmt->run(); - return { stmt->get(0), stmt->get(1) }; + query.bind(1, regionID); + query.run(); + return { query.get(0), query.get(1) }; } template -T OfflineDatabase::getPragma(const char * sql) { - Statement stmt = getStatement(sql); - stmt->run(); - return stmt->get(0); +T OfflineDatabase::getPragma(const char* sql) { + mapbox::sqlite::Query query{ getStatement(sql) }; + query.run(); + return query.get(0); } // Remove least-recently used resources and tiles until the used database size, @@ -813,7 +793,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { // size, and because pages can get fragmented on the database. while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) { // clang-format off - Statement accessedStmt = getStatement( + mapbox::sqlite::Query accessedQuery{ getStatement( "SELECT max(accessed) " "FROM ( " " SELECT accessed " @@ -829,16 +809,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " WHERE tile_id IS NULL " " ORDER BY accessed ASC LIMIT ?1 " ") " - ); - accessedStmt->bind(1, 50); + ) }; + accessedQuery.bind(1, 50); // clang-format on - if (!accessedStmt->run()) { + if (!accessedQuery.run()) { return false; } - Timestamp accessed = accessedStmt->get(0); + Timestamp accessed = accessedQuery.get(0); // clang-format off - Statement stmt1 = getStatement( + mapbox::sqlite::Query resourceQuery{ getStatement( "DELETE FROM resources " "WHERE id IN ( " " SELECT id FROM resources " @@ -846,14 +826,14 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON resource_id = resources.id " " WHERE resource_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt1->bind(1, accessed); - stmt1->run(); - uint64_t changes1 = stmt1->changes(); + resourceQuery.bind(1, accessed); + resourceQuery.run(); + const uint64_t resourceChanges = resourceQuery.changes(); // clang-format off - Statement stmt2 = getStatement( + mapbox::sqlite::Query tileQuery{ getStatement( "DELETE FROM tiles " "WHERE id IN ( " " SELECT id FROM tiles " @@ -861,16 +841,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) { " ON tile_id = tiles.id " " WHERE tile_id IS NULL " " AND accessed <= ?1 " - ") "); + ") ") }; // clang-format on - stmt2->bind(1, accessed); - stmt2->run(); - uint64_t changes2 = stmt2->changes(); + tileQuery.bind(1, accessed); + tileQuery.run(); + const uint64_t tileChanges = tileQuery.changes(); // The cached value of offlineTileCount does not need to be updated // here because only non-offline tiles can be removed by eviction. - if (changes1 == 0 && changes2 == 0) { + if (resourceChanges == 0 && tileChanges == 0) { return false; } } @@ -901,16 +881,16 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() { } // clang-format off - Statement stmt = getStatement( + mapbox::sqlite::Query query{ getStatement( "SELECT COUNT(DISTINCT id) " "FROM region_tiles, tiles " "WHERE tile_id = tiles.id " - "AND url_template LIKE 'mapbox://%' "); + "AND url_template LIKE 'mapbox://%' ") }; // clang-format on - stmt->run(); + query.run(); - offlineMapboxTileCount = stmt->get(0); + offlineMapboxTileCount = query.get(0); return *offlineMapboxTileCount; } diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 91b544a9e0..9673ad8212 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -15,6 +15,7 @@ namespace mapbox { namespace sqlite { class Database; class Statement; +class Query; } // namespace sqlite } // namespace mapbox @@ -66,20 +67,7 @@ private: void migrateToVersion5(); void migrateToVersion6(); - class Statement { - public: - explicit Statement(mapbox::sqlite::Statement& stmt_) : stmt(stmt_) {} - Statement(Statement&&) = default; - Statement(const Statement&) = delete; - ~Statement(); - - mapbox::sqlite::Statement* operator->() { return &stmt; }; - - private: - mapbox::sqlite::Statement& stmt; - }; - - Statement getStatement(const char *); + mapbox::sqlite::Statement& getStatement(const char *); optional> getTile(const Resource::TileData&); optional hasTile(const Resource::TileData&); @@ -102,8 +90,8 @@ private: std::pair getCompletedTileCountAndSize(int64_t regionID); const std::string path; - std::unique_ptr<::mapbox::sqlite::Database> db; - std::unordered_map> statements; + std::unique_ptr db; + std::unordered_map> statements; template T getPragma(const char *); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index ba7a1f6446..8a567d602e 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -201,85 +201,93 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - assert(impl); - return Statement(this, query); +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique(db.impl->db, sql)) { } -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique(db->impl->db, sql)) -{ +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement::Statement(Statement &&other) { - *this = std::move(other); -} +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); -Statement &Statement::operator=(Statement &&other) { - std::swap(impl, other.impl); - return *this; +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() = default; +Query::~Query() { + reset(); + clearBindings(); -template <> void Statement::bind(int offset, std::nullptr_t) { - assert(impl); - impl->check(sqlite3_bind_null(impl->stmt, offset)); +#ifndef NDEBUG + stmt.used = false; +#endif } -template <> void Statement::bind(int offset, int8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset)); } -template <> void Statement::bind(int offset, int16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, int64_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint8_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, int64_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint16_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint8_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, uint32_t value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint16_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, float value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, uint32_t value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, double value) { - assert(impl); - impl->check(sqlite3_bind_double(impl->stmt, offset, value)); +template <> void Query::bind(int offset, float value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, bool value) { - assert(impl); - impl->check(sqlite3_bind_int(impl->stmt, offset, value)); +template <> void Query::bind(int offset, double value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value)); } -template <> void Statement::bind(int offset, const char *value) { - assert(impl); - impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC)); +template <> void Query::bind(int offset, bool value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value)); +} + +template <> void Query::bind(int offset, const char *value) { + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC)); } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they @@ -289,40 +297,40 @@ template <> void Statement::bind(int offset, const char *value) { // According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, // the first iOS version with 3.8.7+ was 9.0, with 3.8.8. -void Statement::bind(int offset, const char * value, std::size_t length, bool retain) { - assert(impl); +void Query::bind(int offset, const char * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { throw std::range_error("value too long for sqlite3_bind_text"); } - impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length), + stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length), retain ? SQLITE_TRANSIENT : SQLITE_STATIC)); } -void Statement::bindBlob(int offset, const std::vector& value, bool retain) { +void Query::bindBlob(int offset, const std::vector& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } template <> -void Statement::bind( +void Query::bind( int offset, std::chrono::time_point value) { - assert(impl); - impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); + assert(stmt.impl); + stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value))); } -template <> void Statement::bind(int offset, optional value) { +template <> void Query::bind(int offset, optional value) { if (!value) { bind(offset, nullptr); } else { @@ -331,7 +339,7 @@ template <> void Statement::bind(int offset, optional value) { } template <> -void Statement::bind( +void Query::bind( int offset, optional> value) { if (!value) { @@ -341,86 +349,86 @@ void Statement::bind( } } -bool Statement::run() { - assert(impl); - const int err = sqlite3_step(impl->stmt); - impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt)); - impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt)); +bool Query::run() { + assert(stmt.impl); + const int err = sqlite3_step(stmt.impl->stmt); + stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt)); + stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt)); if (err == SQLITE_DONE) { return false; } else if (err == SQLITE_ROW) { return true; } else if (err != SQLITE_OK) { - throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) }; + throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) }; } else { return false; } } -template <> bool Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> bool Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int Statement::get(int offset) { - assert(impl); - return sqlite3_column_int(impl->stmt, offset); +template <> int Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int(stmt.impl->stmt, offset); } -template <> int64_t Statement::get(int offset) { - assert(impl); - return sqlite3_column_int64(impl->stmt, offset); +template <> int64_t Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_int64(stmt.impl->stmt, offset); } -template <> double Statement::get(int offset) { - assert(impl); - return sqlite3_column_double(impl->stmt, offset); +template <> double Query::get(int offset) { + assert(stmt.impl); + return sqlite3_column_double(stmt.impl->stmt, offset); } -template <> std::string Statement::get(int offset) { - assert(impl); +template <> std::string Query::get(int offset) { + assert(stmt.impl); return { - reinterpret_cast(sqlite3_column_blob(impl->stmt, offset)), - size_t(sqlite3_column_bytes(impl->stmt, offset)) + reinterpret_cast(sqlite3_column_blob(stmt.impl->stmt, offset)), + size_t(sqlite3_column_bytes(stmt.impl->stmt, offset)) }; } -template <> std::vector Statement::get(int offset) { - assert(impl); - const auto* begin = reinterpret_cast(sqlite3_column_blob(impl->stmt, offset)); - const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset); +template <> std::vector Query::get(int offset) { + assert(stmt.impl); + const auto* begin = reinterpret_cast(sqlite3_column_blob(stmt.impl->stmt, offset)); + const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset); return { begin, end }; } template <> std::chrono::time_point -Statement::get(int offset) { - assert(impl); +Query::get(int offset) { + assert(stmt.impl); return std::chrono::time_point_cast( - std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset))); + std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset))); } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); } } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); } } -template <> optional Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +template <> optional Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return optional(); } else { return get(offset); @@ -429,9 +437,9 @@ template <> optional Statement::get(int offset) { template <> optional> -Statement::get(int offset) { - assert(impl); - if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) { +Query::get(int offset) { + assert(stmt.impl); + if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) { return {}; } else { return get>( @@ -439,24 +447,24 @@ Statement::get(int offset) { } } -void Statement::reset() { - assert(impl); - sqlite3_reset(impl->stmt); +void Query::reset() { + assert(stmt.impl); + sqlite3_reset(stmt.impl->stmt); } -void Statement::clearBindings() { - assert(impl); - sqlite3_clear_bindings(impl->stmt); +void Query::clearBindings() { + assert(stmt.impl); + sqlite3_clear_bindings(stmt.impl->stmt); } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - auto changes_ = impl->changes; +uint64_t Query::changes() const { + assert(stmt.impl); + auto changes_ = stmt.impl->changes; return (changes_ < 0 ? 0 : changes_); } diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 4080d33950..20d09b550c 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -67,6 +67,7 @@ public: class DatabaseImpl; class Statement; class StatementImpl; +class Query; class Database { private: @@ -81,7 +82,6 @@ public: void setBusyTimeout(std::chrono::milliseconds); void exec(const std::string &sql); - Statement prepare(const char *query); private: std::unique_ptr impl; @@ -89,28 +89,54 @@ private: friend class Statement; }; +// A Statement object represents a prepared statement that can be run repeatedly run with a Query object. class Statement { +public: + Statement(Database& db, const char* sql); + Statement(const Statement&) = delete; + Statement(Statement&&) = delete; + Statement& operator=(const Statement&) = delete; + Statement& operator=(Statement&&) = delete; + ~Statement(); + + friend class Query; + private: - Statement(const Statement &) = delete; - Statement &operator=(const Statement &) = delete; + std::unique_ptr impl; +#ifndef NDEBUG + // This flag stores whether there exists a Query object that uses this prepared statement. + // There may only be one Query object at a time. Statement objects must outlive Query objects. + // While a Query object exists, a Statement object may not be moved or deleted. + bool used = false; +#endif +}; + +// A Query object is used to run a database query with a prepared statement (stored in a Statement +// object). There may only exist one Query object per Statement object. Query objects are designed +// to be constructed and destroyed frequently. +class Query { public: - Statement(Database *db, const char *sql); - Statement(Statement &&); - ~Statement(); - Statement &operator=(Statement &&); + Query(Statement&); + Query(const Query&) = delete; + Query(Query&&) = delete; + Query& operator=(const Query&) = delete; + Query& operator=(Query&&) = delete; + ~Query(); - template void bind(int offset, T value); + template + void bind(int offset, T value); // Text - void bind(int offset, const char *, std::size_t length, bool retain = true); + void bind(int offset, const char*, std::size_t length, bool retain = true); void bind(int offset, const std::string&, bool retain = true); // Blob - void bindBlob(int offset, const void *, std::size_t length, bool retain = true); + void bindBlob(int offset, const void*, std::size_t length, bool retain = true); void bindBlob(int offset, const std::vector&, bool retain = true); - template T get(int offset); + template + T get(int offset); bool run(); void reset(); @@ -120,7 +146,7 @@ public: uint64_t changes() const; private: - std::unique_ptr impl; + Statement& stmt; }; class Transaction { diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 09a3a16002..4bcaea0e31 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -186,74 +186,82 @@ void Database::exec(const std::string &sql) { } } -Statement Database::prepare(const char *query) { - return Statement(this, query); -} - -Statement::Statement(Database *db, const char *sql) - : impl(std::make_unique(QString(sql), QSqlDatabase::database(db->impl->connectionName))) { +Statement::Statement(Database& db, const char* sql) + : impl(std::make_unique(QString(sql), + QSqlDatabase::database(db.impl->connectionName))) { assert(impl); } -Statement::Statement(Statement &&other) - : impl(std::move(other.impl)) { - assert(impl); +Statement::~Statement() { +#ifndef NDEBUG + // Crash if we're destructing this object while we know a Query object references this. + assert(!used); +#endif } -Statement &Statement::operator=(Statement &&other) { - assert(impl); - std::swap(impl, other.impl); - return *this; +Query::Query(Statement& stmt_) : stmt(stmt_) { + assert(stmt.impl); + +#ifndef NDEBUG + assert(!stmt.used); + stmt.used = true; +#endif } -Statement::~Statement() { +Query::~Query() { + reset(); + clearBindings(); + +#ifndef NDEBUG + stmt.used = false; +#endif } -template void Statement::bind(int, int64_t); +template void Query::bind(int, int64_t); template -void Statement::bind(int offset, T value) { - assert(impl); +void Query::bind(int offset, T value) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant::fromValue(value), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant::fromValue(value), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, std::nullptr_t) { - assert(impl); +void Query::bind(int offset, std::nullptr_t) { + assert(stmt.impl); // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); - checkQueryError(impl->query); + stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(stmt.impl->query); } template <> -void Statement::bind(int offset, int32_t value) { +void Query::bind(int offset, int32_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, bool value) { +void Query::bind(int offset, bool value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, int8_t value) { +void Query::bind(int offset, int8_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, uint8_t value) { +void Query::bind(int offset, uint8_t value) { bind(offset, static_cast(value)); } template <> -void Statement::bind(int offset, mbgl::Timestamp value) { +void Query::bind(int offset, mbgl::Timestamp value) { bind(offset, std::chrono::system_clock::to_time_t(value)); } template <> -void Statement::bind(int offset, optional value) { +void Query::bind(int offset, optional value) { if (value) { bind(offset, *value); } else { @@ -262,7 +270,7 @@ void Statement::bind(int offset, optional value) { } template <> -void Statement::bind(int offset, optional value) { +void Query::bind(int offset, optional value) { if (value) { bind(offset, *value); } else { @@ -270,25 +278,25 @@ void Statement::bind(int offset, optional value) { } } -void Statement::bind(int offset, const char* value, std::size_t length, bool /* retain */) { - assert(impl); +void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) { + assert(stmt.impl); if (length > std::numeric_limits::max()) { // Kept for consistence with the default implementation. throw std::range_error("value too long"); } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); + stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bind(int offset, const std::string& value, bool retain) { +void Query::bind(int offset, const std::string& value, bool retain) { bind(offset, value.data(), value.size(), retain); } -void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { - assert(impl); +void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + assert(stmt.impl); const char* value = reinterpret_cast(value_); if (length > std::numeric_limits::max()) { // Kept for consistence with the default implementation. @@ -296,123 +304,123 @@ void Statement::bindBlob(int offset, const void* value_, std::size_t length, boo } // Field numbering starts at 0. - impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } -void Statement::bindBlob(int offset, const std::vector& value, bool retain) { +void Query::bindBlob(int offset, const std::vector& value, bool retain) { bindBlob(offset, value.data(), value.size(), retain); } -bool Statement::run() { - assert(impl); +bool Query::run() { + assert(stmt.impl); - if (!impl->query.isValid()) { - if (impl->query.exec()) { - impl->lastInsertRowId = impl->query.lastInsertId().value(); - impl->changes = impl->query.numRowsAffected(); + if (!stmt.impl->query.isValid()) { + if (stmt.impl->query.exec()) { + stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value(); + stmt.impl->changes = stmt.impl->query.numRowsAffected(); } else { - checkQueryError(impl->query); + checkQueryError(stmt.impl->query); } } - const bool hasNext = impl->query.next(); - if (!hasNext) impl->query.finish(); + const bool hasNext = stmt.impl->query.next(); + if (!hasNext) stmt.impl->query.finish(); return hasNext; } -template bool Statement::get(int); -template int Statement::get(int); -template int64_t Statement::get(int); -template double Statement::get(int); +template bool Query::get(int); +template int Query::get(int); +template int64_t Query::get(int); +template double Query::get(int); -template T Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template T Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return value.value(); } -template <> std::vector Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray byteArray = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::vector Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray byteArray = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); std::vector blob(byteArray.begin(), byteArray.end()); return blob; } -template <> mbgl::Timestamp Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> mbgl::Timestamp Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); return std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(value.value<::time_t>())); } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value() }; } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { value.value() }; } -template <> std::string Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> std::string Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); return std::string(value.constData(), value.size()); } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QByteArray value = impl->query.value(offset).toByteArray(); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QByteArray value = stmt.impl->query.value(offset).toByteArray(); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::string(value.constData(), value.size()) }; } -template <> optional Statement::get(int offset) { - assert(impl && impl->query.isValid()); - QVariant value = impl->query.value(offset); - checkQueryError(impl->query); +template <> optional Query::get(int offset) { + assert(stmt.impl && stmt.impl->query.isValid()); + QVariant value = stmt.impl->query.value(offset); + checkQueryError(stmt.impl->query); if (value.isNull()) return {}; return { std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(value.value<::time_t>())) }; } -void Statement::reset() { - assert(impl); - impl->query.finish(); +void Query::reset() { + assert(stmt.impl); + stmt.impl->query.finish(); } -void Statement::clearBindings() { +void Query::clearBindings() { // no-op } -int64_t Statement::lastInsertRowId() const { - assert(impl); - return impl->lastInsertRowId; +int64_t Query::lastInsertRowId() const { + assert(stmt.impl); + return stmt.impl->lastInsertRowId; } -uint64_t Statement::changes() const { - assert(impl); - return (impl->changes < 0 ? 0 : impl->changes); +uint64_t Query::changes() const { + assert(stmt.impl); + return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes); } Transaction::Transaction(Database& db_, Mode mode) diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 23117173d1..620e6eaa6d 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -66,7 +66,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { std::string path("test/fixtures/offline_database/offline.db"); { - mapbox::sqlite::Database db(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + mapbox::sqlite::Database db{ path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite }; db.exec("PRAGMA user_version = 1"); } @@ -599,40 +599,45 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { } static int databasePageCount(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma page_count"); - stmt.run(); - return stmt.get(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma page_count" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get(0); } static int databaseUserVersion(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma user_version"); - stmt.run(); - return stmt.get(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma user_version" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get(0); } static std::string databaseJournalMode(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma journal_mode"); - stmt.run(); - return stmt.get(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get(0); } static int databaseSyncMode(const std::string& path) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); - mapbox::sqlite::Statement stmt = db.prepare("pragma synchronous"); - stmt.run(); - return stmt.get(0); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Statement stmt{ db, "pragma synchronous" }; + mapbox::sqlite::Query query{ stmt }; + query.run(); + return query.get(0); } static std::vector databaseTableColumns(const std::string& path, const std::string& name) { - mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly); + mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; const auto sql = std::string("pragma table_info(") + name + ")"; - mapbox::sqlite::Statement stmt = db.prepare(sql.c_str()); + mapbox::sqlite::Statement stmt{ db, sql.c_str() }; + mapbox::sqlite::Query query{ stmt }; std::vector columns; - while (stmt.run()) { - columns.push_back(stmt.get(1)); + while (query.run()) { + columns.push_back(query.get(1)); } return columns; } diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 7f33174c0d..918200181f 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -9,21 +9,23 @@ TEST(SQLite, Statement) { mapbox::sqlite::Database db(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("CREATE TABLE test (id INTEGER);"); - mapbox::sqlite::Statement stmt1 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt1.lastInsertRowId(), 0); - ASSERT_EQ(stmt1.changes(), 0u); - stmt1.bind(1, 10); - stmt1.run(); - ASSERT_EQ(stmt1.lastInsertRowId(), 1); - ASSERT_EQ(stmt1.changes(), 1u); + mapbox::sqlite::Statement stmt1{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query1{ stmt1 }; + ASSERT_EQ(query1.lastInsertRowId(), 0); + ASSERT_EQ(query1.changes(), 0u); + query1.bind(1, 10); + query1.run(); + ASSERT_EQ(query1.lastInsertRowId(), 1); + ASSERT_EQ(query1.changes(), 1u); - mapbox::sqlite::Statement stmt2 = db.prepare("INSERT INTO test (id) VALUES (?1);"); - ASSERT_EQ(stmt2.lastInsertRowId(), 0); - ASSERT_EQ(stmt2.changes(), 0u); - stmt2.bind(1, 20); - stmt2.run(); - ASSERT_EQ(stmt2.lastInsertRowId(), 2); - ASSERT_EQ(stmt2.changes(), 1u); + mapbox::sqlite::Statement stmt2{ db, "INSERT INTO test (id) VALUES (?1);" }; + mapbox::sqlite::Query query2{ stmt2 }; + ASSERT_EQ(query2.lastInsertRowId(), 0); + ASSERT_EQ(query2.changes(), 0u); + query2.bind(1, 20); + query2.run(); + ASSERT_EQ(query2.lastInsertRowId(), 2); + ASSERT_EQ(query2.changes(), 1u); } TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { -- cgit v1.2.1 From ae0e583590ce8d7a564c8d9cb7c0fcee5515125e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 5 Mar 2018 14:22:06 +0100 Subject: [android] fix CameraAnimatorActivity home button (#11383) --- .../testapp/activity/camera/CameraAnimatorActivity.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java index f25fd6ab27..176d713a4b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java @@ -164,9 +164,12 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe if (mapboxMap == null) { return false; } - findViewById(R.id.fab).setVisibility(View.GONE); - resetCameraPosition(); - playAnimation(item.getItemId()); + + if (item.getItemId() != android.R.id.home) { + findViewById(R.id.fab).setVisibility(View.GONE); + resetCameraPosition(); + playAnimation(item.getItemId()); + } return super.onOptionsItemSelected(item); } -- cgit v1.2.1 From fc140e265b559868a2a9f568583689ee4a6b9dfd Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Fri, 2 Mar 2018 19:43:30 +0200 Subject: [Qt] Expose mapLoadingFailed signal --- platform/qt/include/qmapboxgl.hpp | 1 + platform/qt/src/qmapboxgl.cpp | 1 + platform/qt/src/qmapboxgl_map_observer.cpp | 7 ++++++- platform/qt/src/qmapboxgl_map_observer.hpp | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index bc18eaba59..7b07a0dff6 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -251,6 +251,7 @@ public slots: signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(const QString &reason); void copyrightsChanged(const QString ©rightsHtml); void staticRenderFinished(const QString &error); diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 982f4b3b35..5430af30e2 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1649,6 +1649,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin qRegisterMetaType("QMapboxGL::MapChange"); connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); + connect(m_mapObserver.get(), SIGNAL(mapLoadingFailed(QString)), q, SIGNAL(mapLoadingFailed(QString))); connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); // Setup the Map object diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp index 60c6b81841..43b4162eb4 100644 --- a/platform/qt/src/qmapboxgl_map_observer.cpp +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -2,6 +2,10 @@ #include "qmapboxgl_p.hpp" +#include + +#include + QMapboxGLMapObserver::QMapboxGLMapObserver(QMapboxGLPrivate *d) : d_ptr(d) { @@ -44,9 +48,10 @@ void QMapboxGLMapObserver::onDidFinishLoadingMap() emit mapChanged(QMapboxGL::MapChangeDidFinishLoadingMap); } -void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr) +void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr exception) { emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); + emit mapLoadingFailed(QString::fromStdString(mbgl::util::toString(exception))); } void QMapboxGLMapObserver::onWillStartRenderingFrame() diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp index c9d0581a90..7c272c39ae 100644 --- a/platform/qt/src/qmapboxgl_map_observer.hpp +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -36,6 +36,7 @@ public: signals: void mapChanged(QMapboxGL::MapChange); + void mapLoadingFailed(const QString &reason); void copyrightsChanged(const QString ©rightsHtml); private: -- cgit v1.2.1 From 803a4d46ed15a790876f5bda0e1d49d9ba0483a4 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 5 Mar 2018 13:47:31 -0500 Subject: [Qt] Added QMapboxGL::MapLoadingFailure enum --- platform/qt/include/qmapboxgl.hpp | 10 +++++++++- platform/qt/src/qmapboxgl.cpp | 22 +++++++++++++++++++++- platform/qt/src/qmapboxgl_map_observer.cpp | 24 ++++++++++++++++++++++-- platform/qt/src/qmapboxgl_map_observer.hpp | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index 7b07a0dff6..b5676fbedd 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -128,6 +128,13 @@ public: MapChangeSourceDidChange }; + enum MapLoadingFailure { + StyleParseFailure, + StyleLoadFailure, + NotFoundFailure, + UnknownFailure + }; + // Determines the orientation of the map. enum NorthOrientation { NorthUpwards, // Default @@ -251,7 +258,7 @@ public slots: signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); - void mapLoadingFailed(const QString &reason); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); void staticRenderFinished(const QString &error); @@ -263,5 +270,6 @@ private: }; Q_DECLARE_METATYPE(QMapboxGL::MapChange); +Q_DECLARE_METATYPE(QMapboxGL::MapLoadingFailure); #endif // QMAPBOXGL_H diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 5430af30e2..34fd5509fa 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -517,6 +517,19 @@ void QMapboxGLSettings::setResourceTransform(const std::function("QMapboxGL::MapChange"); connect(m_mapObserver.get(), SIGNAL(mapChanged(QMapboxGL::MapChange)), q, SIGNAL(mapChanged(QMapboxGL::MapChange))); - connect(m_mapObserver.get(), SIGNAL(mapLoadingFailed(QString)), q, SIGNAL(mapLoadingFailed(QString))); + connect(m_mapObserver.get(), SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString)), q, SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString))); connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString))); // Setup the Map object diff --git a/platform/qt/src/qmapboxgl_map_observer.cpp b/platform/qt/src/qmapboxgl_map_observer.cpp index 43b4162eb4..44cb8c41d5 100644 --- a/platform/qt/src/qmapboxgl_map_observer.cpp +++ b/platform/qt/src/qmapboxgl_map_observer.cpp @@ -2,7 +2,7 @@ #include "qmapboxgl_p.hpp" -#include +#include #include @@ -51,7 +51,27 @@ void QMapboxGLMapObserver::onDidFinishLoadingMap() void QMapboxGLMapObserver::onDidFailLoadingMap(std::exception_ptr exception) { emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap); - emit mapLoadingFailed(QString::fromStdString(mbgl::util::toString(exception))); + + QMapboxGL::MapLoadingFailure type; + QString description; + + try { + std::rethrow_exception(exception); + } catch (const mbgl::util::StyleParseException& e) { + type = QMapboxGL::MapLoadingFailure::StyleParseFailure; + description = e.what(); + } catch (const mbgl::util::StyleLoadException& e) { + type = QMapboxGL::MapLoadingFailure::StyleLoadFailure; + description = e.what(); + } catch (const mbgl::util::NotFoundException& e) { + type = QMapboxGL::MapLoadingFailure::NotFoundFailure; + description = e.what(); + } catch (const std::exception& e) { + type = QMapboxGL::MapLoadingFailure::UnknownFailure; + description = e.what(); + } + + emit mapLoadingFailed(type, description); } void QMapboxGLMapObserver::onWillStartRenderingFrame() diff --git a/platform/qt/src/qmapboxgl_map_observer.hpp b/platform/qt/src/qmapboxgl_map_observer.hpp index 7c272c39ae..98da5b6add 100644 --- a/platform/qt/src/qmapboxgl_map_observer.hpp +++ b/platform/qt/src/qmapboxgl_map_observer.hpp @@ -36,7 +36,7 @@ public: signals: void mapChanged(QMapboxGL::MapChange); - void mapLoadingFailed(const QString &reason); + void mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason); void copyrightsChanged(const QString ©rightsHtml); private: -- cgit v1.2.1 From d7808b3a77bce6d2c8221ea624ef5b78616b760c Mon Sep 17 00:00:00 2001 From: Langston Smith Date: Tue, 6 Mar 2018 06:09:47 -0800 Subject: Android readme snapshot dependency line cleanup (#11071) * Snapshot dependency line cleanup * Matching version nums * changed compile to implementation --- platform/android/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/android/README.md b/platform/android/README.md index 1dbdeee343..4e9d55f8c8 100644 --- a/platform/android/README.md +++ b/platform/android/README.md @@ -89,15 +89,14 @@ More information about building and distributing this project in [DISTRIBUTE.md] #### Using the SDK snapshot -Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example `5.2.0-SNAPSHOT` or: +Instead of using the latest stable release of the Maps SDK for Android, you can use a "snapshot" or the beta version if there is one available. Our snapshots are built every time a Github pull request adds code to this repository's `master` branch. If you'd like to use a snapshot build, your Android project's gradle file should have -SNAPSHOT appended to the SDK version number. For example, the `5.2.0-SNAPSHOT` would look like: ```java // Mapbox SDK dependency -compile('com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT@aar') { - transitive = true -} +implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT' ``` -You need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: + +You also need to have the section below in your build.gradle root folder to be able to resolve the SNAPSHOT dependencies: ``` allprojects { repositories { -- cgit v1.2.1 From eeadec3e061c0041257bc792eabb1db5fc05ef27 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 7 Mar 2018 14:21:41 +0200 Subject: [qt] Fix Qt build on MinGW --- src/mbgl/util/compression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp index 30e813cbb8..ee3ebe7cae 100644 --- a/src/mbgl/util/compression.cpp +++ b/src/mbgl/util/compression.cpp @@ -1,6 +1,6 @@ #include -#if defined(__QT__) && defined(_WINDOWS) +#if defined(__QT__) && defined(_WINDOWS) && !defined(__GNUC__) #include #else #include -- cgit v1.2.1 From 268a4d7404d30dc4f866711e3fd1e778892bbe35 Mon Sep 17 00:00:00 2001 From: Cameron Mace Date: Wed, 7 Mar 2018 10:45:39 -0500 Subject: Update README.md (#11408) Update splash image --- platform/android/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/README.md b/platform/android/README.md index 4e9d55f8c8..31e0a94f2c 100644 --- a/platform/android/README.md +++ b/platform/android/README.md @@ -12,7 +12,7 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ **To install and use the Mapbox Maps SDK for Android in an application, see the [Mapbox Maps SDK for Android website](https://www.mapbox.com/install/android/).** -[![](https://www.mapbox.com/android-sdk/images/splash.png)](https://www.mapbox.com/android-sdk/) +[![](https://www.mapbox.com/android-docs/assets/overview-map-sdk-322-9abe118316efb5910b6101e222a2e57c.png)](https://www.mapbox.com/android-sdk/) ### Setup environment -- cgit v1.2.1 From c54035afb159826740ebb4c6a69dfe0f1809604f Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Fri, 16 Feb 2018 12:38:23 -0800 Subject: [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 c9e3b0630a..da4ca4abf0 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -137,10 +137,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 f630a49078..8554cfb65e 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 dc8ae28705..c4c37c74d7 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); -- cgit v1.2.1 From 7df451def6c37bd842b13954b91fe8f303ecec9f Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Thu, 8 Mar 2018 12:18:59 -0800 Subject: [core] TileJSON conversion clamps bounds longitude to [-180,180], per spec --- include/mbgl/style/conversion/tileset.hpp | 2 ++ test/style/conversion/tileset.test.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 6ec46aa7b6..16322b58ee 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -100,6 +100,8 @@ public: error = { "bounds left longitude should be less than right longitude" }; return {}; } + *left = util::max(-180.0, *left); + *right = util::min(180.0, *right); result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right }); } diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp index 9487277cca..f10aa0e318 100644 --- a/test/style/conversion/tileset.test.cpp +++ b/test/style/conversion/tileset.test.cpp @@ -62,6 +62,16 @@ TEST(Tileset, ValidWorldBounds) { EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); } +TEST(Tileset, BoundsAreClamped) { + Error error; + mbgl::optional converted = convertJSON(R"JSON({ + "tiles": ["http://mytiles"], + "bounds": [-181.0000005,-90,180.00000000000006,90] + })JSON", error); + EXPECT_TRUE((bool) converted); + EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); +} + TEST(Tileset, FullConversion) { Error error; Tileset converted = *convertJSON(R"JSON({ -- cgit v1.2.1 From c815c1e2d661ae43584db0e6eed2a2aaf5fe7e14 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 8 Mar 2018 14:31:58 -0500 Subject: [ios] Fix pinch drift ignoring delegate camera change response --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLMapView.mm | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index e2052abb89..49b2add722 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -4,6 +4,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## 3.7.6 +* Fixed an issue where the pinch gesture could drift beyond bounds imposed by `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]`. ([#11423](https://github.com/mapbox/mapbox-gl-native/pull/11423)) * Improved the visibility of the heading indicator arrow. ([#11337](https://github.com/mapbox/mapbox-gl-native/pull/11337)) ## 3.7.5 - February 16, 2018 diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 02f529d094..0fae5e0394 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1459,10 +1459,12 @@ public: double zoom = log2(newScale); MGLMapCamera *toCamera = [self cameraByZoomingToZoomLevel:zoom aroundAnchorPoint:centerPoint]; - if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) + if ( ! [self _shouldChangeFromCamera:oldCamera toCamera:toCamera]) { drift = NO; - } else { + } + else + { if (drift) { _mbglMap->setZoom(zoom, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationFromTimeInterval(duration)); @@ -1695,7 +1697,9 @@ public: { [weakSelf unrotateIfNeededForGesture]; }]; - } else { + } + else + { [self unrotateIfNeededForGesture]; } } -- cgit v1.2.1 From e690e7cf1e91ba33017575ed9d068f2f38acab03 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 8 Mar 2018 14:52:43 -0500 Subject: [ios] Add camera limit debug option to iosapp Adapted from https://github.com/mapbox/ios-sdk-examples/blob/cdff47276d261d58c7eb2d0ba75d9cce6c308417/Examples/ObjectiveC/BlockingGesturesDelegateExample.m --- platform/ios/app/MBXViewController.m | 43 +++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 1046644f8c..63b06af583 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -18,6 +18,11 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = { { .latitude = -13.15589555, .longitude = -74.2178961777998 }, }; +static const MGLCoordinateBounds colorado = { + .sw = { .latitude = 36.986207, .longitude = -109.049896}, + .ne = { .latitude = 40.989329, .longitude = -102.062592}, +}; + static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXViewControllerAnnotationViewReuseIdentifer"; typedef NS_ENUM(NSInteger, MBXSettingsSections) { @@ -85,6 +90,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { MBXSettingsMiscellaneousToggleTwoMaps, MBXSettingsMiscellaneousCountryLabels, MBXSettingsMiscellaneousShowSnapshots, + MBXSettingsMiscellaneousShouldLimitCameraChanges, MBXSettingsMiscellaneousPrintLogFile, MBXSettingsMiscellaneousDeleteLogFile, }; @@ -122,6 +128,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @property (nonatomic) BOOL usingLocaleBasedCountryLabels; @property (nonatomic) BOOL reuseQueueStatsEnabled; @property (nonatomic) BOOL showZoomLevelEnabled; +@property (nonatomic) BOOL shouldLimitCameraChanges; @end @@ -363,7 +370,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Embedded Map View", [NSString stringWithFormat:@"%@ Second Map", ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")], [NSString stringWithFormat:@"Show Labels in %@", (_usingLocaleBasedCountryLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])], - @"Show Snapshots" + @"Show Snapshots", + [NSString stringWithFormat:@"%@ Camera Changes", (_shouldLimitCameraChanges ? @"Unlimit" : @"Limit")], ]]; if (self.debugLoggingEnabled) @@ -652,6 +660,14 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self performSegueWithIdentifier:@"ShowSnapshots" sender:nil]; break; } + case MBXSettingsMiscellaneousShouldLimitCameraChanges: + { + self.shouldLimitCameraChanges = !self.shouldLimitCameraChanges; + if (self.shouldLimitCameraChanges) { + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(39.748947, -104.995882) zoomLevel:10 direction:0 animated:NO]; + } + break; + } default: NSAssert(NO, @"All miscellaneous setting rows should be implemented"); break; @@ -1880,6 +1896,31 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { _usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"]; } +- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera { + if (_shouldLimitCameraChanges) { + // Get the current camera to restore it after. + MGLMapCamera *currentCamera = mapView.camera; + + // From the new camera obtain the center to test if it’s inside the boundaries. + CLLocationCoordinate2D newCameraCenter = newCamera.centerCoordinate; + + // Set the map’s visible bounds to newCamera. + mapView.camera = newCamera; + MGLCoordinateBounds newVisibleCoordinates = mapView.visibleCoordinateBounds; + + // Revert the camera. + mapView.camera = currentCamera; + + // Test if the newCameraCenter and newVisibleCoordinates are inside Colorado. + BOOL inside = MGLCoordinateInCoordinateBounds(newCameraCenter, colorado); + BOOL intersects = MGLCoordinateInCoordinateBounds(newVisibleCoordinates.ne, colorado) && MGLCoordinateInCoordinateBounds(newVisibleCoordinates.sw, colorado); + + return inside && intersects; + } else { + return YES; + } +} + - (void)mapViewRegionIsChanging:(MGLMapView *)mapView { [self updateHUD]; -- cgit v1.2.1 From 5b3ebc4a2429c202f08c7468c300950de4844615 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 8 Mar 2018 15:48:08 -0800 Subject: [core] Add expression filter support (#11251) * WIP * WIP * WIP * Remove Filter::operator()(const Feature&) * WIP * WIP * WIP * WIP * Hook up expression filter evaluator * Replace `shared_ptr` with &reference * Fill in implementation of `void operator()(const ExpressionFilter&)` * Fix failing tests * Switch back to a shared_ptr per chat with @anandthakker * Fix benchmark compilation * Shot in the dark to fix CI * Shot in the dark to fix CI (part 2) * Shot in the dark to fix CI (part 3) * In src/mbgl/style/conversion/filter.cpp, add a port of isExpressionFilter and use it to decide in Converter::operator() whether to parse the incoming JSON as an ExpressionFilter or one of the legacy filter types * Remove bool Filter::operator()(const GeometryTileFeature&) const * Ensure the map zoom is passed into filtering operations wherever applicable * Add expression filter tests * Addressed PR feedback * Implement `NSPredicate *operator()(mbgl::style::ExpressionFilter filter)` * Fix formatting& nit --- benchmark/fixtures/api/cache.db | Bin 1298432 -> 1298432 bytes benchmark/parse/filter.benchmark.cpp | 11 +- cmake/core-files.cmake | 2 + include/mbgl/style/filter.hpp | 22 +- include/mbgl/style/filter_evaluator.hpp | 259 +++--------------------- platform/darwin/src/NSPredicate+MGLAdditions.mm | 6 + src/mbgl/geometry/feature_index.cpp | 2 +- src/mbgl/layout/symbol_layout.cpp | 2 +- src/mbgl/style/conversion/filter.cpp | 65 +++++- src/mbgl/style/conversion/stringify.hpp | 4 + src/mbgl/style/filter.cpp | 13 ++ src/mbgl/style/filter_evaluator.cpp | 225 ++++++++++++++++++++ src/mbgl/tile/custom_geometry_tile.cpp | 2 +- src/mbgl/tile/geojson_tile.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 2 +- src/mbgl/tile/geometry_tile_worker.cpp | 2 +- test/style/filter.test.cpp | 140 ++++++------- 17 files changed, 423 insertions(+), 336 deletions(-) create mode 100644 src/mbgl/style/filter.cpp create mode 100644 src/mbgl/style/filter_evaluator.cpp diff --git a/benchmark/fixtures/api/cache.db b/benchmark/fixtures/api/cache.db index 6a1d60421f..10452b2714 100644 Binary files a/benchmark/fixtures/api/cache.db and b/benchmark/fixtures/api/cache.db differ diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index 4984668400..e4cf635256 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace mbgl; @@ -22,15 +23,11 @@ static void Parse_Filter(benchmark::State& state) { static void Parse_EvaluateFilter(benchmark::State& state) { const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER"); - const PropertyMap properties = { { "foo", std::string("bar") } }; + const StubGeometryTileFeature feature = { {}, FeatureType::Unknown , {}, {{ "foo", std::string("bar") }} }; + const style::expression::EvaluationContext context = { &feature }; while (state.KeepRunning()) { - filter(FeatureType::Unknown, {}, [&] (const std::string& key) -> optional { - auto it = properties.find(key); - if (it == properties.end()) - return {}; - return it->second; - }); + filter(context); } } diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index f24482e301..35eb9c9400 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -376,6 +376,8 @@ set(MBGL_CORE_FILES src/mbgl/style/collection.hpp src/mbgl/style/custom_tile_loader.cpp src/mbgl/style/custom_tile_loader.hpp + src/mbgl/style/filter.cpp + src/mbgl/style/filter_evaluator.cpp src/mbgl/style/image.cpp src/mbgl/style/image_impl.cpp src/mbgl/style/image_impl.hpp diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index a204a2b17a..ccf8dce188 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -232,6 +233,15 @@ public: return true; } }; + +class ExpressionFilter { +public: + std::shared_ptr expression; + + friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { + return *(lhs.expression) == *(rhs.expression); + } +}; using FilterBase = variant< @@ -258,19 +268,13 @@ using FilterBase = variant< class IdentifierInFilter, class IdentifierNotInFilter, class HasIdentifierFilter, - class NotHasIdentifierFilter>; + class NotHasIdentifierFilter, + class ExpressionFilter>; class Filter : public FilterBase { public: using FilterBase::FilterBase; - - bool operator()(const Feature&) const; - - template - bool operator()(const GeometryTileFeature&) const; - - template - bool operator()(FeatureType type, optional id, PropertyAccessor accessor) const; + bool operator()(const expression::EvaluationContext& context) const; }; } // namespace style diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp index 66223d7282..a4a4098b3f 100644 --- a/include/mbgl/style/filter_evaluator.hpp +++ b/include/mbgl/style/filter_evaluator.hpp @@ -19,242 +19,37 @@ namespace style { // does not match } */ -template class FilterEvaluator { public: - const FeatureType featureType; - const optional featureIdentifier; - const PropertyAccessor propertyAccessor; + const expression::EvaluationContext context; + + bool operator()(const NullFilter&) const; + bool operator()(const EqualsFilter& filter) const; + bool operator()(const NotEqualsFilter& filter) const; + bool operator()(const LessThanFilter& filter) const; + bool operator()(const LessThanEqualsFilter& filter) const; + bool operator()(const GreaterThanFilter& filter) const; + bool operator()(const GreaterThanEqualsFilter& filter) const; + bool operator()(const InFilter& filter) const; + bool operator()(const NotInFilter& filter) const; + bool operator()(const AnyFilter& filter) const; + bool operator()(const AllFilter& filter) const; + bool operator()(const NoneFilter& filter) const; + bool operator()(const HasFilter& filter) const; + bool operator()(const NotHasFilter& filter) const; + bool operator()(const TypeEqualsFilter& filter) const; + bool operator()(const TypeNotEqualsFilter& filter) const; + bool operator()(const TypeInFilter& filter) const; + bool operator()(const TypeNotInFilter& filter) const; + bool operator()(const IdentifierEqualsFilter& filter) const; + bool operator()(const IdentifierNotEqualsFilter& filter) const; + bool operator()(const IdentifierInFilter& filter) const; + bool operator()(const IdentifierNotInFilter& filter) const; + bool operator()(const HasIdentifierFilter&) const; + bool operator()(const NotHasIdentifierFilter&) const; + bool operator()(const ExpressionFilter&) const; - bool operator()(const NullFilter&) const { - return true; - } - - bool operator()(const EqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && equal(*actual, filter.value); - } - - bool operator()(const NotEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return !actual || !equal(*actual, filter.value); - } - - bool operator()(const LessThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); - } - - bool operator()(const LessThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); - } - - bool operator()(const GreaterThanFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); - } - - bool operator()(const GreaterThanEqualsFilter& filter) const { - optional actual = propertyAccessor(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); - } - - bool operator()(const InFilter& filter) const { - optional actual = propertyAccessor(filter.key); - if (!actual) - return false; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return true; - } - } - return false; - } - - bool operator()(const NotInFilter& filter) const { - optional actual = propertyAccessor(filter.key); - if (!actual) - return true; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return false; - } - } - return true; - } - - bool operator()(const AnyFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return true; - } - } - return false; - } - - bool operator()(const AllFilter& filter) const { - for (const auto& f: filter.filters) { - if (!Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const NoneFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return false; - } - } - return true; - } - - bool operator()(const HasFilter& filter) const { - return bool(propertyAccessor(filter.key)); - } - - bool operator()(const NotHasFilter& filter) const { - return !propertyAccessor(filter.key); - } - - - bool operator()(const TypeEqualsFilter& filter) const { - return featureType == filter.value; - } - - bool operator()(const TypeNotEqualsFilter& filter) const { - return featureType != filter.value; - } - - bool operator()(const TypeInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureType == v) { - return true; - } - } - return false; - } - - bool operator()(const TypeNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureType == v) { - return false; - } - } - return true; - } - - - bool operator()(const IdentifierEqualsFilter& filter) const { - return featureIdentifier == filter.value; - } - - bool operator()(const IdentifierNotEqualsFilter& filter) const { - return featureIdentifier != filter.value; - } - - bool operator()(const IdentifierInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureIdentifier == v) { - return true; - } - } - return false; - } - - bool operator()(const IdentifierNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (featureIdentifier == v) { - return false; - } - } - return true; - } - - bool operator()(const HasIdentifierFilter&) const { - return bool(featureIdentifier); - } - - bool operator()(const NotHasIdentifierFilter&) const { - return !featureIdentifier; - } - -private: - template - struct Comparator { - const Op& op; - - template - bool operator()(const T& lhs, const T& rhs) const { - return op(lhs, rhs); - } - - template - auto operator()(const T0& lhs, const T1& rhs) const - -> typename std::enable_if_t::value && !std::is_same::value && - std::is_arithmetic::value && !std::is_same::value, bool> { - return op(double(lhs), double(rhs)); - } - - template - auto operator()(const T0&, const T1&) const - -> typename std::enable_if_t::value || std::is_same::value || - !std::is_arithmetic::value || std::is_same::value, bool> { - return false; - } - - bool operator()(const NullValue&, - const NullValue&) const { - // Should be unreachable; null is not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const std::vector&, - const std::vector&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const PropertyMap&, - const PropertyMap&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - }; - - template - bool compare(const Value& lhs, const Value& rhs, const Op& op) const { - return Value::binary_visit(lhs, rhs, Comparator { op }); - } - - bool equal(const Value& lhs, const Value& rhs) const { - return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); - } }; -inline bool Filter::operator()(const Feature& feature) const { - return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional { - auto it = feature.properties.find(key); - if (it == feature.properties.end()) - return {}; - return it->second; - }); -} - -template -bool Filter::operator()(const GeometryTileFeature& feature) const { - return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); }); -} - -template -bool Filter::operator()(FeatureType type, optional id, PropertyAccessor accessor) const { - return FilterBase::visit(*this, FilterEvaluator { type, id, accessor }); -} - } // namespace style } // namespace mbgl diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 3ffe200d01..63c8307803 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -1,6 +1,7 @@ #import "NSPredicate+MGLAdditions.h" #import "MGLValueEvaluator.h" +#import "MGLStyleValue_Private.h" class FilterEvaluator { public: @@ -194,6 +195,11 @@ public: NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) { return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"]; } + + NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { + id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression); + return [NSPredicate mgl_predicateWithJSONObject:jsonObject]; + } }; @implementation NSPredicate (MGLAdditions) diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 3b5e12b54a..6fb0d5e446 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -125,7 +125,7 @@ void FeatureIndex::addFeature( continue; } - if (options.filter && !(*options.filter)(*geometryTileFeature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(tileID.z), geometryTileFeature.get() })) { continue; } diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index a41a98fcaf..3bf85407c6 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -100,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, const size_t featureCount = sourceLayer->featureCount(); for (size_t i = 0; i < featureCount; ++i) { auto feature = sourceLayer->getFeature(i); - if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() })) continue; SymbolFeature ft(std::move(feature)); diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index bb7bb6ea98..fba149da12 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,11 +1,52 @@ #include #include +#include +#include +#include namespace mbgl { namespace style { namespace conversion { + +using GeometryValue = mapbox::geometry::value; -static optional normalizeValue(const optional& value, Error& error) { +// This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js +static bool isExpressionFilter(const Convertible& filter) { + if (!isArray(filter) || arrayLength(filter) == 0) { + return false; + } + + optional op = toString(arrayMember(filter, 0)); + + if (!op) { + return false; + + } else if (*op == "has") { + if (arrayLength(filter) < 2) return false; + optional operand = toString(arrayMember(filter, 1)); + return operand && *operand != "$id" && *operand != "$type"; + + } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") { + return false; + + } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") { + return arrayLength(filter) == 3 && (isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2))); + + } else if (*op == "any" || *op == "all") { + for (std::size_t i = 1; i < arrayLength(filter); i++) { + Convertible f = arrayMember(filter, i); + if (!isExpressionFilter(f) && !toBool(f)) { + return false; + } + } + return true; + + } else { + return true; + } +} + +static optional normalizeValue(const optional& value, Error& error) { if (!value) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -32,7 +73,7 @@ static optional toFeatureType(const Convertible& value, Error& erro } static optional toFeatureIdentifier(const Convertible& value, Error& error) { - optional identifier = toValue(value); + optional identifier = toValue(value); if (!identifier) { error = { "filter expression value must be a boolean, number, or string" }; return {}; @@ -99,7 +140,7 @@ optional convertEqualityFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { *filterValue } }; } else { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -121,7 +162,7 @@ optional convertBinaryFilter(const Convertible& value, Error& error) { return {}; } - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { return {}; } @@ -167,9 +208,9 @@ optional convertSetFilter(const Convertible& value, Error& error) { return { IdentifierFilterType { std::move(values) } }; } else { - std::vector values; + std::vector values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); + optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); if (!filterValue) { return {}; } @@ -193,8 +234,20 @@ optional convertCompoundFilter(const Convertible& value, Error& error) { return { FilterType { std::move(filters) } }; } + +optional convertExpressionFilter(const Convertible& value, Error& error) { + optional> expression = convert>(value, error, expression::type::Boolean); + if (!expression) { + return {}; + } + return { ExpressionFilter { std::move(*expression) } }; +} optional Converter::operator()(const Convertible& value, Error& error) const { + if (isExpressionFilter(value)) { + return convertExpressionFilter(value, error); + } + if (!isArray(value)) { error = { "filter expression must be an array" }; return {}; diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 6ae6fede42..7924a442c4 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -225,6 +225,10 @@ public: void operator()(const NotHasIdentifierFilter&) { stringifyUnaryFilter("!has", "$id"); } + + void operator()(const ExpressionFilter& filter) { + stringify(writer, filter.expression->serialize()); + } private: template diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp new file mode 100644 index 0000000000..51aa6bcf82 --- /dev/null +++ b/src/mbgl/style/filter.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +bool Filter::operator()(const expression::EvaluationContext &context) const { + return FilterBase::visit(*this, FilterEvaluator { context }); +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp new file mode 100644 index 0000000000..72022172f4 --- /dev/null +++ b/src/mbgl/style/filter_evaluator.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +namespace mbgl { +namespace style { + +template +struct Comparator { + const Op& op; + + template + bool operator()(const T& lhs, const T& rhs) const { + return op(lhs, rhs); + } + + template + auto operator()(const T0& lhs, const T1& rhs) const + -> typename std::enable_if_t::value && !std::is_same::value && + std::is_arithmetic::value && !std::is_same::value, bool> { + return op(double(lhs), double(rhs)); + } + + template + auto operator()(const T0&, const T1&) const + -> typename std::enable_if_t::value || std::is_same::value || + !std::is_arithmetic::value || std::is_same::value, bool> { + return false; + } + + bool operator()(const NullValue&, + const NullValue&) const { + // Should be unreachable; null is not currently allowed by the style specification. + assert(false); + return false; + } + + bool operator()(const std::vector&, + const std::vector&) const { + // Should be unreachable; nested values are not currently allowed by the style specification. + assert(false); + return false; + } + + bool operator()(const PropertyMap&, + const PropertyMap&) const { + // Should be unreachable; nested values are not currently allowed by the style specification. + assert(false); + return false; + } +}; + +template +bool compare(const Value& lhs, const Value& rhs, const Op& op) { + return Value::binary_visit(lhs, rhs, Comparator { op }); +} + +bool equal(const Value& lhs, const Value& rhs) { + return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); +} + +bool FilterEvaluator::operator()(const NullFilter&) const { + return true; +} + +bool FilterEvaluator::operator()(const EqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && equal(*actual, filter.value); +} + +bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return !actual || !equal(*actual, filter.value); +} + +bool FilterEvaluator::operator()(const LessThanFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); +} + +bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); +} + +bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); +} + +bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); +} + +bool FilterEvaluator::operator()(const InFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + if (!actual) + return false; + for (const auto& v: filter.values) { + if (equal(*actual, v)) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const NotInFilter& filter) const { + optional actual = context.feature->getValue(filter.key); + if (!actual) + return true; + for (const auto& v: filter.values) { + if (equal(*actual, v)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const AnyFilter& filter) const { + for (const auto& f: filter.filters) { + if (Filter::visit(f, *this)) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const AllFilter& filter) const { + for (const auto& f: filter.filters) { + if (!Filter::visit(f, *this)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const NoneFilter& filter) const { + for (const auto& f: filter.filters) { + if (Filter::visit(f, *this)) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const HasFilter& filter) const { + return bool(context.feature->getValue(filter.key)); +} + +bool FilterEvaluator::operator()(const NotHasFilter& filter) const { + return !context.feature->getValue(filter.key); +} + +bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const { + return context.feature->getType() == filter.value; +} + +bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const { + return context.feature->getType() != filter.value; +} + +bool FilterEvaluator::operator()(const TypeInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getType() == v) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getType() == v) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const { + return context.feature->getID() == filter.value; +} + +bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const { + return context.feature->getID() != filter.value; +} + +bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getID() == v) { + return true; + } + } + return false; +} + +bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const { + for (const auto& v: filter.values) { + if (context.feature->getID() == v) { + return false; + } + } + return true; +} + +bool FilterEvaluator::operator()(const HasIdentifierFilter&) const { + return bool(context.feature->getID()); +} + +bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { + return !context.feature->getID(); +} + +bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { + const expression::EvaluationResult result = filter.expression->evaluate(context); + if (result) { + const optional typed = expression::fromExpressionValue(*result); + return typed ? *typed : false; + } + return false; +} + +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index 33962ad87d..a2fefcfa9f 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -79,7 +79,7 @@ void CustomGeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (queryOptions.filter && !(*queryOptions.filter)(*feature)) { + if (queryOptions.filter && !(*queryOptions.filter)(style::expression::EvaluationContext { static_cast(id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index bbec899950..f211c03569 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -30,7 +30,7 @@ void GeoJSONTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a58c744065..82d0c91806 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -281,7 +281,7 @@ void GeometryTile::querySourceFeatures( auto feature = layer->getFeature(i); // Apply filter, if any - if (options.filter && !(*options.filter)(*feature)) { + if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) { continue; } diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 24841dd125..8b6f1f63bf 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -339,7 +339,7 @@ void GeometryTileWorker::redoLayout() { for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) { std::unique_ptr feature = geometryLayer->getFeature(i); - if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) + if (!filter(expression::EvaluationContext { static_cast(this->id.overscaledZ), feature.get() })) continue; GeometryCollection geometries = feature->getGeometries(); diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 73f8e7626d..6f261c43ec 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -10,114 +11,101 @@ using namespace mbgl; using namespace mbgl::style; -Filter parse(const char * expression) { +bool filter(const char * json, const PropertyMap& featureProperties = {{}}, optional featureId = {}, FeatureType featureType = FeatureType::Point, GeometryCollection featureGeometry = {}) { conversion::Error error; - optional filter = conversion::convertJSON(expression, error); + optional filter = conversion::convertJSON(json, error); EXPECT_TRUE(bool(filter)); - return *filter; -} - -Feature feature(const PropertyMap& properties, const Geometry& geometry = Point()) { - Feature result { geometry }; - result.properties = properties; - return result; + EXPECT_EQ(error.message, ""); + + StubGeometryTileFeature feature { featureId, featureType, featureGeometry, featureProperties }; + expression::EvaluationContext context = { &feature }; + + return (*filter)(context); } TEST(Filter, EqualsString) { - Filter f = parse(R"(["==", "foo", "bar"])"); - ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }}))); + auto f = R"(["==", "foo", "bar"])"; + ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("baz") }})); } TEST(Filter, EqualsNumber) { - Filter f = parse(R"(["==", "foo", 0])"); - ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }}))); - ASSERT_TRUE(f(feature({{ "foo", double(0) }}))); - ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", double(1) }}))); - ASSERT_FALSE(f(feature({{ "foo", std::string("0") }}))); - ASSERT_FALSE(f(feature({{ "foo", false }}))); - ASSERT_FALSE(f(feature({{ "foo", true }}))); - ASSERT_FALSE(f(feature({{ "foo", mapbox::geometry::null_value }}))); - ASSERT_FALSE(f(feature({{}}))); + auto f = R"(["==", "foo", 0])"; + ASSERT_TRUE(filter(f, {{ "foo", int64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", uint64_t(0) }})); + ASSERT_TRUE(filter(f, {{ "foo", double(0) }})); + ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", uint64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", double(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }})); + ASSERT_FALSE(filter(f, {{ "foo", false }})); + ASSERT_FALSE(filter(f, {{ "foo", true }})); + ASSERT_FALSE(filter(f, {{ "foo", mapbox::geometry::null_value }})); + ASSERT_FALSE(filter(f, {{}})); } TEST(Filter, EqualsType) { - Filter f = parse(R"(["==", "$type", "LineString"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); + auto f = R"(["==", "$type", "LineString"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {})); } TEST(Filter, InType) { - Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])"); - ASSERT_FALSE(f(feature({{}}, Point()))); - ASSERT_TRUE(f(feature({{}}, LineString()))); - ASSERT_TRUE(f(feature({{}}, Polygon()))); + auto f = R"(["in", "$type", "LineString", "Polygon"])"; + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString)); + ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon)); } TEST(Filter, Any) { - ASSERT_FALSE(parse("[\"any\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_FALSE(filter("[\"any\"]")); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"any\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, All) { - ASSERT_TRUE(parse("[\"all\"]")(feature({{}}))); - ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"all\"]", {{}})); + ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, None) { - ASSERT_TRUE(parse("[\"none\"]")(feature({{}}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")( - feature({{ std::string("foo"), int64_t(1) }}))); + ASSERT_TRUE(filter("[\"none\"]")); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"none\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } TEST(Filter, Has) { - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_TRUE(parse("[\"has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_FALSE(parse("[\"has\", \"foo\"]")( - feature({{}}))); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); + ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }})); + ASSERT_FALSE(filter("[\"has\", \"foo\"]")); } TEST(Filter, NotHas) { - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(1) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), int64_t(0) }}))); - ASSERT_FALSE(parse("[\"!has\", \"foo\"]")( - feature({{ std::string("foo"), false }}))); - ASSERT_TRUE(parse("[\"!has\", \"foo\"]")( - feature({{}}))); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); + ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), false }})); + ASSERT_TRUE(filter("[\"!has\", \"foo\"]")); } TEST(Filter, ID) { - Feature feature1 { Point() }; - feature1.id = { uint64_t(1234) }; - - ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1)); - ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1)); + FeatureIdentifier id1 { uint64_t{ 1234 } }; + ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1)); + ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1)); + + ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); +} - Feature feature2 { Point() }; - feature2.properties["id"] = { uint64_t(1234) }; +TEST(Filter, Expression) { + ASSERT_TRUE(filter("[\"==\", [\"+\", 1, 1], 2]")); + ASSERT_FALSE(filter("[\"==\", [\"+\", 1, 1], 4]")); +} - ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2)); +TEST(Filter, PropertyExpression) { + ASSERT_TRUE(filter("[\"==\", [\"get\", \"two\"], 2]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}})); } -- cgit v1.2.1 From ec91b83ea4543c7a0cf0056d48f2d7f52179e1e1 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Wed, 10 Jan 2018 18:12:00 -0500 Subject: [build] Update to Xcode 9.2 on CircleCI (#10893) When CircleCI says 9.2.0, they mean it... --- circle.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/circle.yml b/circle.yml index d76ab4a8ab..fac2d2158c 100644 --- a/circle.yml +++ b/circle.yml @@ -654,7 +654,7 @@ jobs: # ------------------------------------------------------------------------------ ios-debug: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 @@ -672,7 +672,7 @@ jobs: # ------------------------------------------------------------------------------ ios-sanitize-address: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 @@ -691,7 +691,7 @@ jobs: # ------------------------------------------------------------------------------ ios-sanitize-thread: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 @@ -710,7 +710,7 @@ jobs: # ------------------------------------------------------------------------------ macos-debug: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 @@ -731,7 +731,7 @@ jobs: # ------------------------------------------------------------------------------ macos-debug-qt5: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 @@ -756,7 +756,7 @@ jobs: # ------------------------------------------------------------------------------ macos-release-node4: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: RelWithDebInfo HOMEBREW_NO_AUTO_UPDATE: 1 @@ -777,7 +777,7 @@ jobs: # ------------------------------------------------------------------------------ macos-release-node6: macos: - xcode: "9.0" + xcode: "9.2.0" environment: BUILDTYPE: RelWithDebInfo HOMEBREW_NO_AUTO_UPDATE: 1 -- cgit v1.2.1 From 1b3d8f990f0ac524ad7142e31433717b1f8be81c Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 12 Mar 2018 13:57:22 -0400 Subject: [ios, build] Move iOS release builds to CircleCI --- circle.yml | 34 ++++++++++++++ platform/ios/bitrise.yml | 98 ---------------------------------------- platform/ios/scripts/document.sh | 2 +- 3 files changed, 35 insertions(+), 99 deletions(-) delete mode 100644 platform/ios/bitrise.yml diff --git a/circle.yml b/circle.yml index fac2d2158c..f2e82d2a7b 100644 --- a/circle.yml +++ b/circle.yml @@ -33,6 +33,12 @@ workflows: - ios-debug #- ios-sanitize-address - ios-sanitize-thread + - ios-release: + filters: + tags: + only: /ios-.*/ + branches: + ignore: /.*/ - macos-debug - macos-debug-qt5 - macos-release-node4: @@ -707,6 +713,34 @@ jobs: - *show-ccache-stats - *save-cache +# ------------------------------------------------------------------------------ + ios-release: + macos: + xcode: "9.2.0" + environment: + HOMEBREW_NO_AUTO_UPDATE: 1 + shell: /bin/bash --login -eo pipefail + steps: + - checkout + - *install-macos-dependencies + - run: + name: Install packaging dependencies + command: | + echo "ruby-2.3" > ~/.ruby-version + sudo gem install jazzy --no-document + brew install awscli wget + - *generate-cache-key + - *restore-cache + - *reset-ccache-stats + - run: + name: Build, package, and upload iOS release + command: | + export VERSION_TAG=${CIRCLE_TAG} + export GITHUB_TOKEN=${DANGER_GITHUB_API_TOKEN} + platform/ios/scripts/deploy-packages.sh + - *show-ccache-stats + - *save-cache + # ------------------------------------------------------------------------------ macos-debug: macos: diff --git a/platform/ios/bitrise.yml b/platform/ios/bitrise.yml deleted file mode 100644 index 24bd054dbc..0000000000 --- a/platform/ios/bitrise.yml +++ /dev/null @@ -1,98 +0,0 @@ ---- -format_version: 1.0.0 -default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git -trigger_map: -- pattern: nightly-release - workflow: nightly-release -- pattern: release-from-tag - workflow: release-from-tag -- pattern: "*" - is_pull_request_allowed: true - workflow: primary -workflows: - primary: - steps: - - script: - title: Skip Workflow - inputs: - - content: echo "This workflow is obsolete — see CircleCi." - nightly-release: - steps: - - script: - title: Install Dependencies - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - brew install cmake - - is_debug: 'yes' - - script: - title: Configure AWS-CLI - inputs: - - content: |- - #!/bin/bash - pip install awscli - - script: - title: Build package - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - export BUILDTYPE=Release - export BUILD_DEVICE=true - export FORMAT=dynamic - make ipackage-strip - CLOUDWATCH=true platform/ios/scripts/metrics.sh - platform/ios/scripts/deploy-nightly.sh - - is_debug: 'yes' - - slack: - title: Post to Slack - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise iOS Nightly 💤' - - from_username_on_error: 'Bitrise iOS Nightly 💤' - - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for - completed successfully.' - - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for - failed.' - - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png - - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png - release-from-tag: - steps: - - script: - title: Install Dependencies - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - brew install cmake - sudo easy_install pip - sudo pip install awscli - - is_debug: 'yes' - - script: - title: Build package - inputs: - - content: |- - #!/bin/bash - set -eu -o pipefail - export VERSION_TAG=${BITRISE_GIT_TAG} - platform/ios/scripts/deploy-packages.sh - - is_debug: 'yes' - - slack: - title: Post to Slack - inputs: - - webhook_url: "$SLACK_HOOK_URL" - - channel: "#gl-bots" - - from_username: 'Bitrise iOS Deploy' - - from_username_on_error: 'Bitrise iOS Deploy' - - message: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for - completed successfully.' - - message_on_error: '<${BITRISE_BUILD_URL}|Build #${BITRISE_BUILD_NUMBER}> - for - failed.' - - icon_url: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-icon-128.png - - icon_url_on_error: https://bitrise-public-content-production.s3.amazonaws.com/slack/bitrise-slack-error-icon-128.png diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh index 57b596a4b9..43cbe3067a 100755 --- a/platform/ios/scripts/document.sh +++ b/platform/ios/scripts/document.sh @@ -6,7 +6,7 @@ set -u if [ -z `which jazzy` ]; then echo "Installing jazzy…" - gem install jazzy --no-rdoc --no-ri + gem install jazzy --no-document if [ -z `which jazzy` ]; then echo "Unable to install jazzy. See https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/INSTALL.md" exit 1 -- cgit v1.2.1 From 40e5af700b387d4ff5dcec4b9f056bc4a7e8e383 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 12 Mar 2018 17:17:36 -0400 Subject: [ios] Prepare changelog for ios-v3.7.6 --- platform/ios/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 49b2add722..e7277a2e10 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,8 +2,11 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 3.7.6 +## 3.7.6 - March 12, 2018 +* Fixed an issue where full-resolution tiles could fail to replace lower-resolution placeholders. ([#11227](https://github.com/mapbox/mapbox-gl-native/pull/11227)) +* Fixed an issue where tilesets with bounds that cover the entire world would fail to render. ([#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425)) +* Fixed a memory leak in `MGLMapSnapshotter`. ([#11193](https://github.com/mapbox/mapbox-gl-native/pull/11193)) * Fixed an issue where the pinch gesture could drift beyond bounds imposed by `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]`. ([#11423](https://github.com/mapbox/mapbox-gl-native/pull/11423)) * Improved the visibility of the heading indicator arrow. ([#11337](https://github.com/mapbox/mapbox-gl-native/pull/11337)) -- cgit v1.2.1 From 4544b7aae9ab96ff89f3e46a1ee38ce0af95054f Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 12 Mar 2018 17:19:34 -0400 Subject: [ios] Bump podspecs for ios-v3.7.6 --- platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index bf2f854b50..268b7b9da6 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.5' + version = '3.7.6' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index e0c787e5da..a1b2441d29 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.5' + version = '3.7.6' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index ea7ce47a8b..7e696a23cf 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.5' + version = '3.7.6' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From d7bc36db6f3aa46246bc1aae1c5c63c56b5ce262 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 7 Mar 2018 15:17:16 +0200 Subject: [core] prevent render thread from waking up unneeded on still rendering - No longer sends a update message to the render frontend when rendering in still mode only to shortcut it in the renderer. This prevents unneeded render passes in qt. --- src/mbgl/map/map.cpp | 5 +++++ src/mbgl/renderer/renderer_impl.cpp | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 947973415a..525291a628 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -748,6 +748,11 @@ void Map::Impl::onInvalidate() { } void Map::Impl::onUpdate() { + // Don't load/render anything in still mode until explicitly requested. + if (mode != MapMode::Continuous && !stillImageRequest) { + return; + } + TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max(); transform.updateTransitions(timePoint); diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 2ac714e122..962a6571db 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -85,11 +85,6 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) { void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (updateParameters.mode != MapMode::Continuous) { - // Don't load/render anyting in still mode until explicitly requested. - if (!updateParameters.stillImageRequest) { - return; - } - // Reset zoom history state. zoomHistory.first = true; } -- cgit v1.2.1 From 690c7e58556335220791af156660a65af1fdebd6 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 19 Mar 2018 11:19:39 +0100 Subject: [android] - verify optional before accessing it --- platform/android/src/file_source.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp index 612619a30b..d8d715dbd3 100644 --- a/platform/android/src/file_source.cpp +++ b/platform/android/src/file_source.cpp @@ -70,14 +70,16 @@ void FileSource::resume(jni::JNIEnv&) { activationCounter.value()++; if (activationCounter == 1) { - fileSource->resume(); + fileSource->resume(); } } void FileSource::pause(jni::JNIEnv&) { - activationCounter.value()--; - if (activationCounter == 0) { - fileSource->pause(); + if (activationCounter) { + activationCounter.value()--; + if (activationCounter == 0) { + fileSource->pause(); + } } } -- cgit v1.2.1 From 553efa38e3591ce62863d4d74222710f8e3c2337 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 20 Mar 2018 17:18:42 +0100 Subject: [android] - update changelog for v6.0.0-beta.4 (#11486) --- platform/android/CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 60d3dc6b70..bae36142f5 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,28 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.0-beta.4 - March 20, 2018 + - Gesture library 0.1.0 stable [#11483](https://github.com/mapbox/mapbox-gl-native/pull/11483) + - Filters with expressions [#11429](https://github.com/mapbox/mapbox-gl-native/pull/11429) + - Telemetry 3.0.0-beta.2 [#11474](https://github.com/mapbox/mapbox-gl-native/pull/11474) + - High level JNI conversion for geojson [#11471](https://github.com/mapbox/mapbox-gl-native/pull/11471) + - Update to MAS 3.0.0-beta.4 [#11468](https://github.com/mapbox/mapbox-gl-native/pull/11468) + - Support for expression literal on arrays [#11457](https://github.com/mapbox/mapbox-gl-native/pull/11457) + - Fix telemetry integration for two finger tap gesture [#11460](https://github.com/mapbox/mapbox-gl-native/pull/11460) + - Revisit proguard configuration [#11434](https://github.com/mapbox/mapbox-gl-native/pull/11434) + - Expressions accessor support [#11352](https://github.com/mapbox/mapbox-gl-native/pull/11352) + - Calculate camera's LatLng for bounds without map padding [#11410](https://github.com/mapbox/mapbox-gl-native/pull/11410) + - Check for null on http body request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413) + - Expose more gesture settings [#11407](https://github.com/mapbox/mapbox-gl-native/pull/11407) + - Revert java 8 language support [#11398](https://github.com/mapbox/mapbox-gl-native/pull/11398) + - Update to MAS 3.0.0-beta.3 [#11373](https://github.com/mapbox/mapbox-gl-native/pull/11373) + - Rework match expression to style specification syntax [#11388](https://github.com/mapbox/mapbox-gl-native/pull/11388) + - Update javadoc configuration for Gradle 4.4 [#11384](https://github.com/mapbox/mapbox-gl-native/pull/11384) + - Rework zoomIn and zoomOut to use ValueAnimators [#11382](https://github.com/mapbox/mapbox-gl-native/pull/11382) + - Delete LocalRef when converting Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350) + - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) + - Validate width/height when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364) + ## 6.0.0-beta.3 - March 2, 2018 - Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) - Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201) -- cgit v1.2.1 From 58e4d395bfd2a7c9871dcef81c32141f5308f4e4 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 26 Mar 2018 09:40:38 -0400 Subject: [android] - don't load default style if style json string was set (#11519) --- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 8 +-- .../mapboxsdk/testapp/style/StyleLoaderTest.java | 77 ++++++++++++++++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index ba116e1278..85ac99a188 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -111,7 +111,7 @@ public final class MapboxMap { void onStart() { nativeMapView.update(); trackingSettings.onStart(); - if (TextUtils.isEmpty(nativeMapView.getStyleUrl())) { + if (TextUtils.isEmpty(nativeMapView.getStyleUrl()) && TextUtils.isEmpty(nativeMapView.getStyleJson())) { // if user hasn't loaded a Style yet nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); } @@ -1882,7 +1882,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * * @deprecated Use {@link #addOnScrollListener(OnScrollListener)} instead. */ @Deprecated @@ -1895,7 +1894,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void addOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onAddScrollListener(listener); @@ -1906,7 +1904,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is scrolled. * To unset the callback, use null. - * */ public void removeOnScrollListener(@Nullable OnScrollListener listener) { onRegisterTouchListener.onRemoveScrollListener(listener); @@ -1917,7 +1914,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the map is flinged. * To unset the callback, use null. - * * @deprecated Use {@link #addOnFlingListener(OnFlingListener)} instead. */ @Deprecated @@ -1950,7 +1946,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapClickListener(OnMapClickListener)} instead. */ @Deprecated @@ -1983,7 +1978,6 @@ public final class MapboxMap { * * @param listener The callback that's invoked when the user long clicks on the map view. * To unset the callback, use null. - * * @deprecated Use {@link #addOnMapLongClickListener(OnMapLongClickListener)} instead. */ @Deprecated diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java new file mode 100644 index 0000000000..1a5201193c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java @@ -0,0 +1,77 @@ +package com.mapbox.mapboxsdk.testapp.style; + + +import android.support.test.espresso.UiController; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction; +import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest; +import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity; +import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils; + +import org.junit.Test; + +import java.io.IOException; + +import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke; +import static org.junit.Assert.assertEquals; + +/** + * Tests around style loading + */ +public class StyleLoaderTest extends BaseActivityTest { + + + @Override + protected Class getActivityClass() { + return EspressoTestActivity.class; + } + + @Test + public void testSetGetStyleJsonString() throws Exception { + validateTestSetup(); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + try { + String expected = ResourceUtils.readRawResource(rule.getActivity(), R.raw.local_style); + mapboxMap.setStyleJson(expected); + String actual = mapboxMap.getStyleJson(); + assertEquals("Style json should match", expected, actual); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + }); + } + + @Test + public void testDefaultStyleLoadWithActivityLifecycleChange() throws Exception { + validateTestSetup(); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + try { + String expected = ResourceUtils.readRawResource(rule.getActivity(), R.raw.local_style); + mapboxMap.setStyleJson(expected); + + // fake activity stop/start + MapView mapView = (MapView) rule.getActivity().findViewById(R.id.mapView); + mapView.onPause(); + mapView.onStop(); + + mapView.onStart(); + mapView.onResume(); + + String actual = mapboxMap.getStyleJson(); + assertEquals("Style URL should be empty", "", mapboxMap.getStyleUrl()); + assertEquals("Style json should match", expected, actual); + } catch (IOException exception) { + exception.printStackTrace(); + } + } + }); + } +} \ No newline at end of file -- cgit v1.2.1 From 69dfd1ed3eaf279a6fadcf6482145532a93db846 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 1 Mar 2018 11:04:32 +0100 Subject: [android] - delete local ref pixel buffer when converting Image.java to core --- platform/android/src/map/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp index 52e0e0d255..c3b22b0054 100644 --- a/platform/android/src/map/image.cpp +++ b/platform/android/src/map/image.cpp @@ -29,7 +29,7 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, jni::Object image) { } jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast(premultipliedImage.data.get())); - + jni::DeleteLocalRef(env, pixels); return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio}; } -- cgit v1.2.1 From 804738fe145c43628648c8dd352d649b57bc7037 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Mar 2018 10:21:43 +0100 Subject: [android] - use float for pixelratio when creating a snapshotter --- .../main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index 1c59bb468e..161b4c7380 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -82,7 +82,7 @@ public class MapSnapshotter { * MapSnapshotter options */ public static class Options { - private int pixelRatio = 1; + private float pixelRatio = 1; private int width; private int height; private String styleUrl = Style.MAPBOX_STREETS; @@ -122,7 +122,7 @@ public class MapSnapshotter { * @param pixelRatio the pixel ratio to use (default: 1) * @return the mutated {@link Options} */ - public Options withPixelRatio(int pixelRatio) { + public Options withPixelRatio(float pixelRatio) { this.pixelRatio = pixelRatio; return this; } @@ -164,7 +164,7 @@ public class MapSnapshotter { /** * @return the pixel ratio */ - public int getPixelRatio() { + public float getPixelRatio() { return pixelRatio; } -- cgit v1.2.1 From 3a1db905fc2258a67385a742393893d982ca896c Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 2 Mar 2018 10:05:04 +0100 Subject: [android] - validate if width and height aren't 0 when creating options class --- .../src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index 161b4c7380..0895096f6e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -95,6 +95,9 @@ public class MapSnapshotter { * @param height the height of the image */ public Options(int width, int height) { + if (width == 0 || height == 0) { + throw new IllegalArgumentException("Unable to create a snapshot with width or height set to 0"); + } this.width = width; this.height = height; } -- cgit v1.2.1 From f2eb250518b3ca6d8956936362cc9568c277d609 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 8 Mar 2018 07:30:31 +0100 Subject: [android] - check for null body on http request, cleanup code --- .../com/mapbox/mapboxsdk/http/HTTPRequest.java | 168 +++++++++++---------- platform/android/src/http_file_source.cpp | 2 +- 2 files changed, 91 insertions(+), 79 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java index 129e75965e..3491ec5c85 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java @@ -3,11 +3,14 @@ package com.mapbox.mapboxsdk.http; import android.content.Context; import android.content.pm.PackageInfo; import android.os.Build; +import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.Mapbox; import com.mapbox.mapboxsdk.constants.MapboxConstants; +import com.mapbox.services.android.telemetry.utils.TelemetryUtils; import java.io.IOException; import java.io.InterruptedIOException; @@ -26,55 +29,42 @@ import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import timber.log.Timber; -import static com.mapbox.services.android.telemetry.utils.TelemetryUtils.toHumanReadableAscii; +import okhttp3.ResponseBody; +import timber.log.Timber; import static android.util.Log.DEBUG; +import static android.util.Log.ERROR; import static android.util.Log.INFO; +import static android.util.Log.VERBOSE; import static android.util.Log.WARN; class HTTPRequest implements Callback { - private static OkHttpClient mClient = new OkHttpClient.Builder().dispatcher(getDispatcher()).build(); - private static boolean logEnabled = true; - private static boolean logRequestUrl = false; - - private String USER_AGENT_STRING = null; - private static final int CONNECTION_ERROR = 0; private static final int TEMPORARY_ERROR = 1; private static final int PERMANENT_ERROR = 2; - // Reentrancy is not needed, but "Lock" is an - // abstract class. - private ReentrantLock mLock = new ReentrantLock(); - - private long mNativePtr = 0; - - private Call mCall; - private Request mRequest; - - private static Dispatcher getDispatcher() { - Dispatcher dispatcher = new Dispatcher(); - // Matches core limit set on - // https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/src/http_file_source.cpp#L192 - dispatcher.setMaxRequestsPerHost(20); - return dispatcher; - } - - private native void nativeOnFailure(int type, String message); + private static OkHttpClient client = new OkHttpClient.Builder().dispatcher(getDispatcher()).build(); + private static boolean logEnabled = true; + private static boolean logRequestUrl = false; - private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, - String retryAfter, String xRateLimitReset, byte[] body); + // Reentrancy is not needed, but "Lock" is an abstract class. + private ReentrantLock lock = new ReentrantLock(); + private String userAgentString; + private long nativePtr = 0; + private Call call; private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) { - mNativePtr = nativePtr; + this.nativePtr = nativePtr; try { HttpUrl httpUrl = HttpUrl.parse(resourceUrl); - final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE); + if (httpUrl == null) { + log(Log.ERROR, String.format("[HTTP] Unable to parse resourceUrl %s", resourceUrl)); + } + final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE); // Don't try a request to remote server if we aren't connected if (!Mapbox.isConnected() && !host.equals("127.0.0.1") && !host.equals("localhost")) { throw new NoRouteToHostException("No Internet connection available."); @@ -99,18 +89,18 @@ class HTTPRequest implements Callback { } else if (modified.length() > 0) { builder = builder.addHeader("If-Modified-Since", modified); } - mRequest = builder.build(); - mCall = mClient.newCall(mRequest); - mCall.enqueue(this); + Request request = builder.build(); + call = client.newCall(request); + call.enqueue(this); } catch (Exception exception) { - handleFailure(mCall, exception); + handleFailure(call, exception); } } public void cancel() { - // mCall can be null if the constructor gets aborted (e.g, under a NoRouteToHostException). - if (mCall != null) { - mCall.cancel(); + // call can be null if the constructor gets aborted (e.g, under a NoRouteToHostException). + if (call != null) { + call.cancel(); } // TODO: We need a lock here because we can try @@ -118,37 +108,40 @@ class HTTPRequest implements Callback { // answered on the OkHTTP thread. We could get rid of // this lock by using Runnable when we move Android // implementation of mbgl::RunLoop to Looper. - mLock.lock(); - mNativePtr = 0; - mLock.unlock(); + lock.lock(); + nativePtr = 0; + lock.unlock(); } @Override - public void onResponse(Call call, Response response) throws IOException { + public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { + if (response.isSuccessful()) { + log(VERBOSE, String.format("[HTTP] Request was successful (code = %s).", response.code())); + } else { + // We don't want to call this unsuccessful because a 304 isn't really an error + String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information"; + log(DEBUG, String.format("[HTTP] Request with response code = %s: %s", response.code(), message)); + } - if (logEnabled) { - if (response.isSuccessful()) { - Timber.v("[HTTP] Request was successful (code = %s).", response.code()); - } else { - // We don't want to call this unsuccessful because a 304 isn't really an error - String message = !TextUtils.isEmpty(response.message()) ? response.message() : "No additional information"; - Timber.d("[HTTP] Request with response code = %s: %s", response.code(), message); - } + ResponseBody responseBody = response.body(); + if (responseBody == null) { + log(ERROR, "[HTTP] Received empty response body"); + return; } byte[] body; try { - body = response.body().bytes(); + body = responseBody.bytes(); } catch (IOException ioException) { onFailure(call, ioException); // throw ioException; return; } finally { - response.body().close(); + response.close(); } - mLock.lock(); - if (mNativePtr != 0) { + lock.lock(); + if (nativePtr != 0) { nativeOnResponse(response.code(), response.header("ETag"), response.header("Last-Modified"), @@ -158,14 +151,34 @@ class HTTPRequest implements Callback { response.header("x-rate-limit-reset"), body); } - mLock.unlock(); + lock.unlock(); } @Override - public void onFailure(Call call, IOException e) { + public void onFailure(@NonNull Call call, @NonNull IOException e) { handleFailure(call, e); } + static void enableLog(boolean enabled) { + logEnabled = enabled; + } + + static void enablePrintRequestUrlOnFailure(boolean enabled) { + logRequestUrl = enabled; + } + + static void setOKHttpClient(OkHttpClient client) { + HTTPRequest.client = client; + } + + private static Dispatcher getDispatcher() { + Dispatcher dispatcher = new Dispatcher(); + // Matches core limit set on + // https://github.com/mapbox/mapbox-gl-native/blob/master/platform/android/src/http_file_source.cpp#L192 + dispatcher.setMaxRequestsPerHost(20); + return dispatcher; + } + private void handleFailure(Call call, Exception e) { String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request"; int type = getFailureType(e); @@ -175,11 +188,11 @@ class HTTPRequest implements Callback { logFailure(type, errorMessage, requestUrl); } - mLock.lock(); - if (mNativePtr != 0) { + lock.lock(); + if (nativePtr != 0) { nativeOnFailure(type, errorMessage); } - mLock.unlock(); + lock.unlock(); } private int getFailureType(Exception e) { @@ -192,19 +205,26 @@ class HTTPRequest implements Callback { return PERMANENT_ERROR; } + private void log(int type, String errorMessage) { + if (logEnabled) { + Timber.log(type, errorMessage); + } + } + private void logFailure(int type, String errorMessage, String requestUrl) { - Timber.log( - type == TEMPORARY_ERROR ? DEBUG : type == CONNECTION_ERROR ? INFO : WARN, - "Request failed due to a %s error: %s %s", - type == TEMPORARY_ERROR ? "temporary" : type == CONNECTION_ERROR ? "connection" : "permanent", - errorMessage, - logRequestUrl ? requestUrl : "" + log(type == TEMPORARY_ERROR ? DEBUG : type == CONNECTION_ERROR ? INFO : WARN, + String.format( + "Request failed due to a %s error: %s %s", + type == TEMPORARY_ERROR ? "temporary" : type == CONNECTION_ERROR ? "connection" : "permanent", + errorMessage, + logRequestUrl ? requestUrl : "" + ) ); } private String getUserAgent() { - if (USER_AGENT_STRING == null) { - return USER_AGENT_STRING = toHumanReadableAscii( + if (userAgentString == null) { + userAgentString = TelemetryUtils.toHumanReadableAscii( String.format("%s %s (%s) Android/%s (%s)", getApplicationIdentifier(), BuildConfig.MAPBOX_VERSION_STRING, @@ -212,9 +232,8 @@ class HTTPRequest implements Callback { Build.VERSION.SDK_INT, Build.CPU_ABI) ); - } else { - return USER_AGENT_STRING; } + return userAgentString; } private String getApplicationIdentifier() { @@ -227,15 +246,8 @@ class HTTPRequest implements Callback { } } - static void enableLog(boolean enabled) { - logEnabled = enabled; - } - - static void enablePrintRequestUrlOnFailure(boolean enabled) { - logRequestUrl = enabled; - } + private native void nativeOnFailure(int type, String message); - static void setOKHttpClient(OkHttpClient client) { - mClient = client; - } + private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, + String retryAfter, String xRateLimitReset, byte[] body); } diff --git a/platform/android/src/http_file_source.cpp b/platform/android/src/http_file_source.cpp index 8eb9416e9d..cda84209ea 100644 --- a/platform/android/src/http_file_source.cpp +++ b/platform/android/src/http_file_source.cpp @@ -61,7 +61,7 @@ void RegisterNativeHTTPRequest(jni::JNIEnv& env) { #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) - jni::RegisterNativePeer(env, HTTPRequest::javaClass, "mNativePtr", + jni::RegisterNativePeer(env, HTTPRequest::javaClass, "nativePtr", METHOD(&HTTPRequest::onFailure, "nativeOnFailure"), METHOD(&HTTPRequest::onResponse, "nativeOnResponse")); } -- cgit v1.2.1 From b411eedf501e6c1b7bae712c201723a5b4e812ba Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 26 Mar 2018 12:15:05 -0400 Subject: [android] - update changelog v5.5.1 --- platform/android/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index d0d29eabf6..dc7ff98828 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,15 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.1 - March 26, 2018 + - Verify optional access of FileSource deactivation [#11480](https://github.com/mapbox/mapbox-gl-native/pull/11480) + - Prevent default style loading when style json was set [#11519](https://github.com/mapbox/mapbox-gl-native/pull/11519) + - Delete local reference when convering Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350) + - Use float for pixel ratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) + - Validate if width and height aren't 0 when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364) + - Null check body of http request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413) + - Clamp TileJSON bounds [#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425) + ## 5.5.0 - March 1, 2018 - TileJSON Bounds allows values inclusive of world extents [#11178](https://github.com/mapbox/mapbox-gl-native/pull/11178) - LatLngBounds returned by VisibleRegion when map is rotated [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226) -- cgit v1.2.1 From dc874f2d5f246b14145332b05656494116565253 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 26 Mar 2018 12:19:42 -0400 Subject: [android] - bump snapshot version --- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index f5dc431d40..de93849169 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.5.1-SNAPSHOT +VERSION_NAME=5.5.2-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 4a0e0251348806501f3e2f67eb868c6657aaf3b7 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 3 Apr 2018 15:59:35 +0300 Subject: [core] re-bind uniform locations after re-linking program - On some gl implementations the uniform locations are shifted after linking the program a second time, resulting in errors when using any uniform. On some implementations they are actually doubled. Re-binding the uniforms after selectively binding the vertex attributes prevents this. --- src/mbgl/gl/program.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 3b54ec194a..af02ad3d54 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -36,8 +36,13 @@ public: context.createShader(ShaderType::Fragment, fragmentSource))), uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), attributeLocations(Attributes::bindLocations(program)) { + // Re-link program after manually binding only active attributes in Attributes::bindLocations context.linkProgram(program); + + // We have to re-initialize the uniforms state from the bindings as the uniform locations + // get shifted on some implementations + uniformsState = Uniforms::bindLocations(program); } template -- cgit v1.2.1 From 006d2ab1d5f65242d9e53e9d804c01ac2d450537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Thu, 5 Apr 2018 14:03:44 +0200 Subject: CP missing changelog entries to master (#11596) * [android] - update changelog v5.5.1 (cherry picked from commit b411eed) * [android] Updated changelog for 6.0.0-beta.5 release (cherry picked from commit 73c0159) * Release android v6.0.0 beta.6 (#11592) * [android] updated changelog for 6.0.0-beta.6 (cherry picked from commit d5a3e5b) --- platform/android/CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index bae36142f5..534f628c37 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,33 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.0-beta.6 - April 4, 2018 + - Fix race condition crash for heavily modified annotations [#11551](https://github.com/mapbox/mapbox-gl-native/pull/11551) + - Throw exception when converting PropertyValue with an expression [#11572](https://github.com/mapbox/mapbox-gl-native/pull/11572) + - Invalidate camera position before delivering onMapReady [#11585](https://github.com/mapbox/mapbox-gl-native/pull/11585) + - Rework logical condition convenience expressions [#11555](https://github.com/mapbox/mapbox-gl-native/pull/11555) + - Telemetry library version update to 3.0.0-beta.4 [#11590](https://github.com/mapbox/mapbox-gl-native/pull/11590) + +## 6.0.0-beta.5 - March 27, 2018 + - Avoid flashing on pitched overzoomed tiles [#11488](https://github.com/mapbox/mapbox-gl-native/pull/11488) + - Literal array conversion of primitive arrays [#11500](https://github.com/mapbox/mapbox-gl-native/pull/11500) + - Make default output from step more descriptive [#11501](https://github.com/mapbox/mapbox-gl-native/pull/11501) + - Expose public Telemetry API [#11503](https://github.com/mapbox/mapbox-gl-native/pull/11503) + - Convert Android int colors with to-color expression [#11506](https://github.com/mapbox/mapbox-gl-native/pull/11506) + - Prevent default style reload when string style json was set [#11520](https://github.com/mapbox/mapbox-gl-native/pull/11520) + - String, number and bool Expressions with multiple values [#11512](https://github.com/mapbox/mapbox-gl-native/pull/11512) + - Telemetry library 3.0.0-beta.3 [#11534](https://github.com/mapbox/mapbox-gl-native/pull/11534) + - Gestures library v0.2.0 [#11535](https://github.com/mapbox/mapbox-gl-native/pull/11535) + +## 5.5.1 - March 26, 2018 + - Verify optional access of FileSource deactivation [#11480](https://github.com/mapbox/mapbox-gl-native/pull/11480) + - Prevent default style loading when style json was set [#11519](https://github.com/mapbox/mapbox-gl-native/pull/11519) + - Delete local reference when convering Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350) + - Use float for pixel ratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) + - Validate if width and height aren't 0 when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364) + - Null check body of http request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413) + - Clamp TileJSON bounds [#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425) + ## 6.0.0-beta.4 - March 20, 2018 - Gesture library 0.1.0 stable [#11483](https://github.com/mapbox/mapbox-gl-native/pull/11483) - Filters with expressions [#11429](https://github.com/mapbox/mapbox-gl-native/pull/11429) -- cgit v1.2.1 From 4ba63d541b61502abcfdb5f21319d6fa21f92feb Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 5 Apr 2018 10:57:52 -0400 Subject: Remove unused lambda capture (#11601) Fixes #11588 --- platform/node/src/node_thread_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/node/src/node_thread_pool.cpp b/platform/node/src/node_thread_pool.cpp index fd6df575fc..1f37565e8a 100644 --- a/platform/node/src/node_thread_pool.cpp +++ b/platform/node/src/node_thread_pool.cpp @@ -6,7 +6,7 @@ namespace node_mbgl { NodeThreadPool::NodeThreadPool() - : queue(new util::AsyncQueue>(uv_default_loop(), [this](std::weak_ptr mailbox) { + : queue(new util::AsyncQueue>(uv_default_loop(), [](std::weak_ptr mailbox) { Worker* worker = new Worker(mailbox); Nan::AsyncQueueWorker(worker); })) { -- cgit v1.2.1 From 05d24448bdea29e302f788f050829459adba8e83 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Thu, 5 Apr 2018 11:14:55 -0400 Subject: Remove unused lambda capture (#11600) Fixes #11588 --- platform/node/src/node_thread_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/node/src/node_thread_pool.cpp b/platform/node/src/node_thread_pool.cpp index fd6df575fc..1f37565e8a 100644 --- a/platform/node/src/node_thread_pool.cpp +++ b/platform/node/src/node_thread_pool.cpp @@ -6,7 +6,7 @@ namespace node_mbgl { NodeThreadPool::NodeThreadPool() - : queue(new util::AsyncQueue>(uv_default_loop(), [this](std::weak_ptr mailbox) { + : queue(new util::AsyncQueue>(uv_default_loop(), [](std::weak_ptr mailbox) { Worker* worker = new Worker(mailbox); Nan::AsyncQueueWorker(worker); })) { -- cgit v1.2.1 From f55a324917e979e85b97ba56b6de765ca1cf4267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 10 Apr 2018 15:32:32 +0200 Subject: [android] - fixed scale animation focal point (#11643) --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 1788cb4428..07106689be 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -662,7 +662,8 @@ final class MapGestureDetector { @Override public void onAnimationUpdate(ValueAnimator animation) { - transform.setZoom((Float) animation.getAnimatedValue(), scalePointBegin, 0, true); + PointF scalePoint = focalPoint != null ? focalPoint : scalePointBegin; + transform.setZoom((Float) animation.getAnimatedValue(), scalePoint, 0, true); } }); animator.addListener(new AnimatorListenerAdapter() { -- cgit v1.2.1 From 88d21e17dc218638fad232c050e67797a3c5cb9d Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 10 Apr 2018 11:10:14 +0200 Subject: [android] - add paused state to map renderer, don't render snapshot when paused (#11358) --- .../com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java | 8 ++++++-- .../activity/imagegenerator/SnapshotActivity.java | 8 ++++++++ platform/android/src/map_renderer.cpp | 16 ++++++++++++++-- platform/android/src/map_renderer.hpp | 5 +++++ platform/android/src/native_map_view.hpp | 1 - 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index fcee5bd179..f1c70325a0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -37,11 +37,11 @@ public abstract class MapRenderer implements MapRendererScheduler { } public void onPause() { - // Implement if needed + nativeOnPause(); } public void onResume() { - // Implement if needed + nativeOnResume(); } public void onStop() { @@ -124,6 +124,10 @@ public abstract class MapRenderer implements MapRendererScheduler { private native void nativeRender(); + private native void nativeOnResume(); + + private native void nativeOnPause(); + private long frames; private long timeElapsed; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java index 655012f799..0910045885 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.java @@ -17,6 +17,8 @@ import com.mapbox.mapboxsdk.testapp.R; import java.util.Locale; +import timber.log.Timber; + /** * Test activity showcasing the Snapshot API to create and display a bitmap of the current shown Map. */ @@ -79,6 +81,12 @@ public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCal @Override protected void onPause() { super.onPause(); + mapboxMap.snapshot(new MapboxMap.SnapshotReadyCallback() { + @Override + public void onSnapshotReady(Bitmap snapshot) { + Timber.e("Regression test for https://github.com/mapbox/mapbox-gl-native/pull/11358"); + } + }); mapView.onPause(); } diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp index 2440ac93ef..f7e16b7091 100644 --- a/platform/android/src/map_renderer.cpp +++ b/platform/android/src/map_renderer.cpp @@ -136,7 +136,7 @@ void MapRenderer::render(JNIEnv&) { renderer->render(*params); // Deliver the snapshot if requested - if (snapshotCallback) { + if (snapshotCallback && !paused) { snapshotCallback->operator()(backend->readFramebuffer()); snapshotCallback.reset(); } @@ -174,6 +174,14 @@ void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { requestRender(); } +void MapRenderer::onResume(JNIEnv&) { + paused = false; +} + +void MapRenderer::onPause(JNIEnv&) { + paused = true; +} + // Static methods // jni::Class MapRenderer::javaClass; @@ -192,7 +200,11 @@ void MapRenderer::registerNative(jni::JNIEnv& env) { METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"), METHOD(&MapRenderer::onSurfaceChanged, - "nativeOnSurfaceChanged")); + "nativeOnSurfaceChanged"), + METHOD(&MapRenderer::onResume, + "nativeOnResume"), + METHOD(&MapRenderer::onPause, + "nativeOnPause")); } MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object jObject) { diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp index c36357af7a..5fb5ef1a61 100644 --- a/platform/android/src/map_renderer.hpp +++ b/platform/android/src/map_renderer.hpp @@ -98,6 +98,10 @@ private: void onSurfaceChanged(JNIEnv&, jint width, jint height); + void onResume(JNIEnv&); + + void onPause(JNIEnv&); + private: GenericUniqueWeakObject javaPeer; @@ -120,6 +124,7 @@ private: std::mutex updateMutex; bool framebufferSizeChanged = false; + std::atomic paused {false}; std::unique_ptr snapshotCallback; }; diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index d7e3b17b99..1b6371d726 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -257,7 +257,6 @@ private: MapRenderer& mapRenderer; std::string styleUrl; - std::string apiKey; float pixelRatio; -- cgit v1.2.1 From 46d3756cbd537fdde57f6ec64497b97c6050cb4d Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Tue, 3 Apr 2018 15:59:35 +0300 Subject: [core] re-bind uniform locations after re-linking program - On some gl implementations the uniform locations are shifted after linking the program a second time, resulting in errors when using any uniform. On some implementations they are actually doubled. Re-binding the uniforms after selectively binding the vertex attributes prevents this. --- src/mbgl/gl/program.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index 3b54ec194a..af02ad3d54 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -36,8 +36,13 @@ public: context.createShader(ShaderType::Fragment, fragmentSource))), uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), attributeLocations(Attributes::bindLocations(program)) { + // Re-link program after manually binding only active attributes in Attributes::bindLocations context.linkProgram(program); + + // We have to re-initialize the uniforms state from the bindings as the uniform locations + // get shifted on some implementations + uniformsState = Uniforms::bindLocations(program); } template -- cgit v1.2.1 From 4f809d6626cf661ecc3993f7ffa49b338d8c63ad Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Thu, 5 Apr 2018 16:14:22 +0300 Subject: [android] release local refs early --- platform/android/src/image.cpp | 7 ++++++- platform/android/src/text/local_glyph_rasterizer.cpp | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/android/src/image.cpp b/platform/android/src/image.cpp index 2a33944b18..0456381578 100644 --- a/platform/android/src/image.cpp +++ b/platform/android/src/image.cpp @@ -16,7 +16,12 @@ PremultipliedImage decodeImage(const std::string& string) { reinterpret_cast(string.data())); auto bitmap = android::BitmapFactory::DecodeByteArray(*env, array, 0, string.size()); - return android::Bitmap::GetImage(*env, bitmap); + jni::DeleteLocalRef(*env, array); + + auto image = android::Bitmap::GetImage(*env, bitmap); + jni::DeleteLocalRef(*env, bitmap); + + return image; } } // namespace mbgl diff --git a/platform/android/src/text/local_glyph_rasterizer.cpp b/platform/android/src/text/local_glyph_rasterizer.cpp index ce1b0fc8fd..d232058d15 100644 --- a/platform/android/src/text/local_glyph_rasterizer.cpp +++ b/platform/android/src/text/local_glyph_rasterizer.cpp @@ -46,6 +46,7 @@ PremultipliedImage LocalGlyphRasterizer::drawGlyphBitmap(const std::string& font jniFontFamily, static_cast(bold), static_cast(glyphID)); + jni::DeleteLocalRef(*env, jniFontFamily); PremultipliedImage result = Bitmap::GetImage(*env, javaBitmap); jni::DeleteLocalRef(*env, javaBitmap); -- cgit v1.2.1 From 451e6bc8a0ca3e2d2f1629d5351cc3724648b97e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 5 Apr 2018 22:37:45 +0200 Subject: [android] - add delete local refs calls for make jni strings --- platform/android/src/offline/offline_manager.cpp | 8 ++++++-- platform/android/src/offline/offline_region.cpp | 12 +++++++++--- platform/android/src/snapshotter/map_snapshotter.cpp | 4 +++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/platform/android/src/offline/offline_manager.cpp b/platform/android/src/offline/offline_manager.cpp index 02871e7fdf..4960ae2845 100644 --- a/platform/android/src/offline/offline_manager.cpp +++ b/platform/android/src/offline/offline_manager.cpp @@ -102,7 +102,9 @@ void OfflineManager::ListOfflineRegionsCallback::onError(jni::JNIEnv& env, std::exception_ptr error) { static auto method = javaClass.GetMethod(env, "onError"); std::string message = mbgl::util::toString(error); - callback.Call(env, method, jni::Make(env, message)); + auto jmessage = jni::Make(env, message); + callback.Call(env, method, jmessage); + jni::DeleteLocalRef(env, jmessage); } void OfflineManager::ListOfflineRegionsCallback::onList(jni::JNIEnv& env, @@ -138,7 +140,9 @@ void OfflineManager::CreateOfflineRegionCallback::onError(jni::JNIEnv& env, std::exception_ptr error) { static auto method = javaClass.GetMethod(env, "onError"); std::string message = mbgl::util::toString(error); - callback.Call(env, method, jni::Make(env, message)); + auto jmessage = jni::Make(env, message); + callback.Call(env, method, jmessage); + jni::DeleteLocalRef(env, jmessage); } void OfflineManager::CreateOfflineRegionCallback::onCreate(jni::JNIEnv& env, diff --git a/platform/android/src/offline/offline_region.cpp b/platform/android/src/offline/offline_region.cpp index 856434d266..27de76fb00 100644 --- a/platform/android/src/offline/offline_region.cpp +++ b/platform/android/src/offline/offline_region.cpp @@ -239,7 +239,9 @@ void OfflineRegion::OfflineRegionStatusCallback::onError(jni::JNIEnv& env, std::exception_ptr error) { static auto method = javaClass.GetMethod(env, "onError"); std::string message = mbgl::util::toString(error); - callback.Call(env, method, jni::Make(env, message)); + auto jmessage = jni::Make(env, message); + callback.Call(env, method, jmessage); + jni::DeleteLocalRef(env, jmessage); } void OfflineRegion::OfflineRegionStatusCallback::onStatus(jni::JNIEnv& env, @@ -267,7 +269,9 @@ void OfflineRegion::OfflineRegionDeleteCallback::onError(jni::JNIEnv& env, std::exception_ptr error) { static auto method = javaClass.GetMethod(env, "onError"); std::string message = mbgl::util::toString(error); - callback.Call(env, method, jni::Make(env, message)); + auto jmessage = jni::Make(env, message); + callback.Call(env, method, jmessage); + jni::DeleteLocalRef(env, jmessage); } void OfflineRegion::OfflineRegionDeleteCallback::onDelete(jni::JNIEnv& env, jni::Object callback) { @@ -289,7 +293,9 @@ void OfflineRegion::OfflineRegionUpdateMetadataCallback::onError(jni::JNIEnv& en std::exception_ptr error) { static auto method = javaClass.GetMethod(env, "onError"); std::string message = mbgl::util::toString(error); - callback.Call(env, method, jni::Make(env, message)); + auto jmessage = jni::Make(env, message); + callback.Call(env, method, jmessage); + jni::DeleteLocalRef(env, jmessage); } void OfflineRegion::OfflineRegionUpdateMetadataCallback::onUpdate(jni::JNIEnv& env, diff --git a/platform/android/src/snapshotter/map_snapshotter.cpp b/platform/android/src/snapshotter/map_snapshotter.cpp index 71f8b4f4c0..a006953d36 100644 --- a/platform/android/src/snapshotter/map_snapshotter.cpp +++ b/platform/android/src/snapshotter/map_snapshotter.cpp @@ -71,7 +71,9 @@ void MapSnapshotter::start(JNIEnv& env) { if (err) { // error handler callback static auto onSnapshotFailed = javaClass.GetMethod(*_env, "onSnapshotFailed"); - javaPeer->Call(*_env, onSnapshotFailed, jni::Make(*_env, util::toString(err))); + auto message = jni::Make(*_env, util::toString(err)); + javaPeer->Call(*_env, onSnapshotFailed, message); + jni::DeleteLocalRef(*_env, message); } else { // Create the wrapper auto mapSnapshot = android::MapSnapshot::New(*_env, std::move(image), pixelRatio, attributions, showLogo, pointForFn); -- cgit v1.2.1 From 2993a0d59bca56ec08a4e51ae0cc13b9edf205ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 4 Apr 2018 11:55:46 +0200 Subject: [android] invalidate camera position before delivering onMapReady (#11585) --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 85ac99a188..a6da4c57fb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -168,6 +168,7 @@ public final class MapboxMap { * Called before the OnMapReadyCallback is invoked. */ void onPreMapReady() { + invalidateCameraPosition(); annotationManager.reloadMarkers(); annotationManager.adjustTopOffsetPixels(this); } @@ -912,7 +913,7 @@ public final class MapboxMap { /** * Invalidates the current camera position by reconstructing it from mbgl */ - void invalidateCameraPosition() { + private void invalidateCameraPosition() { CameraPosition cameraPosition = transform.invalidateCameraPosition(); if (cameraPosition != null) { transform.updateCameraPosition(cameraPosition); -- cgit v1.2.1 From 61bb9be1e3c3e290d88453ae91d7e26c9dab4009 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Tue, 10 Apr 2018 10:58:58 -0400 Subject: [android] Adds gradle-dependency-graph-generator-plugin to Android project (#11603) * adds gradle-dependency-graph-generator-plugin to the project * keep script in sync with https://github.com/mapbox/mapbox-android-demo/pull/671 * adds a makefile rule for dependency generation --- Makefile | 5 ++++ platform/android/MapboxGLAndroidSDK/.gitignore | 1 + platform/android/MapboxGLAndroidSDK/build.gradle | 1 + .../gradle/gradle-dependencies-graph.gradle | 29 ++++++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 platform/android/MapboxGLAndroidSDK/.gitignore create mode 100644 platform/android/gradle/gradle-dependencies-graph.gradle diff --git a/Makefile b/Makefile index 862201d8d1..818a9585e3 100644 --- a/Makefile +++ b/Makefile @@ -683,6 +683,11 @@ endif android-configuration: platform/android/gradle/configuration.gradle cat platform/android/gradle/configuration.gradle +# Creates a dependency graph using Graphviz +.PHONY: android-graph +android-graph: + cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:generateDependencyGraphMapboxLibraries + #### Miscellaneous targets ##################################################### .PHONY: style-code diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore new file mode 100644 index 0000000000..c24bd2563a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/.gitignore @@ -0,0 +1 @@ +dependency-graph-mapbox-libraries.png diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index 9063321648..2bf452e964 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -150,3 +150,4 @@ apply from: "${rootDir}/gradle/gradle-javadoc.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-tests-staticblockremover.gradle" +apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle" diff --git a/platform/android/gradle/gradle-dependencies-graph.gradle b/platform/android/gradle/gradle-dependencies-graph.gradle new file mode 100644 index 0000000000..5cbc7b974f --- /dev/null +++ b/platform/android/gradle/gradle-dependencies-graph.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.3.0" + } +} + +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin +import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator +import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions +import com.vanniktech.dependency.graph.generator.dot.Color +import com.vanniktech.dependency.graph.generator.dot.Shape +import com.vanniktech.dependency.graph.generator.dot.Style + +plugins.apply(DependencyGraphGeneratorPlugin) + +def mapboxGenerator = new Generator( + "mapboxLibraries", // Suffix for our Gradle task. + "", // Root suffix that we don't want in this case. + { dependency -> dependency.getModuleGroup().startsWith("com.mapbox.mapboxsdk") }, // Only want Mapbox libs. + { dependency -> true }, // Include transitive dependencies. +) + +dependencyGraphGenerator { + generators = [mapboxGenerator] +} -- cgit v1.2.1 From 50a8a44bc6442bbb32025bb898796f9ac5df5987 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 10 Apr 2018 16:43:11 +0200 Subject: [android] - update changelog for v5.5.2 --- platform/android/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index dc7ff98828..f62ecca2e4 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,14 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.2 - April 10, 2018 + - Correct animation scale point [#11643](https://github.com/mapbox/mapbox-gl-native/pull/11643) + - Re-bind uniform locations after re-linking program [#11583](https://github.com/mapbox/mapbox-gl-native/pull/11583) + - Invalidate camera position before delivering onMapReady [#11585](https://github.com/mapbox/mapbox-gl-native/pull/11585) + - Null java peer callback [#11358](https://github.com/mapbox/mapbox-gl-native/pull/11358) + - Add missing delete local reference [#11608](https://github.com/mapbox/mapbox-gl-native/pull/11608) + - Release local refs [#11599](https://github.com/mapbox/mapbox-gl-native/pull/11599) + ## 5.5.1 - March 26, 2018 - Verify optional access of FileSource deactivation [#11480](https://github.com/mapbox/mapbox-gl-native/pull/11480) - Prevent default style loading when style json was set [#11519](https://github.com/mapbox/mapbox-gl-native/pull/11519) -- cgit v1.2.1 From 8e200ae38736e402929c90a105db9ff6f7677c2a Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 10 Apr 2018 16:55:16 +0200 Subject: [android] - bump snapshot version to v5.6.0 --- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index de93849169..5b8260a53c 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=5.5.2-SNAPSHOT +VERSION_NAME=5.6.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 21af83789d90e0acaa413145b370e31f22012bbe Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 10 Apr 2018 16:43:11 +0200 Subject: [android] - update changelog for v5.5.2 --- platform/android/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 534f628c37..db9f91d034 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,14 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.2 - April 10, 2018 + - Correct animation scale point [#11643](https://github.com/mapbox/mapbox-gl-native/pull/11643) + - Re-bind uniform locations after re-linking program [#11583](https://github.com/mapbox/mapbox-gl-native/pull/11583) + - Invalidate camera position before delivering onMapReady [#11585](https://github.com/mapbox/mapbox-gl-native/pull/11585) + - Null java peer callback [#11358](https://github.com/mapbox/mapbox-gl-native/pull/11358) + - Add missing delete local reference [#11608](https://github.com/mapbox/mapbox-gl-native/pull/11608) + - Release local refs [#11599](https://github.com/mapbox/mapbox-gl-native/pull/11599) + ## 6.0.0-beta.6 - April 4, 2018 - Fix race condition crash for heavily modified annotations [#11551](https://github.com/mapbox/mapbox-gl-native/pull/11551) - Throw exception when converting PropertyValue with an expression [#11572](https://github.com/mapbox/mapbox-gl-native/pull/11572) -- cgit v1.2.1 From 8b10bb38c0a7d6e0a1225e185acde5ec8a604bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Mon, 16 Apr 2018 15:41:28 +0300 Subject: [core] update CA bundle --- misc/ca-bundle.crt | 133 +---------------------------------------------------- 1 file changed, 2 insertions(+), 131 deletions(-) diff --git a/misc/ca-bundle.crt b/misc/ca-bundle.crt index 29691b9733..2ec7884afd 100644 --- a/misc/ca-bundle.crt +++ b/misc/ca-bundle.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Wed Jan 17 00:48:54 2018 GMT +## Certificate data from Mozilla as of: Mon Apr 16 12:40:48 2018 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.27. -## SHA256: a3ac15b98179dd2f3c5de076d10b1d53048754372f7207c2f327510cdd78fbd8 +## SHA256: 704f02707ec6b4c4a7597a8c6039b020def11e64f3ef0605a9c3543d48038a57 ## @@ -446,60 +446,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH llpwrN9M -----END CERTIFICATE----- -Camerfirma Chambers of Commerce Root -==================================== ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx -NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp -cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn -MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC -AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU -xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH -NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW -DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV -d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud -EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v -cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P -AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh -bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD -VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi -fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD -L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN -UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n -ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 -erfutGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- - -Camerfirma Global Chambersign Root -================================== ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx -NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt -YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg -MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw -ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J -1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O -by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl -6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c -8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ -BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j -aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B -Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj -aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y -ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA -PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y -gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ -PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 -IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes -t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -710,30 +656,6 @@ RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ -----END CERTIFICATE----- -DST ACES CA X6 -============== ------BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT -MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha -MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE -CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI -DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa -pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow -GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy -MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu -Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy -dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU -CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 -5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t -Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs -vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 -oKfN5XozNmr6mis= ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -976,27 +898,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- -Security Communication EV RootCA1 -================================= ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE -BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl -Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO -/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX -WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z -ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 -bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK -9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG -SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm -iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG -Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW -mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW -T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------END CERTIFICATE----- - OISTE WISeKey Global Root GA CA =============================== -----BEGIN CERTIFICATE----- @@ -2027,36 +1928,6 @@ NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv w9y4AyHqnxbxLFS1 -----END CERTIFICATE----- -CA Disig Root R1 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy -3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 -u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 -m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk -CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa -YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 -vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL -LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX -ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is -XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ -04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B -LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM -CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb -VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 -YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS -ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix -lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N -UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ -a7+h89n07eLw4+1knj0vllJPgFOL ------END CERTIFICATE----- - CA Disig Root R2 ================ -----BEGIN CERTIFICATE----- -- cgit v1.2.1 From 7c1d79fb95882003a7424a181bcdfb95bbfe23a9 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 17 Apr 2018 09:35:37 +0200 Subject: [android] - changelog entry for v6.0.0 --- platform/android/CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index db9f91d034..dfec87ea1f 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,25 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.0 - April 17, 2018 + - Bump telemetry version to 3.0.1 [#11700](https://github.com/mapbox/mapbox-gl-native/pull/11700) + - Update layer when changing its min/max zoom [#11687](https://github.com/mapbox/mapbox-gl-native/pull/11687) + +## 6.0.0-beta.7 - April 12, 2018 + - Add abs, round, floor, ceil expression operators [#11653](https://github.com/mapbox/mapbox-gl-native/pull/11653) + - LatLngBounds correct center calculation [#11650](https://github.com/mapbox/mapbox-gl-native/pull/11650) + - Bump telemetry to 3.0.0 final [#11658](https://github.com/mapbox/mapbox-gl-native/pull/11658) + - Correctly calculate LatLngBounds [#11647](https://github.com/mapbox/mapbox-gl-native/pull/11647) + - Add convenience step expression [#11641](https://github.com/mapbox/mapbox-gl-native/pull/11641) + - Add javadoc examples for Android [#11540](https://github.com/mapbox/mapbox-gl-native/pull/11540) + - Add paused state to map renderer, don't render snapshots after onPause [#11358](https://github.com/mapbox/mapbox-gl-native/pull/11358) + - Rework internal expression conversion [#11490](https://github.com/mapbox/mapbox-gl-native/pull/11490) + - Fixed gesture event listeners javadoc [#11630](https://github.com/mapbox/mapbox-gl-native/pull/11630) + - Add delete local reference on jni strings [#11608](https://github.com/mapbox/mapbox-gl-native/pull/11608) + - Release local references early [#11599](https://github.com/mapbox/mapbox-gl-native/pull/11599) + - Re-bind uniform locations after re-linking program [#11618](https://github.com/mapbox/mapbox-gl-native/pull/11618) + - Bump mapbox-sdk-services to 3.0.1 [#11593](https://github.com/mapbox/mapbox-gl-native/pull/11593) + ## 5.5.2 - April 10, 2018 - Correct animation scale point [#11643](https://github.com/mapbox/mapbox-gl-native/pull/11643) - Re-bind uniform locations after re-linking program [#11583](https://github.com/mapbox/mapbox-gl-native/pull/11583) -- cgit v1.2.1 From 2bb785dad2489d04db179fa9cf65514640db0a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Wed, 18 Apr 2018 10:29:08 +0200 Subject: [android] - updated changelog for 6.0.1 (#11715) (cherry picked from commit 0256f15) --- platform/android/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index dfec87ea1f..246b5828c4 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.1 - April 17, 2018 + - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) + ## 6.0.0 - April 17, 2018 - Bump telemetry version to 3.0.1 [#11700](https://github.com/mapbox/mapbox-gl-native/pull/11700) - Update layer when changing its min/max zoom [#11687](https://github.com/mapbox/mapbox-gl-native/pull/11687) -- cgit v1.2.1 From c5625f988a37b6876c4ef5fc8d9de295ee28ed44 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Date: Mon, 23 Apr 2018 17:59:17 -0400 Subject: [ios, android] Resolve merge conflicts. --- circle.yml | 2 +- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- platform/ios/CHANGELOG.md | 14 +------------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/circle.yml b/circle.yml index 5f06791cd6..1aacf1a2a6 100644 --- a/circle.yml +++ b/circle.yml @@ -422,7 +422,7 @@ jobs: - deploy: name: Publish to Maven command: | - if [ "${CIRCLE_BRANCH}" == "release-boba" ]; then make run-android-upload-archives ; fi + if [ "${CIRCLE_BRANCH}" == "master" ]; then make run-android-upload-archives ; fi # ------------------------------------------------------------------------------ diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index bdfc444454..84aeabef1a 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=6.1.0-SNAPSHOT +VERSION_NAME=7.0.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 9d8390803b..1775d628f6 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,7 +2,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 4.0.1 +## master ### Style layers @@ -88,18 +88,6 @@ The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iO * 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.6 - March 12, 2018 - -* Fixed an issue where full-resolution tiles could fail to replace lower-resolution placeholders. ([#11227](https://github.com/mapbox/mapbox-gl-native/pull/11227)) -* Fixed an issue where tilesets with bounds that cover the entire world would fail to render. ([#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425)) -* Fixed a memory leak in `MGLMapSnapshotter`. ([#11193](https://github.com/mapbox/mapbox-gl-native/pull/11193)) -* Fixed an issue where the pinch gesture could drift beyond bounds imposed by `-[MGLMapViewDelegate mapView:shouldChangeFromCamera:toCamera:]`. ([#11423](https://github.com/mapbox/mapbox-gl-native/pull/11423)) -* Improved the visibility of the heading indicator arrow. ([#11337](https://github.com/mapbox/mapbox-gl-native/pull/11337)) - -## 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)) - ## 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)) -- cgit v1.2.1 From eb39c80604935deb666907f90ddc31f50865f828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 24 Apr 2018 11:31:29 +0200 Subject: [android] - unwrap LatLngBounds for the shortest path when passing to core (#11759) --- .../mapbox/mapboxsdk/geometry/LatLngBounds.java | 45 ++++++++++++++++++---- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 13 +++++-- .../mapboxsdk/geometry/LatLngBoundsTest.java | 22 ++++++++++- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 05187cf333..eec0d566bb 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -28,7 +28,7 @@ public class LatLngBounds implements Parcelable { /** * Construct a new LatLngBounds based on its corners, given in NESW * order. - * + *

* If eastern longitude is smaller than the western one, bounds will include antimeridian. * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees * and cross the antimeridian. @@ -46,6 +46,11 @@ public class LatLngBounds implements Parcelable { this.longitudeWest = westLongitude; } + LatLngBounds(LatLngBounds latLngBounds) { + this(latLngBounds.latitudeNorth, latLngBounds.longitudeEast, + latLngBounds.latitudeSouth, latLngBounds.longitudeWest); + } + /** * Returns the world bounds. * @@ -75,7 +80,6 @@ public class LatLngBounds implements Parcelable { if (longCenter >= GeometryConstants.MAX_LONGITUDE) { longCenter = this.longitudeEast - halfSpan; } - return new LatLng(latCenter, longCenter); } return new LatLng(latCenter, longCenter); @@ -188,7 +192,6 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } - static double getLongitudeSpan(final double longEast, final double longWest) { double longSpan = Math.abs(longEast - longWest); if (longEast >= longWest) { @@ -199,6 +202,25 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } + /** + * If bounds cross the antimeridian, unwrap west longitude for the shortest path. + * + * @return unwrapped bounds + */ + public LatLngBounds unwrapBounds() { + double unwrapedLonWest = longitudeWest; + if (longitudeEast < longitudeWest) { + if (longitudeWest > 0 && longitudeEast < 0) { + unwrapedLonWest -= GeometryConstants.LONGITUDE_SPAN; + } else if (longitudeWest < 0 && longitudeEast > 0) { + unwrapedLonWest += GeometryConstants.LONGITUDE_SPAN; + } + return unwrapped(latitudeNorth, longitudeEast, latitudeSouth, unwrapedLonWest); + } else { + return new LatLngBounds(this); + } + } + /** * Validate if LatLngBounds is empty, determined if absolute distance is * @@ -279,12 +301,11 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from doubles representing a LatLng pair. - * + *

* This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. - * *

* This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -318,12 +339,20 @@ public class LatLngBounds implements Parcelable { throw new IllegalArgumentException("LatSouth cannot be less than latNorth"); } + return wrapped(latNorth, lonEast, latSouth, lonWest); + } + + static LatLngBounds wrapped(double latNorth, double lonEast, double latSouth, double lonWest) { lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); } + static LatLngBounds unwrapped(double latNorth, double lonEast, double latSouth, double lonWest) { + return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); + } + private static double lat_(int z, int y) { double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z); return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); @@ -335,14 +364,14 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from a Tile identifier. - * + *

* Returned bounds will have latitude in the range of Mercator projection. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE * * @param z Tile zoom level. * @param x Tile X coordinate. * @param y Tile Y coordinate. + * @see GeometryConstants#MIN_MERCATOR_LATITUDE + * @see GeometryConstants#MAX_MERCATOR_LATITUDE */ public static LatLngBounds from(int z, int x, int y) { return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 5e36dd0f78..745485e2d2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1540,7 +1540,12 @@ public final class MapboxMap { * @param latLngBounds the bounds to constrain the map with */ public void setLatLngBoundsForCameraTarget(@Nullable LatLngBounds latLngBounds) { - nativeMapView.setLatLngBounds(latLngBounds); + if (latLngBounds == null) { + nativeMapView.setLatLngBounds(latLngBounds); + } else { + //unwrapping the bounds to generate the right convex hull in core + nativeMapView.setLatLngBounds(latLngBounds.unwrapBounds()); + } } /** @@ -1550,9 +1555,9 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { - // get padded camera position from LatLngBounds - return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); + public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { + // get padded camera position from LatLngBounds, unwrapping the bounds to generate the right convex hull in core + return nativeMapView.getCameraForLatLngBounds(latLngBounds.unwrapBounds(), padding); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e072f07fb9..c1e497af32 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -524,7 +524,6 @@ public class LatLngBoundsTest { LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); } - @Test public void testConstructorChecksSouthLatitudeNaN() { exception.expect(IllegalArgumentException.class); @@ -543,7 +542,7 @@ public class LatLngBoundsTest { public void testConstructorChecksSouthLatitudeGreaterThan90() { exception.expect(IllegalArgumentException.class); exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20,95, 0); + LatLngBounds.from(20, 20, 95, 0); } @Test @@ -566,4 +565,23 @@ public class LatLngBoundsTest { exception.expectMessage("LatSouth cannot be less than latNorth"); LatLngBounds.from(0, 20, 20, 0); } + + @Test + public void testCopyConstructor() { + LatLngBounds bounds = LatLngBounds.from(50, 10, -20, -30); + LatLngBounds copyBounds = new LatLngBounds(bounds); + assertEquals(bounds, copyBounds); + } + + @Test + public void testUnwrapBounds() { + LatLngBounds bounds = LatLngBounds.from(16.5, -172.8, -35.127709, 172.6); + LatLngBounds unwrappedBounds = bounds.unwrapBounds(); + assertEquals(bounds.getCenter().wrap(), unwrappedBounds.getCenter().wrap()); + assertEquals(bounds.getSpan(), unwrappedBounds.getSpan()); + assertTrue(unwrappedBounds.getLonEast() < 0 && unwrappedBounds.getLonWest() < 0); + + LatLngBounds bounds2 = LatLngBounds.from(16.5, -162.8, -35.127709, -177.4); + assertEquals(bounds2, bounds2.unwrapBounds()); + } } -- cgit v1.2.1 From a62745edf9ee2da1f6ebda07acfd8260f3696e50 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Wed, 18 Apr 2018 16:33:37 -0700 Subject: Port global symbol query from GL JS: - Symbol querying is now global instead of per-tile - Symbols that bleed over tile boundaries no longer missed in queries - Symbol results now sorted based on rendering order (ie overlapping symbols change their sort order when a bearing change causes their render order to change) - Placement::retainedQueryData now responsible for maintaining symbol querying data for buckets that may no longer be in the TilePyramid. --- platform/node/test/ignores.json | 2 - src/mbgl/annotation/render_annotation_source.cpp | 5 +- src/mbgl/annotation/render_annotation_source.hpp | 3 +- src/mbgl/geometry/feature_index.cpp | 58 ++++++++++++++------ src/mbgl/geometry/feature_index.hpp | 32 ++++++------ src/mbgl/layout/symbol_layout.cpp | 12 ++--- src/mbgl/layout/symbol_layout.hpp | 7 +-- src/mbgl/renderer/buckets/symbol_bucket.cpp | 4 ++ src/mbgl/renderer/buckets/symbol_bucket.hpp | 2 + src/mbgl/renderer/render_source.hpp | 3 +- src/mbgl/renderer/renderer_impl.cpp | 50 +++++++++++++----- src/mbgl/renderer/renderer_impl.hpp | 8 ++- .../sources/render_custom_geometry_source.cpp | 5 +- .../sources/render_custom_geometry_source.hpp | 3 +- .../renderer/sources/render_geojson_source.cpp | 5 +- .../renderer/sources/render_geojson_source.hpp | 3 +- src/mbgl/renderer/sources/render_image_source.cpp | 3 +- src/mbgl/renderer/sources/render_image_source.hpp | 3 +- .../renderer/sources/render_raster_dem_source.cpp | 3 +- .../renderer/sources/render_raster_dem_source.hpp | 3 +- src/mbgl/renderer/sources/render_raster_source.cpp | 3 +- src/mbgl/renderer/sources/render_raster_source.hpp | 3 +- src/mbgl/renderer/sources/render_vector_source.cpp | 5 +- src/mbgl/renderer/sources/render_vector_source.hpp | 3 +- src/mbgl/renderer/tile_pyramid.cpp | 6 +-- src/mbgl/renderer/tile_pyramid.hpp | 3 +- src/mbgl/text/collision_index.cpp | 61 +++++++--------------- src/mbgl/text/collision_index.hpp | 5 +- src/mbgl/text/placement.cpp | 36 ++++++++++--- src/mbgl/text/placement.hpp | 20 ++++++- src/mbgl/tile/geometry_tile.cpp | 34 ++++-------- src/mbgl/tile/geometry_tile.hpp | 10 ++-- src/mbgl/tile/geometry_tile_worker.cpp | 3 +- src/mbgl/tile/tile.cpp | 3 +- src/mbgl/tile/tile.hpp | 8 +-- 35 files changed, 219 insertions(+), 198 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index e0c2475b75..b7ca72cee0 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -6,8 +6,6 @@ "query-tests/geometry/multilinestring": "needs investigation", "query-tests/geometry/multipolygon": "needs investigation", "query-tests/geometry/polygon": "needs investigation", - "query-tests/symbol/panned-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408", - "query-tests/symbol/rotated-after-insert": "https://github.com/mapbox/mapbox-gl-native/issues/10408", "query-tests/world-wrapping/box": "skip - needs issue", "query-tests/world-wrapping/point": "skip - needs issue", "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619", diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index a237100d13..19d19d2901 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -64,9 +64,8 @@ std::unordered_map> RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index e812ec2883..da5376ab2d 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -27,8 +27,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index c67786274a..8ea6259129 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -33,14 +33,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries, } } -static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex > b.sortIndex; -} - -static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) { - return a.sortIndex < b.sortIndex; -} - void FeatureIndex::query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, @@ -49,9 +41,7 @@ void FeatureIndex::query( const double scale, const RenderedQueryOptions& queryOptions, const UnwrappedTileID& tileID, - const std::string& sourceID, const std::vector& layers, - const CollisionIndex& collisionIndex, const float additionalQueryRadius) const { if (!tileData) { @@ -68,7 +58,9 @@ void FeatureIndex::query( convertPoint(box.max + additionalRadius) }); - std::sort(features.begin(), features.end(), topDown); + std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) { + return a.sortIndex > b.sortIndex; + }); size_t previousSortIndex = std::numeric_limits::max(); for (const auto& indexedFeature : features) { @@ -76,23 +68,55 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, bearing, pixelsToTileUnits); } +} + +std::unordered_map> FeatureIndex::lookupSymbolFeatures(const std::vector& symbolFeatures, + const RenderedQueryOptions& queryOptions, + const std::vector& layers, + const OverscaledTileID& tileID, + const std::shared_ptr>& featureSortOrder) const { + std::unordered_map> result; + if (!tileData) { + return result; + } + std::vector sortedFeatures(symbolFeatures.begin(), symbolFeatures.end()); + + std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) { + // Same idea as the non-symbol sort order, but symbol features may have changed their sort order + // since their corresponding IndexedSubfeature was added to the CollisionIndex + // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort + if (featureSortOrder) { + // queryRenderedSymbols documentation says we'll return features in + // "top-to-bottom" rendering order (aka last-to-first). + // Actually there can be multiple symbol instances per feature, so + // we sort each feature based on the first matching symbol instance. + auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index); + auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index); + assert(sortedA != featureSortOrder->end()); + assert(sortedB != featureSortOrder->end()); + return sortedA > sortedB; + } else { + // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data" + // logic as non-symboles + return a.sortIndex > b.sortIndex; + } + }); - std::vector symbolFeatures = collisionIndex.queryRenderedSymbols(queryGeometry, tileID, sourceID); - std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); - for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, tileID.canonical, layers, bearing, pixelsToTileUnits); + for (const auto& symbolFeature : sortedFeatures) { + addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), 0, 0); } + return result; } void FeatureIndex::addFeature( std::unordered_map>& result, const IndexedSubfeature& indexedFeature, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, const CanonicalTileID& tileID, const std::vector& layers, + const GeometryCoordinates& queryGeometry, const float bearing, const float pixelsToTileUnits) const { diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 9e0c172342..5a15a379c4 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -25,27 +25,24 @@ public: , sourceLayerName(std::move(sourceLayerName_)) , bucketName(std::move(bucketName_)) , sortIndex(sortIndex_) - , tileID(0, 0, 0) + , bucketInstanceId(0) {} - IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_, - std::string sourceID_, CanonicalTileID tileID_) - : index(index_) - , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) - , sortIndex(std::move(sortIndex_)) - , sourceID(std::move(sourceID_)) - , tileID(std::move(tileID_)) - {} + IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_) + : index(other.index) + , sourceLayerName(other.sourceLayerName) + , bucketName(other.bucketName) + , sortIndex(other.sortIndex) + , bucketInstanceId(bucketInstanceId_) + {} size_t index; std::string sourceLayerName; std::string bucketName; size_t sortIndex; // Only used for symbol features - std::string sourceID; - CanonicalTileID tileID; + uint32_t bucketInstanceId; }; class FeatureIndex { @@ -64,9 +61,7 @@ public: const double scale, const RenderedQueryOptions& options, const UnwrappedTileID&, - const std::string&, const std::vector&, - const CollisionIndex&, const float additionalQueryRadius) const; static optional translateQueryGeometry( @@ -77,15 +72,22 @@ public: const float pixelsToTileUnits); void setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs); + + std::unordered_map> lookupSymbolFeatures( + const std::vector& symbolFeatures, + const RenderedQueryOptions& options, + const std::vector& layers, + const OverscaledTileID& tileID, + const std::shared_ptr>& featureSortOrder) const; private: void addFeature( std::unordered_map>& result, const IndexedSubfeature&, - const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, const CanonicalTileID&, const std::vector&, + const GeometryCoordinates& queryGeometry, const float bearing, const float pixelsToTileUnits) const; diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 82a9255824..d3126bba45 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -181,8 +181,7 @@ bool SymbolLayout::hasSymbolInstances() const { } void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyphPositions, - const ImageMap& imageMap, const ImagePositions& imagePositions, - const OverscaledTileID& tileID, const std::string& sourceID) { + const ImageMap& imageMap, const ImagePositions& imagePositions) { const bool textAlongLine = layout.get() == AlignmentType::Map && layout.get() == SymbolPlacementType::Line; @@ -253,7 +252,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap, tileID, sourceID); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, glyphPositionMap); } feature.geometry.clear(); @@ -266,9 +265,7 @@ void SymbolLayout::addFeature(const std::size_t index, const SymbolFeature& feature, const std::pair& shapedTextOrientations, optional shapedIcon, - const GlyphPositionMap& glyphPositionMap, - const OverscaledTileID& tileID, - const std::string& sourceID) { + const GlyphPositionMap& glyphPositionMap) { const float minScale = 0.5f; const float glyphSize = 24.0f; @@ -297,8 +294,7 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size(), - sourceID, tileID.canonical); + IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index 6951c29ada..c93d8f4106 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -34,8 +34,7 @@ public: GlyphDependencies&); void prepare(const GlyphMap&, const GlyphPositions&, - const ImageMap&, const ImagePositions&, - const OverscaledTileID&, const std::string&); + const ImageMap&, const ImagePositions&); std::unique_ptr place(const bool showCollisionBoxes); @@ -52,9 +51,7 @@ private: const SymbolFeature&, const std::pair& shapedTextOrientations, optional shapedIcon, - const GlyphPositionMap&, - const OverscaledTileID&, - const std::string&); + const GlyphPositionMap&); bool anchorIsTooClose(const std::u16string& text, const float repeatDistance, const Anchor&); std::map> compareText; diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 60e8a0b504..3d40a1714d 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -200,8 +200,12 @@ void SymbolBucket::sortFeatures(const float angle) { text.triangles.clear(); icon.triangles.clear(); + featureSortOrder = std::make_unique>(); + featureSortOrder->reserve(symbolInstanceIndexes.size()); + for (auto i : symbolInstanceIndexes) { const SymbolInstance& symbolInstance = symbolInstances[i]; + featureSortOrder->push_back(symbolInstance.featureIndex); if (symbolInstance.placedTextIndex) { addPlacedSymbol(text.triangles, text.placedSymbols[*symbolInstance.placedTextIndex]); diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index ed8afb052c..e52d18372d 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -131,6 +131,8 @@ public: uint32_t bucketInstanceId = 0; bool justReloaded = false; + + std::shared_ptr> featureSortOrder; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 53519c763e..54a0b5db43 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -64,8 +64,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const = 0; + const RenderedQueryOptions& options) const = 0; virtual std::vector querySourceFeatures(const SourceQueryOptions&) const = 0; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 962a6571db..5b739678fb 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -396,10 +396,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { } placementChanged = newPlacement->commit(*placement, parameters.timePoint); - // commitFeatureIndexes depends on the assumption that no new FeatureIndex has been loaded since placement - // started. If we violate this assumption, then we need to either make CollisionIndex completely independendent of - // FeatureIndex, or find a way for its entries to point to multiple FeatureIndexes. - commitFeatureIndexes(); crossTileSymbolIndex.pruneUnusedLayers(usedSymbolLayers); if (placementChanged || symbolBucketsChanged) { placement = std::move(newPlacement); @@ -668,6 +664,39 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin return queryRenderedFeatures(geometry, options, layers); } + +void Renderer::Impl::queryRenderedSymbols(std::unordered_map>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector& layers, + const RenderedQueryOptions& options) const { + + auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry); + std::vector> bucketQueryData; + for (auto entry : renderedSymbols) { + bucketQueryData.push_back(placement->getQueryData(entry.first)); + } + // Although symbol query is global, symbol results are only sortable within a bucket + // For a predictable global sort order, we sort the buckets based on their corresponding tile position + std::sort(bucketQueryData.begin(), bucketQueryData.end(), [](const RetainedQueryData& a, const RetainedQueryData& b) { + return + std::tie(a.tileID.canonical.z, a.tileID.canonical.y, a.tileID.wrap, a.tileID.canonical.x) < + std::tie(b.tileID.canonical.z, b.tileID.canonical.y, b.tileID.wrap, b.tileID.canonical.x); + }); + + for (auto wrappedQueryData : bucketQueryData) { + auto& queryData = wrappedQueryData.get(); + auto bucketSymbols = queryData.featureIndex->lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId], + options, + layers, + queryData.tileID, + queryData.featureSortOrder); + + for (auto layer : bucketSymbols) { + auto& resultFeatures = resultsByLayer[layer.first]; + std::move(layer.second.begin(), layer.second.end(), std::inserter(resultFeatures, resultFeatures.end())); + } + } +} std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector& layers) const { std::unordered_set sourceIDs; @@ -678,10 +707,12 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin std::unordered_map> resultsByLayer; for (const auto& sourceID : sourceIDs) { if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, placement->getCollisionIndex()); + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } + + queryRenderedSymbols(resultsByLayer, geometry, layers, options); std::vector result; @@ -780,15 +811,6 @@ bool Renderer::Impl::hasTransitions(TimePoint timePoint) const { return false; } -void Renderer::Impl::commitFeatureIndexes() { - for (auto& source : renderSources) { - for (auto& renderTile : source.second->getRenderTiles()) { - Tile& tile = renderTile.get().tile; - tile.commitFeatureIndex(); - } - } -} - void Renderer::Impl::updateFadingTiles() { fadingTiles = false; for (auto& source : renderSources) { diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 4675ac79ea..4124f6f416 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -64,7 +64,12 @@ private: RenderLayer* getRenderLayer(const std::string& id); const RenderLayer* getRenderLayer(const std::string& id) const; - + + void queryRenderedSymbols(std::unordered_map>& resultsByLayer, + const ScreenLineString& geometry, + const std::vector& layers, + const RenderedQueryOptions& options) const; + std::vector queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector&) const; // GlyphManagerObserver implementation. @@ -74,7 +79,6 @@ private: void onTileChanged(RenderSource&, const OverscaledTileID&) override; void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override; - void commitFeatureIndexes(); void updateFadingTiles(); friend class Renderer; diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 057ad5a4a7..88792db00b 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -67,9 +67,8 @@ std::unordered_map> RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 033d731029..9e47b9e392 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -27,8 +27,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index cbf4db70b5..7492a4cf16 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -85,9 +85,8 @@ std::unordered_map> RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index 72fccbd043..bcdc109953 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -31,8 +31,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex&) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 31a5916a34..b35e151f82 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -84,8 +84,7 @@ std::unordered_map> RenderImageSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&, - const CollisionIndex&) const { + const RenderedQueryOptions&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 85ee0ace11..7dc89d3591 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -32,8 +32,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index b3153622c3..58bdba1840 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -146,8 +146,7 @@ std::unordered_map> RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&, - const CollisionIndex& ) const { + const RenderedQueryOptions&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 741214a14d..512fe6367c 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -27,8 +27,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 60b3fa9a3b..3162acc7b4 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -76,8 +76,7 @@ std::unordered_map> RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&, - const CollisionIndex& ) const { + const RenderedQueryOptions&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index 78eda199ac..c60a51c63b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -27,8 +27,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index e87bea5dcd..c9c47359f4 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -79,9 +79,8 @@ std::unordered_map> RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, collisionIndex); + const RenderedQueryOptions& options) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index 592160dc16..a0351c226c 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -27,8 +27,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const final; + const RenderedQueryOptions& options) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index 8f83a0f982..e2d4d9829f 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -241,8 +241,7 @@ void TilePyramid::update(const std::vector>& layer std::unordered_map> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const { + const RenderedQueryOptions& options) const { std::unordered_map> result; if (renderTiles.empty() || geometry.empty()) { return result; @@ -285,8 +284,7 @@ std::unordered_map> TilePyramid::queryRendered tileSpaceQueryGeometry, transformState, layers, - options, - collisionIndex); + options); } return result; diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index 2638599c38..bf3ba0d7b8 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -53,8 +53,7 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector&, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) const; + const RenderedQueryOptions& options) const; std::vector querySourceFeatures(const SourceQueryOptions&) const; diff --git a/src/mbgl/text/collision_index.cpp b/src/mbgl/text/collision_index.cpp index 833658c33e..091840a371 100644 --- a/src/mbgl/text/collision_index.cpp +++ b/src/mbgl/text/collision_index.cpp @@ -219,7 +219,7 @@ std::pair CollisionIndex::placeLineFeature(CollisionFeature& feature, } -void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement) { +void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId) { if (feature.alongLine) { for (auto& circle : feature.boxes) { if (!circle.used) { @@ -227,18 +227,18 @@ void CollisionIndex::insertFeature(CollisionFeature& feature, bool ignorePlaceme } if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ circle.px, circle.py }, circle.radius}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius}); } } } else { assert(feature.boxes.size() == 1); auto& box = feature.boxes[0]; if (ignorePlacement) { - ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } else { - collisionGrid.insert(IndexedSubfeature(feature.indexedFeature), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); + collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }}); } } } @@ -262,66 +262,41 @@ bool polygonIntersectsBox(const LineString& polygon, const GridIndex CollisionIndex::queryRenderedSymbols(const GeometryCoordinates& queryGeometry, const UnwrappedTileID& tileID, const std::string& sourceID) const { - std::vector result; +std::unordered_map> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const { + std::unordered_map> result; if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) { return result; } - - mat4 posMatrix; - mat4 projMatrix; - transformState.getProjMatrix(projMatrix); - transformState.matrixFor(posMatrix, tileID); - matrix::multiply(posMatrix, projMatrix, posMatrix); - - // queryGeometry is specified in integer tile units, but in projecting we switch to float pixels - LineString projectedQuery; + + LineString gridQuery; for (const auto& point : queryGeometry) { - auto projected = projectPoint(posMatrix, convertPoint(point)); - projectedQuery.push_back(projected); + gridQuery.emplace_back(point.x + viewportPadding, point.y + viewportPadding); } - auto envelope = mapbox::geometry::envelope(projectedQuery); + auto envelope = mapbox::geometry::envelope(gridQuery); using QueryResult = std::pair::BBox>; - std::vector thisTileFeatures; std::vector features = collisionGrid.queryWithBoxes(envelope); - - for (auto& queryResult : features) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - // We only have to filter on the canonical ID because even if the feature is showing multiple times - // we treat it as one feature. - thisTileFeatures.push_back(queryResult); - } - } - std::vector ignoredFeatures = ignoredGrid.queryWithBoxes(envelope); - for (auto& queryResult : ignoredFeatures) { - auto& feature = queryResult.first; - if (feature.sourceID == sourceID && feature.tileID == tileID.canonical) { - thisTileFeatures.push_back(queryResult); - } - } + features.insert(features.end(), ignoredFeatures.begin(), ignoredFeatures.end()); - std::unordered_map>> sourceLayerFeatures; - for (auto& queryResult : thisTileFeatures) { + std::unordered_map> seenBuckets; + for (auto& queryResult : features) { auto& feature = queryResult.first; auto& bbox = queryResult.second; // Skip already seen features. - auto& seenFeatures = sourceLayerFeatures[feature.sourceLayerName][feature.bucketName]; + auto& seenFeatures = seenBuckets[feature.bucketInstanceId]; if (seenFeatures.find(feature.index) != seenFeatures.end()) continue; - - seenFeatures.insert(feature.index); - if (!polygonIntersectsBox(projectedQuery, bbox)) { + if (!polygonIntersectsBox(gridQuery, bbox)) { continue; } - result.push_back(feature); + seenFeatures.insert(feature.index); + result[feature.bucketInstanceId].push_back(feature); } return result; diff --git a/src/mbgl/text/collision_index.hpp b/src/mbgl/text/collision_index.hpp index 8653c1d76c..b2be4c6ade 100644 --- a/src/mbgl/text/collision_index.hpp +++ b/src/mbgl/text/collision_index.hpp @@ -28,11 +28,10 @@ public: const bool pitchWithMap, const bool collisionDebug); - void insertFeature(CollisionFeature& feature, bool ignorePlacement); + void insertFeature(CollisionFeature& feature, bool ignorePlacement, uint32_t bucketInstanceId); - std::vector queryRenderedSymbols(const GeometryCoordinates&, const UnwrappedTileID& tileID, const std::string& sourceID) const; + std::unordered_map> queryRenderedSymbols(const ScreenLineString&) const; - private: bool isOffscreen(const CollisionBox&) const; bool isInsideGrid(const CollisionBox&) const; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 54b2b7539b..daf996356e 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -46,11 +46,13 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri std::unordered_set seenCrossTileIDs; for (RenderTile& renderTile : symbolLayer.renderTiles) { - if (!renderTile.tile.isRenderable()) { + if (!renderTile.tile.isRenderable() || !dynamic_cast(&renderTile.tile)) { continue; } - - auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); + GeometryTile& geometryTile = static_cast(renderTile.tile); + + + auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); @@ -58,8 +60,8 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom()); - const float scale = std::pow(2, state.getZoom() - renderTile.tile.id.overscaledZ); - const float textPixelRatio = (util::tileSize * renderTile.tile.id.overscaleFactor()) / util::EXTENT; + const float scale = std::pow(2, state.getZoom() - geometryTile.id.overscaledZ); + const float textPixelRatio = (util::tileSize * geometryTile.id.overscaleFactor()) / util::EXTENT; mat4 posMatrix; state.matrixFor(posMatrix, renderTile.id); @@ -76,7 +78,14 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri layout.get() == style::AlignmentType::Map, state, pixelsToTileUnits); - + + + // As long as this placement lives, we have to hold onto this bucket's + // matching FeatureIndex/data for querying purposes + retainedQueryData.emplace(std::piecewise_construct, + std::forward_as_tuple(symbolBucket.bucketInstanceId), + std::forward_as_tuple(symbolBucket.bucketInstanceId, geometryTile.getFeatureIndex(), geometryTile.id)); + placeLayerBucket(symbolBucket, posMatrix, textLabelPlaneMatrix, iconLabelPlaneMatrix, scale, textPixelRatio, showCollisionBoxes, seenCrossTileIDs, renderTile.tile.holdForFade()); } } @@ -150,11 +159,11 @@ void Placement::placeLayerBucket( } if (placeText) { - collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get()); + collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get(), bucket.bucketInstanceId); } if (placeIcon) { - collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get()); + collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get(), bucket.bucketInstanceId); } assert(symbolInstance.crossTileID != 0); @@ -305,6 +314,9 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& bucket.updateOpacity(); bucket.sortFeatures(state.getAngle()); + if (retainedQueryData.find(bucket.bucketInstanceId) != retainedQueryData.end()) { + retainedQueryData.find(bucket.bucketInstanceId)->second.featureSortOrder = bucket.featureSortOrder; + } } float Placement::symbolFadeChange(TimePoint now) const { @@ -337,5 +349,13 @@ void Placement::setStale() { const CollisionIndex& Placement::getCollisionIndex() const { return collisionIndex; } + +const RetainedQueryData& Placement::getQueryData(uint32_t bucketInstanceId) const { + auto it = retainedQueryData.find(bucketInstanceId); + if (it == retainedQueryData.end()) { + throw std::runtime_error("Placement::getQueryData with unrecognized bucketInstanceId"); + } + return it->second; +} } // namespace mbgl diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp index 653ae352ed..0e1751b127 100644 --- a/src/mbgl/text/placement.hpp +++ b/src/mbgl/text/placement.hpp @@ -44,7 +44,21 @@ public: // visible right away. const bool skipFade; }; - + +struct RetainedQueryData { + uint32_t bucketInstanceId; + std::shared_ptr featureIndex; + OverscaledTileID tileID; + std::shared_ptr> featureSortOrder; + + RetainedQueryData(uint32_t bucketInstanceId_, + std::shared_ptr featureIndex_, + OverscaledTileID tileID_) + : bucketInstanceId(bucketInstanceId_) + , featureIndex(std::move(featureIndex_)) + , tileID(std::move(tileID_)) {} +}; + class Placement { public: Placement(const TransformState&, MapMode mapMode); @@ -59,6 +73,8 @@ public: bool stillRecent(TimePoint now) const; void setRecent(TimePoint now); void setStale(); + + const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const; private: void placeLayerBucket( @@ -85,6 +101,8 @@ private: TimePoint recentUntil; bool stale = false; + + std::unordered_map retainedQueryData; }; } // namespace mbgl diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a99cb91d26..317c5454f6 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -133,7 +133,7 @@ void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelatio buckets = std::move(result.buckets); - featureIndexPendingCommit = { std::move(result.featureIndex) }; + latestFeatureIndex = std::move(result.featureIndex); if (result.glyphAtlasImage) { glyphAtlasImage = std::move(*result.glyphAtlasImage); @@ -201,22 +201,12 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { return it->second.get(); } -void GeometryTile::commitFeatureIndex() { - // We commit our pending FeatureIndex when a global placement has run, - // synchronizing the global CollisionIndex with the latest buckets/FeatureIndex - if (featureIndexPendingCommit) { - featureIndex = std::move(*featureIndexPendingCommit); - featureIndexPendingCommit = nullopt; - } -} - void GeometryTile::queryRenderedFeatures( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) { + const RenderedQueryOptions& options) { if (!getData()) return; @@ -229,17 +219,15 @@ void GeometryTile::queryRenderedFeatures( } } - featureIndex->query(result, - queryGeometry, - transformState.getAngle(), - util::tileSize * id.overscaleFactor(), - std::pow(2, transformState.getZoom() - id.overscaledZ), - options, - id.toUnwrapped(), - sourceID, - layers, - collisionIndex, - additionalRadius); + latestFeatureIndex->query(result, + queryGeometry, + transformState.getAngle(), + util::tileSize * id.overscaleFactor(), + std::pow(2, transformState.getZoom() - id.overscaledZ), + options, + id.toUnwrapped(), + layers, + additionalRadius); } void GeometryTile::querySourceFeatures( diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 418db4a0b2..a3b2eb6492 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -54,8 +54,7 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector& layers, - const RenderedQueryOptions& options, - const CollisionIndex& collisionIndex) override; + const RenderedQueryOptions& options) override; void querySourceFeatures( std::vector& result, @@ -88,11 +87,11 @@ public: void markRenderedPreviously() override; void performedFadePlacement() override; - void commitFeatureIndex() override; + const std::shared_ptr getFeatureIndex() const { return latestFeatureIndex; } protected: const GeometryTileData* getData() { - return featureIndex ? featureIndex->getData() : nullptr; + return latestFeatureIndex ? latestFeatureIndex->getData() : nullptr; } private: @@ -113,8 +112,7 @@ private: std::unordered_map> buckets; - optional> featureIndexPendingCommit; - std::unique_ptr featureIndex; + std::shared_ptr latestFeatureIndex; optional glyphAtlasImage; optional iconAtlasImage; diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 1378ad5d3a..2e7d588d9b 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -441,8 +441,7 @@ void GeometryTileWorker::performSymbolLayout() { } symbolLayout->prepare(glyphMap, glyphAtlas.positions, - imageMap, imageAtlas.positions, - id, sourceID); + imageMap, imageAtlas.positions); } symbolLayoutsNeedPreparation = false; diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 88db2ba07c..e65bc39540 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -37,8 +37,7 @@ void Tile::queryRenderedFeatures( const GeometryCoordinates&, const TransformState&, const std::vector&, - const RenderedQueryOptions&, - const CollisionIndex&) {} + const RenderedQueryOptions&) {} void Tile::querySourceFeatures( std::vector&, diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 23365c6ae3..a0effa29a1 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -57,8 +57,7 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector&, - const RenderedQueryOptions& options, - const CollisionIndex&); + const RenderedQueryOptions& options); virtual void querySourceFeatures( std::vector& result, @@ -109,11 +108,6 @@ public: // and will have time to finish by the second placement. virtual void performedFadePlacement() {} - // FeatureIndexes are loaded asynchronously, but must be used with a CollisionIndex - // generated from the same data. Calling commitFeatureIndex signals the current - // CollisionIndex is up-to-date and allows us to start using the last loaded FeatureIndex - virtual void commitFeatureIndex() {} - void dumpDebugLogs() const; const OverscaledTileID id; -- cgit v1.2.1 From 60cce56d46cb52c73fcb14d3917c1c47c328b72e Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Thu, 19 Apr 2018 13:06:42 -0700 Subject: Bump GL JS pin to get tests for global symbol querying. - Pulls over an update to line.vertex.glsl (looks like a no-op?) - Add test ignores for collator, is-supported-script, line-gradient - Exclude collator, is-supported-script, line-gradient from code generation. --- mapbox-gl-js | 2 +- .../mapboxsdk/style/layers/PropertyFactory.java | 20 ++++++++++---------- platform/darwin/src/MGLFillStyleLayer.h | 3 ++- platform/darwin/src/MGLLineStyleLayer.h | 5 ++++- platform/node/test/ignores.json | 22 ++++++++++++++++++++++ scripts/generate-shaders.js | 2 ++ scripts/style-spec.js | 1 + src/mbgl/shaders/line.cpp | 3 +++ test/style/expression/expression.test.cpp | 4 ++++ 9 files changed, 49 insertions(+), 13 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 6b96d69ab5..b0ba8bded5 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 6b96d69ab54b149db1f6ef06daed37379ac07447 +Subproject commit b0ba8bded514e791ba8bd7eda4a513a762c5e5ae diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index 4289deeda3..1dd8eddab9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -166,7 +166,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -176,7 +176,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -356,7 +356,7 @@ public class PropertyFactory { } /** - * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a Float[] value * @return property wrapper around Float[] @@ -366,7 +366,7 @@ public class PropertyFactory { } /** - * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. + * Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash length to density-independent pixels, multiply the length by the current line width. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -376,7 +376,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -386,7 +386,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing image lines. For seamless patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -1156,7 +1156,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -1166,7 +1166,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing images on extruded fills. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement @@ -1536,7 +1536,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param value a String value * @return property wrapper around String @@ -1546,7 +1546,7 @@ public class PropertyFactory { } /** - * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). + * Name of image in sprite to use for drawing an image background. For seamless patterns, image width and height must be a factor of two (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be evaluated only at integer zoom levels. * * @param expression an expression statement * @return property wrapper around an expression statement diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index a159a924e6..90740223eb 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -218,7 +218,8 @@ MGL_EXPORT /** Name of image in sprite to use for drawing image fills. For seamless patterns, - image width and height must be a factor of two (2, 4, 8, ..., 512). + image width and height must be a factor of two (2, 4, 8, ..., 512). Note that + zoom-dependent expressions will be evaluated only at integer zoom levels. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index a7510142fc..32c8ece6c5 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -302,7 +302,10 @@ MGL_EXPORT /** Specifies the lengths of the alternating dashes and gaps that form the dash pattern. The lengths are later scaled by the line width. To convert a dash - length to points, multiply the length by the current line width. + length to points, multiply the length by the current line width. Note that + GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to + the expected scale. Also note that zoom-dependent expressions will be evaluated + only at integer zoom levels. This property is measured in line widths. diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index b7ca72cee0..195c9161fb 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -1,4 +1,22 @@ { + "expression-tests/collator/accent-equals-de": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/accent-lt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/accent-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-default-locale": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/base-gt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-lteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/case-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/comparison-number-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/diacritic-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/equals-non-string-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/non-object-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/variant-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/collator/variant-gteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/is-supported-script/default": "https://github.com/mapbox/mapbox-gl-native/issues/11693", + "expression-tests/resolved-locale/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11692", + "expression-tests/to-string/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11719", "query-tests/circle-pitch-scale/viewport-inside-align-map": "https://github.com/mapbox/mapbox-gl-native/issues/10615", "query-tests/circle-pitch-scale/viewport-inside-align-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/10615", "query-tests/edge-cases/box-cutting-antimeridian-z0": "https://github.com/mapbox/mapbox-gl-native/issues/11607", @@ -25,6 +43,10 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", + "render-tests/is-supported-script/filter": "https://github.com/mapbox/mapbox-gl-native/issues/11693", + "render-tests/is-supported-script/layout": "https://github.com/mapbox/mapbox-gl-native/issues/11693", + "render-tests/line-gradient/gradient": "https://github.com/mapbox/mapbox-gl-native/issues/11718", + "render-tests/line-gradient/translucent": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195", "render-tests/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412", diff --git a/scripts/generate-shaders.js b/scripts/generate-shaders.js index b1eeffb8a0..6758793056 100755 --- a/scripts/generate-shaders.js +++ b/scripts/generate-shaders.js @@ -7,6 +7,8 @@ const outputPath = 'src/mbgl/shaders'; var shaders = require('../mapbox-gl-js/src/shaders'); +delete shaders.lineGradient; + require('./style-code'); writeIfModified(path.join(outputPath, 'preludes.hpp'), `// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. diff --git a/scripts/style-spec.js b/scripts/style-spec.js index 196adc0b32..dd9a127b70 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.js @@ -1,3 +1,4 @@ var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8'); // Make temporary modifications here when Native doesn't have all features that JS has. +delete spec.paint_line['line-gradient']; diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index c700295a15..68d2dcc468 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; varying float v_gamma_scale; +varying highp float v_linesofar; #ifndef HAS_UNIFORM_u_color @@ -131,6 +132,8 @@ void main() { vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + vec2 pos = a_pos_normal.xy; // x is 1 if it's a round cap, 0 otherwise diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index fe5c261be1..d5598d873b 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -29,6 +29,10 @@ TEST(Expression, IsExpression) { for(auto& entry : allExpressions.GetObject()) { const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; + if (name == "collator" || name == "line-progress" || name == "is-supported-script" || name == "resolved-locale") { + // Not yet implemented + continue; + } JSDocument document; document.Parse<0>(R"([")" + name + R"("])"); -- cgit v1.2.1 From c04cf57a74fb17eb33893fb097a9c7e96bf24bcf Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 24 Apr 2018 15:24:51 -0700 Subject: Review changes: - assert symbol layer tiles must be geometry tiles, instead of dynamically checking - re-use retainedBucketQuery iterator instead of calling find twice. --- src/mbgl/text/placement.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index daf996356e..43e8ff4f93 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -46,9 +46,10 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri std::unordered_set seenCrossTileIDs; for (RenderTile& renderTile : symbolLayer.renderTiles) { - if (!renderTile.tile.isRenderable() || !dynamic_cast(&renderTile.tile)) { + if (!renderTile.tile.isRenderable()) { continue; } + assert(dynamic_cast(&renderTile.tile)); GeometryTile& geometryTile = static_cast(renderTile.tile); @@ -314,8 +315,9 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set& bucket.updateOpacity(); bucket.sortFeatures(state.getAngle()); - if (retainedQueryData.find(bucket.bucketInstanceId) != retainedQueryData.end()) { - retainedQueryData.find(bucket.bucketInstanceId)->second.featureSortOrder = bucket.featureSortOrder; + auto retainedData = retainedQueryData.find(bucket.bucketInstanceId); + if (retainedData != retainedQueryData.end()) { + retainedData->second.featureSortOrder = bucket.featureSortOrder; } } -- cgit v1.2.1 From 4344812ef5ef3b5b67e9f313e0c3cc87b3e95c46 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Thu, 26 Apr 2018 07:43:38 -0700 Subject: [core] Streaming TileCover for polygonal regions (#11267) A per-tile streaming algorithm for tile cover on points, lines, and polygons. Works for individual zoom levels, and not zoom ranges. --- benchmark/util/tilecover.benchmark.cpp | 96 ++++++ cmake/benchmark-files.cmake | 1 + cmake/core-files.cmake | 2 + include/mbgl/util/projection.hpp | 16 +- platform/default/mbgl/storage/offline.cpp | 2 +- .../renderer/layers/render_background_layer.cpp | 1 + src/mbgl/util/tile_cover.cpp | 104 ++++-- src/mbgl/util/tile_cover.hpp | 24 +- src/mbgl/util/tile_cover_impl.cpp | 365 +++++++++++++++++++++ src/mbgl/util/tile_cover_impl.hpp | 90 +++++ test/util/tile_cover.test.cpp | 232 ++++++++++++- test/util/tile_range.test.cpp | 8 +- 12 files changed, 886 insertions(+), 55 deletions(-) create mode 100644 benchmark/util/tilecover.benchmark.cpp create mode 100644 src/mbgl/util/tile_cover_impl.cpp create mode 100644 src/mbgl/util/tile_cover_impl.hpp diff --git a/benchmark/util/tilecover.benchmark.cpp b/benchmark/util/tilecover.benchmark.cpp new file mode 100644 index 0000000000..186de6f216 --- /dev/null +++ b/benchmark/util/tilecover.benchmark.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include + +using namespace mbgl; + +static const LatLngBounds sanFrancisco = + LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); + +static void TileCountBounds(benchmark::State& state) { + std::size_t length = 0; + while (state.KeepRunning()) { + auto count = util::tileCount(sanFrancisco, 10); + length += count; + } +} + +static void TileCoverPitchedViewport(benchmark::State& state) { + Transform transform; + transform.resize({ 512, 512 }); + // slightly offset center so that tile order is better defined + transform.setLatLng({ 0.1, -0.1 }); + transform.setZoom(8); + transform.setAngle(5.0); + transform.setPitch(40.0 * M_PI / 180.0); + + std::size_t length = 0; + while (state.KeepRunning()) { + auto tiles = util::tileCover(transform.getState(), 8); + length += tiles.size(); + } +} + +static void TileCoverBounds(benchmark::State& state) { + std::size_t length = 0; + while (state.KeepRunning()) { + auto tiles = util::tileCover(sanFrancisco, 8); + length += tiles.size(); + } +} + +static const auto geomPolygon = Polygon{ + { + {-122.5143814086914,37.779127216982424}, + {-122.50811576843262,37.72721239056709}, + {-122.50313758850099,37.70820178063929}, + {-122.3938751220703,37.707454835665274}, + {-122.37567901611328,37.70663997801684}, + {-122.36297607421874,37.71343018466285}, + {-122.354736328125,37.727280276860036}, + {-122.36469268798828,37.73868429065797}, + {-122.38014221191408,37.75442980295571}, + {-122.38391876220702,37.78753873820529}, + {-122.35919952392578,37.8065289741725}, + {-122.35679626464844,37.820632846207864}, + {-122.3712158203125,37.835276322922695}, + {-122.3818588256836,37.82958198283902}, + {-122.37190246582031,37.80788523279169}, + {-122.38735198974608,37.791337175930686}, + {-122.40966796874999,37.812767557570204}, + {-122.46425628662108,37.807071480609274}, + {-122.46803283691405,37.810326435534755}, + {-122.47901916503906,37.81168262440736}, + {-122.48966217041016,37.78916666399649}, + {-122.50579833984375,37.78781006166096}, + {-122.5143814086914,37.779127216982424} + } +}; + +static void TileCoverPolygon(benchmark::State& state) { + std::size_t length = 0; + + while (state.KeepRunning()) { + auto tiles = util::tileCover(geomPolygon, 8); + length += tiles.size(); + } +} + +static void TileCountPolygon(benchmark::State& state) { + std::size_t length = 0; + + while (state.KeepRunning()) { + auto tiles = util::tileCount(geomPolygon, 16); + length += tiles; + } +} + +BENCHMARK(TileCountBounds); +BENCHMARK(TileCountPolygon); +BENCHMARK(TileCoverPitchedViewport); +BENCHMARK(TileCoverBounds); +BENCHMARK(TileCoverPolygon); + diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake index fdafcf30f9..21547eb9c4 100644 --- a/cmake/benchmark-files.cmake +++ b/cmake/benchmark-files.cmake @@ -23,5 +23,6 @@ set(MBGL_BENCHMARK_FILES # util benchmark/util/dtoa.benchmark.cpp + benchmark/util/tilecover.benchmark.cpp ) diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 0853097630..d4fec34bf5 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -753,6 +753,8 @@ set(MBGL_CORE_FILES src/mbgl/util/tile_coordinate.hpp src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp + src/mbgl/util/tile_cover_impl.cpp + src/mbgl/util/tile_cover_impl.hpp src/mbgl/util/tile_range.hpp src/mbgl/util/tiny_sdf.cpp src/mbgl/util/tiny_sdf.hpp diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index b4a34521a4..65a84d8dc2 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -78,8 +78,9 @@ public: return project_(latLng, worldSize(scale)); } - static Point project(const LatLng& latLng, uint8_t zoom) { - return project_(latLng, std::pow(2.0, zoom)); + //Returns point on tile + static Point project(const LatLng& latLng, int32_t zoom) { + return project_(latLng, 1 << zoom); } static LatLng unproject(const Point& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -90,16 +91,7 @@ public: wrapMode }; } - - // Project lat, lon to point in a zoom-dependent world size - static Point project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { - const double t2z = tileSize * std::pow(2, zoom); - Point pt = project_(point, t2z); - // Flip y coordinate - auto x = ::round(std::min(pt.x, t2z)); - auto y = ::round(std::min(t2z - pt.y, t2z)); - return { x, y }; - } + private: static Point project_(const LatLng& latLng, double worldSize) { return Point { diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index 7670790be9..598a0b182b 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -43,7 +43,7 @@ uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, u const Range clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); unsigned long result = 0;; for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { - result += util::tileCount(bounds, z, tileSize); + result += util::tileCount(bounds, z); } return result; diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index aebc4cc9aa..44c3fffb6c 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace mbgl { diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 39b562d811..488e6b88ce 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -2,13 +2,18 @@ #include #include #include +#include +#include #include +#include namespace mbgl { namespace { +using ScanLine = const std::function; + // Taken from polymaps src/Layer.js // https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383 struct edge { @@ -27,8 +32,6 @@ struct edge { } }; -using ScanLine = const std::function; - // scan-line conversion static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) { double y0 = ::fmax(ymin, std::floor(e1.y0)); @@ -147,11 +150,11 @@ std::vector tileCover(const LatLngBounds& bounds_, int32_t z) { { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); return tileCover( - TileCoordinate::fromLatLng(z, bounds.northwest()).p, - TileCoordinate::fromLatLng(z, bounds.northeast()).p, - TileCoordinate::fromLatLng(z, bounds.southeast()).p, - TileCoordinate::fromLatLng(z, bounds.southwest()).p, - TileCoordinate::fromLatLng(z, bounds.center()).p, + Projection::project(bounds.northwest(), z), + Projection::project(bounds.northeast(), z), + Projection::project(bounds.southeast(), z), + Projection::project(bounds.southwest(), z), + Projection::project(bounds.center(), z), z); } @@ -169,25 +172,80 @@ std::vector tileCover(const TransformState& state, int32_t z) { z); } +std::vector tileCover(const Geometry& geometry, int32_t z) { + std::vector result; + TileCover tc(geometry, z, true); + while (tc.hasNext()) { + result.push_back(*tc.next()); + }; + + return result; +} + // Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs // Computes the projected tiles for the lower left and upper right points of the bounds // and uses that to compute the tile cover count -uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){ - - auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_); - auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_); - - auto x1 = floor(sw.x/ tileSize_); - auto x2 = floor((ne.x - 1) / tileSize_); - auto y1 = floor(sw.y/ tileSize_); - auto y2 = floor((ne.y - 1) / tileSize_); - - auto minX = ::fmax(std::min(x1, x2), 0); - auto maxX = std::max(x1, x2); - auto minY = (std::pow(2, zoom) - 1) - std::max(y1, y2); - auto maxY = (std::pow(2, zoom) - 1) - ::fmax(std::min(y1, y2), 0); - - return (maxX - minX + 1) * (maxY - minY + 1); +uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){ + if (zoom == 0) { + return 1; + } + auto sw = Projection::project(bounds.southwest(), zoom); + auto ne = Projection::project(bounds.northeast(), zoom); + auto maxTile = std::pow(2.0, zoom); + auto x1 = floor(sw.x); + auto x2 = ceil(ne.x) - 1; + auto y1 = util::clamp(floor(sw.y), 0.0, maxTile - 1); + auto y2 = util::clamp(floor(ne.y), 0.0, maxTile - 1); + + auto dx = x1 > x2 ? (maxTile - x1) + x2 : x2 - x1; + auto dy = y1 - y2; + return (dx + 1) * (dy + 1); +} + +uint64_t tileCount(const Geometry& geometry, uint8_t z) { + uint64_t tileCount = 0; + + TileCover tc(geometry, z, true); + while (tc.next()) { + tileCount++; + }; + return tileCount; +} + +TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) { + LatLngBounds bounds = LatLngBounds::hull( + { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() }, + { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() }); + + if (bounds.isEmpty() || + bounds.south() > util::LATITUDE_MAX || + bounds.north() < -util::LATITUDE_MAX) { + bounds = LatLngBounds::world(); + } + + auto sw = Projection::project(bounds.southwest(), z); + auto ne = Projection::project(bounds.northeast(), z); + auto se = Projection::project(bounds.southeast(), z); + auto nw = Projection::project(bounds.northwest(), z); + + Polygon p({ {sw, nw, ne, se, sw} }); + impl = std::make_unique(z, p, false); +} + +TileCover::TileCover(const Geometry& geom, int32_t z, bool project/* = true*/) + : impl( std::make_unique(z, geom, project)) { +} + +TileCover::~TileCover() { + +} + +optional TileCover::next() { + return impl->next(); +} + +bool TileCover::hasNext() { + return impl->hasNext(); } } // namespace util diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp index b2098b59b8..c953d764d2 100644 --- a/src/mbgl/util/tile_cover.hpp +++ b/src/mbgl/util/tile_cover.hpp @@ -2,9 +2,11 @@ #include #include -#include +#include +#include #include +#include namespace mbgl { @@ -13,13 +15,31 @@ class LatLngBounds; namespace util { +// Helper class to stream tile-cover results per row +class TileCover { +public: + TileCover(const LatLngBounds&, int32_t z); + // When project == true, projects the geometry points to tile coordinates + TileCover(const Geometry&, int32_t z, bool project = true); + ~TileCover(); + + optional next(); + bool hasNext(); + +private: + class Impl; + std::unique_ptr impl; +}; + int32_t coveringZoomLevel(double z, style::SourceType type, uint16_t tileSize); std::vector tileCover(const TransformState&, int32_t z); std::vector tileCover(const LatLngBounds&, int32_t z); +std::vector tileCover(const Geometry&, int32_t z); // Compute only the count of tiles needed for tileCover -uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize); +uint64_t tileCount(const LatLngBounds&, uint8_t z); +uint64_t tileCount(const Geometry&, uint8_t z); } // namespace util } // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp new file mode 100644 index 0000000000..b3fc07f7dd --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.cpp @@ -0,0 +1,365 @@ +#include +#include + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +using PointList = std::vector>; + +struct TileSpan { + int32_t xmin, xmax; + bool winding; +}; + + +// Find the first local minimum going forward in the list. +void start_list_on_local_minimum(PointList& points) { + auto prev_pt = std::prev(points.end(), 2); + auto pt = points.begin(); + auto next_pt = std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + //Re-close linear rings with first_pt = last_pt + if (points.back() == points.front()) { + points.pop_back(); + } + std::rotate(points.begin(), pt, points.end()); + points.push_back(*points.begin()); +} + +//Create a bound towards a local maximum point, starting from pt. +Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + const auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y >= prev_pt->y) && + (pt->y > next_pt->y )) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + std::copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = true; + return bnd; +} + +//Create a bound towards a local minimum point, starting from pt. +Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) { + if (std::distance(pt, points.end()) < 2) { return {}; } + if (std::distance(pt, points.end()) == 2) { + Bound bnd; + if (pt->y < std::next(pt)->y) { + std::copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = true; + } + else { + std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points)); + bnd.winding = false; + } + pt = points.end(); + return bnd; + } + auto begin = pt; + auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt); + auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt); + while (pt != points.end()) { + if ((pt->y <= prev_pt->y) && + (pt->y < next_pt->y)) { + break; + } + prev_pt = pt; + pt++; + next_pt++; + if (next_pt == points.end()) { next_pt = std::next(points.begin()); } + } + + Bound bnd; + if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; }; + bnd.points.reserve(static_cast(std::distance(begin, next_pt))); + //For bounds that start at a max, reverse copy so that all bounds start at a min + std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points)); + bnd.winding = false; + return bnd; +} + +//Build a map of bounds and their starting Y tile coordinate. +void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) { + if (points.size() < 2) return; + //While traversing closed rings, start the bounds at a local minimum + if (closed) { + start_list_on_local_minimum(points); + } + + auto pointsIter = points.begin(); + while (pointsIter != points.end()) { + Bound to_max = create_bound_towards_maximum(points, pointsIter); + Bound to_min = create_bound_towards_minimum(points, pointsIter); + + if (to_max.points.size() > 0) { + // Projections may result in values beyond the bounds, clamp to max tile coordinates + const auto y = static_cast(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_max); + } + if (to_min.points.size() > 0) { + const auto y = static_cast(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile))); + et[y].push_back(to_min); + } + } + assert(pointsIter == points.end()); +} + +void update_span(TileSpan& xp, double x) { + xp.xmin = std::min(xp.xmin, static_cast(std::floor(x))); + xp.xmax = std::max(xp.xmax, static_cast(std::ceil(x))); +} + +//Build a vector of X tile-coordinates spanned by each bound. +std::vector scan_row(uint32_t y, Bounds& aet) { + std::vector tile_range; + tile_range.reserve(aet.size()); + + for(Bound& b: aet) { + TileSpan xp = { INT_MAX, 0, b.winding }; + double x; + const auto numEdges = b.points.size() - 1; + assert(numEdges >= 1); + while (b.currentPoint < numEdges) { + x = b.interpolate(y); + update_span(xp, x); + + // If this edge ends beyond the current row, find the x-intercept where + // it exits the row + auto& p1 = b.points[b.currentPoint + 1]; + if (p1.y > y+1) { + x = b.interpolate(y+1); + update_span(xp, x); + break; + } else if(b.currentPoint == numEdges - 1) { + // For last edge, consider x-intercept at the end of the edge. + x = p1.x; + update_span(xp, x); + } + b.currentPoint++; + } + tile_range.push_back(xp); + } + // Erase bounds in the active table whose current edge ends inside this row, + // or there are no more edges + auto bound = aet.begin(); + while (bound != aet.end()) { + if ( bound->currentPoint == bound->points.size() - 1 && + bound->points[bound->currentPoint].y <= y+1) { + bound = aet.erase(bound); + } else { + bound++; + } + } + // Sort the X-extents of each crossing bound by x_min, x_max + std::sort(tile_range.begin(), tile_range.end(), [] (TileSpan& a, TileSpan& b) { + return std::tie(a.xmin, a.xmax) < std::tie(b.xmin, b.xmax); + }); + + return tile_range; +} + +struct BuildBoundsMap { + int32_t zoom; + bool project = false; + BuildBoundsMap(int32_t z, bool p): zoom(z), project(p) {} + + void buildTable(const std::vector>& points, BoundsMap& et, bool closed = false) const { + PointList projectedPoints; + if (project) { + projectedPoints.reserve(points.size()); + for(const auto&p : points) { + projectedPoints.push_back( + Projection::project(LatLng{ p.y, p.x }, zoom)); + } + } else { + projectedPoints.insert(projectedPoints.end(), points.begin(), points.end()); + } + build_bounds_map(projectedPoints, 1 << zoom, et, closed); + } + + void buildPolygonTable(const Polygon& polygon, BoundsMap& et) const { + for(const auto&ring : polygon) { + buildTable(ring, et, true); + } + } + BoundsMap operator()(const Point&p) const { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + BoundsMap et; + const auto y = static_cast(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + return et; + } + + BoundsMap operator()(const MultiPoint& points) const { + BoundsMap et; + for (const Point& p: points) { + Bound bnd; + auto point = p; + if(project) { + point = Projection::project(LatLng{p.y, p.x}, zoom); + } + bnd.points.insert(bnd.points.end(), 2, point); + bnd.winding = false; + const auto y = static_cast(std::floor(clamp(point.y, 0.0, (double)(1 << zoom)))); + et[y].push_back(bnd); + } + return et; + } + + BoundsMap operator()(const LineString& lines) const { + BoundsMap et; + buildTable(lines, et); + return et; + } + + BoundsMap operator()(const MultiLineString& lines) const { + BoundsMap et; + for(const auto&line : lines) { + buildTable(line, et); + } + return et; + } + + BoundsMap operator()(const Polygon& polygon) const { + BoundsMap et; + buildPolygonTable(polygon, et); + return et; + } + + BoundsMap operator()(const MultiPolygon& polygons) const { + BoundsMap et; + for(const auto& polygon: polygons) { + buildPolygonTable(polygon, et); + } + return et; + } + + BoundsMap operator()(const mapbox::geometry::geometry_collection&) const { + return {}; + } +}; + +TileCover::Impl::Impl(int32_t z, const Geometry& geom, bool project) + : zoom(z) { + ToFeatureType toFeatureType; + isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon; + + BuildBoundsMap toBoundsMap(z, project); + boundsMap = apply_visitor(toBoundsMap, geom); + if (boundsMap.size() == 0) return; + + //Iniitalize the active edge table, and current row span + currentBounds = boundsMap.begin(); + tileY = 0; + nextRow(); + if (tileXSpans.empty()) return; + tileX = tileXSpans.front().first; +} + +void TileCover::Impl::nextRow() { + // Update AET for next row + if (currentBounds != boundsMap.end()) { + if (activeBounds.size() == 0 && currentBounds->first > tileY) { + //For multi-geoms: use the next row with an edge table starting point + tileY = currentBounds->first; + } + if (tileY == currentBounds->first) { + + std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds)); + currentBounds++; + } + } + //Scan aet and update currenRange with x_min, x_max pairs + auto xps = util::scan_row(tileY, activeBounds); + if (xps.size() == 0) { + return; + } + + auto x_min = xps[0].xmin; + auto x_max = xps[0].xmax; + int32_t nzRule = xps[0].winding ? 1 : -1; + for (size_t i = 1; i < xps.size(); i++) { + auto xp = xps[i]; + if (!(isClosed && nzRule != 0)) { + if (xp.xmin > x_max && xp.xmax >= x_max) { + tileXSpans.emplace(x_min, x_max); + x_min = xp.xmin; + } + } + nzRule += xp.winding ? 1 : -1; + x_max = std::max(x_min, xp.xmax); + } + tileXSpans.emplace(x_min, x_max); +} + +bool TileCover::Impl::hasNext() const { + return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom)); +} + +optional TileCover::Impl::next() { + if (!hasNext()) return {}; + + const auto x = tileX; + const auto y = tileY; + tileX++; + if (tileX >= tileXSpans.front().second) { + tileXSpans.pop(); + if(tileXSpans.empty()) { + tileY++; + nextRow(); + } + if (!tileXSpans.empty()) { + tileX = tileXSpans.front().first; + } + } + return UnwrappedTileID(zoom, x, y); +} + +} // namespace util +} // namespace mbgl diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp new file mode 100644 index 0000000000..7c16718984 --- /dev/null +++ b/src/mbgl/util/tile_cover_impl.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +class TransformState; +class LatLngBounds; + +namespace util { + +struct Bound; + +using Bounds = std::vector; +using BoundsMap = std::map; + +// A chain of points from a local minimum to a local maximum. `winding` indicates +// the direction of the original geometry. +struct Bound { + std::vector> points; + size_t currentPoint = 0; + bool winding = false; + + Bound() = default; + Bound(const Bound& rhs) { + points = rhs.points; + currentPoint = rhs.currentPoint; + winding = rhs.winding; + } + Bound& operator=(Bound&& rhs) { + points = std::move(rhs.points); + currentPoint = rhs.currentPoint; + winding = rhs.winding; + return *this; + } + + // Compute the interpolated x coordinate at y for the current edge + double interpolate(uint32_t y) { + const auto& p0 = points[currentPoint]; + const auto& p1 = points[currentPoint + 1]; + + const auto dx = p1.x - p0.x; + const auto dy = p1.y - p0.y; + auto x = p0.x; + if (dx == 0) { + return x; + } else if (dy == 0){ + return y <= p0.y ? p0.x : p1.x; + } + if (y < p0.y) return x; + if (y > p1.y) return p1.x; + x = (dx / dy) * (y - p0.y) + p0.x; + return x; + } +}; + +class TileCover::Impl { +public: + Impl(int32_t z, const Geometry& geom, bool project = true); + ~Impl() = default; + + optional next(); + bool hasNext() const; + +private: + using TileSpans = std::queue>; + + void nextRow(); + + const int32_t zoom; + bool isClosed; + + BoundsMap boundsMap; + BoundsMap::iterator currentBounds; + // List of bounds that begin at or before `tileY` + Bounds activeBounds; + + TileSpans tileXSpans; + uint32_t tileY; + int32_t tileX; +}; + +} // namespace util +} // namespace mbgl diff --git a/test/util/tile_cover.test.cpp b/test/util/tile_cover.test.cpp index 933c18b5ea..7defa761af 100644 --- a/test/util/tile_cover.test.cpp +++ b/test/util/tile_cover.test.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include using namespace mbgl; @@ -22,8 +24,8 @@ TEST(TileCover, Antarctic) { TEST(TileCover, WorldZ0) { EXPECT_EQ((std::vector{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(LatLngBounds::world(), 0)); } @@ -37,15 +39,15 @@ TEST(TileCover, Pitch) { transform.setPitch(40.0 * M_PI / 180.0); EXPECT_EQ((std::vector{ - { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } - }), + { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 } + }), util::tileCover(transform.getState(), 2)); } TEST(TileCover, WorldZ1) { EXPECT_EQ((std::vector{ - { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, - }), + { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 }, + }), util::tileCover(LatLngBounds::world(), 1)); } @@ -59,21 +61,44 @@ TEST(TileCover, SingletonZ1) { util::tileCover(LatLngBounds::singleton({ 0, 0 }), 1)); } +TEST(TileCoverStream, Arctic) { + auto bounds = LatLngBounds::hull({ 84, -180 }, { 70, 180 }); + auto zoom = 3; + util::TileCover tc(bounds, zoom); + auto results = util::tileCover(bounds, zoom); + std::vector t; + while(tc.hasNext()) { + t.push_back(*tc.next()); + }; + EXPECT_EQ(t.size(), results.size()); +} + +TEST(TileCoverStream, WorldZ1) { + auto zoom = 1; + util::TileCover tc(LatLngBounds::world(), zoom); + std::vector t; + while(tc.hasNext()) { + t.push_back(*tc.next()); + }; + EXPECT_EQ((std::vector{ + { 1, 0, 0 }, { 1, 1, 0 }, { 1, 0, 1 }, { 1, 1, 1 }, + }), t); +} + static const LatLngBounds sanFrancisco = LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 }); TEST(TileCover, SanFranciscoZ0) { EXPECT_EQ((std::vector{ - { 0, 0, 0 }, - }), + { 0, 0, 0 }, + }), util::tileCover(sanFrancisco, 0)); } TEST(TileCover, SanFranciscoZ10) { EXPECT_EQ((std::vector{ - { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, - - }), + { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 }, + }), util::tileCover(sanFrancisco, 10)); } @@ -85,11 +110,192 @@ TEST(TileCover, SanFranciscoZ0Wrapped) { util::tileCover(sanFranciscoWrapped, 0)); } +TEST(TileCover, GeomPoint) { + auto point = Point{ -122.5744, 37.6609 }; + + EXPECT_EQ((std::vector{ {2 ,0 ,1 } }), + util::tileCover(point, 2)); +} + +TEST(TileCover, GeomMultiPoint) { + auto points = MultiPoint{ { -122.5, 37.76 }, { -122.4, 37.76} }; + + EXPECT_EQ((std::vector{ + {20, 167480, 405351}, {20, 167772, 405351} }), + util::tileCover(points, 20)); +} + +TEST(TileCover, GeomLineZ10) { + auto lineCover = util::tileCover(LineString{ + {-121.49368286132812,38.57903714667459}, + {-122.4422836303711,37.773157169570695} + }, 10); + EXPECT_EQ((std::vector{ + { 10, 166, 392}, {10, 165, 393}, {10, 166, 393}, + {10, 164, 394}, {10, 165, 394}, {10,163,395}, {10, 164, 395} + }),lineCover); + +} + +TEST(TileCover, WrappedGeomLineZ10) { + auto lineCover = util::tileCover(LineString{ + {-179.93342914581299,38.892101707724315}, + {-180.02394485473633,38.89203490311832} + }, 10); + EXPECT_EQ((std::vector{ { 10, -1, 391 }, { 10, 0, 391 } }), + lineCover); + + lineCover = util::tileCover(LineString{ + {179.93342914581299,38.892101707724315}, + {180.02394485473633,38.89203490311832} + }, 10); + EXPECT_EQ((std::vector{ { 10, 1023, 391 }, { 10, 1024, 391 } }), + lineCover); +} + +TEST(TileCover, GeomMultiLineString) { + auto geom = MultiLineString{ + { { -122.5, 37.76 }, { -122.4, 37.76} }, + { { -122.5, 37.72 }, { -122.4, 37.72} } }; + + EXPECT_EQ((std::vector{ + {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333}, + {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333}, + {14, 2616, 6335}, {14, 2617, 6335}, {14, 2618, 6335}, + {14, 2619, 6335}, {14, 2620, 6335}, {14, 2621, 6335}}), + util::tileCover(geom, 14)); +} + +TEST(TileCover, GeomPolygon) { + auto polygon = Polygon{ + { + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + },{ + {19.6875,49.66762782262194}, + {22.8515625,43.51668853502906}, + {13.623046875,45.089035564831036}, + {16.34765625,39.095962936305476}, + {5.185546875,41.244772343082076}, + {8.701171874999998,50.233151832472245}, + {19.6875,49.66762782262194} + } + }; + + auto results = util::tileCover(polygon, 8); + + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 134, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + // Should have a hole + EXPECT_EQ(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); +} + +TEST(TileCover, GeomMultiPolygon) { + auto multiPolygon = MultiPolygon{ + {{ + {5.09765625,53.067626642387374}, + {2.373046875,43.389081939117496}, + {-4.74609375,48.45835188280866}, + {-1.494140625,37.09023980307208}, + {22.587890625,36.24427318493909}, + {31.640625,46.13417004624326}, + {17.841796875,54.7246201949245}, + {5.09765625,53.067626642387374}, + }},{{ + {59.150390625,45.460130637921004}, + {65.126953125,41.11246878918088}, + {69.169921875,47.45780853075031}, + {63.896484375,50.064191736659104}, + {59.150390625,45.460130637921004} + }} + }; + auto results = util::tileCover(multiPolygon, 8); + + EXPECT_EQ(424u, results.size()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end()); + EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 174, 94}), results.end()); +} + +TEST(TileCover, GeomSanFranciscoPoly) { + auto sanFranciscoGeom = Polygon{ + { + {-122.5143814086914,37.779127216982424}, + {-122.50811576843262,37.72721239056709}, + {-122.50313758850099,37.70820178063929}, + {-122.3938751220703,37.707454835665274}, + {-122.37567901611328,37.70663997801684}, + {-122.36297607421874,37.71343018466285}, + {-122.354736328125,37.727280276860036}, + {-122.36469268798828,37.73868429065797}, + {-122.38014221191408,37.75442980295571}, + {-122.38391876220702,37.78753873820529}, + {-122.35919952392578,37.8065289741725}, + {-122.35679626464844,37.820632846207864}, + {-122.3712158203125,37.835276322922695}, + {-122.3818588256836,37.82958198283902}, + {-122.37190246582031,37.80788523279169}, + {-122.38735198974608,37.791337175930686}, + {-122.40966796874999,37.812767557570204}, + {-122.46425628662108,37.807071480609274}, + {-122.46803283691405,37.810326435534755}, + {-122.47901916503906,37.81168262440736}, + {-122.48966217041016,37.78916666399649}, + {-122.50579833984375,37.78781006166096}, + {-122.5143814086914,37.779127216982424} + } + }; + + EXPECT_EQ((std::vector{ + { 12, 654, 1582 }, { 12, 655, 1582 }, + { 12, 654, 1583 }, { 12, 655, 1583 }, + { 12, 654, 1584 }, { 12, 655, 1584 } + }), util::tileCover(sanFranciscoGeom, 12)); +} + +TEST(TileCover, GeomInvalid) { + auto point = Point{ -122.5744, 97.6609 }; + EXPECT_THROW(util::tileCover(point, 2), std::domain_error); + + auto badPoly = Polygon { { {1.0, 35.0} } }; + EXPECT_EQ((std::vector{ }), util::tileCover(badPoly, 16)); + + //Should handle open polygons. + badPoly = Polygon { { {1.0, 34.2}, {1.0, 34.4}, {0.5, 34.3} } }; + EXPECT_EQ((std::vector{ + { 10, 513, 407 }, { 10, 514, 407}, + { 10, 513, 408 }, { 10, 514, 408} + }), util::tileCover(badPoly, 10)); +} + + +TEST(TileCount, World) { + EXPECT_EQ(1u, util::tileCount(LatLngBounds::world(), 0)); + EXPECT_EQ(4u, util::tileCount(LatLngBounds::world(), 1)); +} + TEST(TileCount, SanFranciscoZ10) { - EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize)); + EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10)); +} + +TEST(TileCount, SanFranciscoWrappedZ10) { + EXPECT_EQ(4u, util::tileCount(sanFranciscoWrapped, 10)); } TEST(TileCount, SanFranciscoZ22) { - EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize)); + EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22)); } +TEST(TileCount, BoundsCrossingAntimeridian) { + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + + EXPECT_EQ(1u, util::tileCount(crossingBounds, 0)); + EXPECT_EQ(4u, util::tileCount(crossingBounds, 3)); + EXPECT_EQ(8u, util::tileCount(crossingBounds, 4)); +} diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp index c4c37c74d7..83ac5096a5 100644 --- a/test/util/tile_range.test.cpp +++ b/test/util/tile_range.test.cpp @@ -52,14 +52,14 @@ TEST(TileRange, ContainsWrappedBounds) { TEST(TileRange, ContainsBoundsCrossingAntimeridian) { { - auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); - auto range = util::TileRange::fromLatLngBounds(cossingBounds, 1); + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(crossingBounds, 1); EXPECT_TRUE(range.contains(CanonicalTileID(1, 1, 1))); EXPECT_TRUE(range.contains(CanonicalTileID(1, 0, 0))); } { - auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); - auto range = util::TileRange::fromLatLngBounds(cossingBounds, 6); + auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830}); + auto range = util::TileRange::fromLatLngBounds(crossingBounds, 6); EXPECT_FALSE(range.contains(CanonicalTileID(6, 55, 34))); EXPECT_FALSE(range.contains(CanonicalTileID(6, 5, 28))); EXPECT_TRUE(range.contains(CanonicalTileID(6, 63, 28))); -- cgit v1.2.1 From 0eb58278f21c7d767b07449b6b167c6859283d5d Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 25 Apr 2018 15:20:31 -0700 Subject: [qt] Fix crash on the example app when launching multiple windows We need to have a valid context when destroying QMapboxGL. --- platform/qt/app/mapwindow.cpp | 7 +++++++ platform/qt/app/mapwindow.hpp | 1 + 2 files changed, 8 insertions(+) diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index f6d5473192..390d89915a 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -22,6 +22,13 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings) setWindowIcon(QIcon(":icon.png")); } +MapWindow::~MapWindow() +{ + // Make sure we have a valid context so we + // can delete the QMapboxGL. + makeCurrent(); +} + void MapWindow::selfTest() { if (m_bearingAnimation) { diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp index c484114ec0..6c05f03562 100644 --- a/platform/qt/app/mapwindow.hpp +++ b/platform/qt/app/mapwindow.hpp @@ -29,6 +29,7 @@ class MapWindow : public QGLWidget public: MapWindow(const QMapboxGLSettings &); + ~MapWindow(); void selfTest(); -- cgit v1.2.1 From 0767bf06869c380305c57df1b69d74fac5a6c94a Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 25 Apr 2018 15:22:00 -0700 Subject: [qt] Only share a FileSource if it points to the same path Previously all QMapboxGL objects were sharing the same cache created by the first instantiated object. Now it will share the cache only if it points to the same path. Fixes #11766 --- platform/qt/src/qmapboxgl.cpp | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index e2c1b31eea..8c3355dc09 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -89,15 +89,33 @@ QThreadStorage> loop; std::shared_ptr sharedDefaultFileSource( const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize) { - static std::weak_ptr weak; - auto fs = weak.lock(); + static std::mutex mutex; + static std::unordered_map> fileSources; - if (!fs) { - weak = fs = std::make_shared( - cachePath, assetRoot, maximumCacheSize); + std::lock_guard lock(mutex); + + // Purge entries no longer in use. + for (auto it = fileSources.begin(); it != fileSources.end();) { + if (!it->second.lock()) { + it = fileSources.erase(it); + } else { + ++it; + } } - return fs; + // Return an existing FileSource if available. + auto sharedFileSource = fileSources.find(cachePath); + if (sharedFileSource != fileSources.end()) { + return sharedFileSource->second.lock(); + } + + // New path, create a new FileSource. + auto newFileSource = std::make_shared( + cachePath, assetRoot, maximumCacheSize); + + fileSources[cachePath] = newFileSource; + + return newFileSource; } // Conversion helper functions. @@ -141,10 +159,9 @@ std::unique_ptr toStyleImage(const QString &id, const QImage QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation. Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed. - Cache-related settings are shared between all QMapboxGL instances because different - maps will share the same cache database file. The first map to configure cache properties - such as size and path will force the configuration to all newly instantiated QMapboxGL - objects. + Cache-related settings are shared between all QMapboxGL instances using the same cache path. + The first map to configure cache properties such as size will force the configuration + to all newly instantiated QMapboxGL objects using the same cache in the same process. \since 4.7 */ -- cgit v1.2.1 From f7d20a587199ccee42469ecea2299ae73bfaae49 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 2 Apr 2018 17:41:09 -0700 Subject: [core] fix querying circles across tile boundaries --- src/mbgl/geometry/feature_index.cpp | 8 ++++---- src/mbgl/geometry/feature_index.hpp | 2 +- src/mbgl/renderer/tile_pyramid.cpp | 6 ++++-- src/mbgl/tile/geometry_tile.cpp | 22 +++++++++++++--------- src/mbgl/tile/geometry_tile.hpp | 2 ++ src/mbgl/tile/tile.cpp | 4 ++++ src/mbgl/tile/tile.hpp | 2 ++ 7 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 8ea6259129..728996a2eb 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -42,7 +42,7 @@ void FeatureIndex::query( const RenderedQueryOptions& queryOptions, const UnwrappedTileID& tileID, const std::vector& layers, - const float additionalQueryRadius) const { + const float additionalQueryPadding) const { if (!tileData) { return; @@ -50,12 +50,12 @@ void FeatureIndex::query( // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = std::min(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); + const int16_t additionalPadding = std::min(util::EXTENT, additionalQueryPadding * pixelsToTileUnits); // Query the grid index mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); - std::vector features = grid.query({ convertPoint(box.min - additionalRadius), - convertPoint(box.max + additionalRadius) }); + std::vector features = grid.query({ convertPoint(box.min - additionalPadding), + convertPoint(box.max + additionalPadding) }); std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) { diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 5a15a379c4..f105260c0c 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -62,7 +62,7 @@ public: const RenderedQueryOptions& options, const UnwrappedTileID&, const std::vector&, - const float additionalQueryRadius) const; + const float additionalQueryPadding) const; static optional translateQueryGeometry( const GeometryCoordinates& queryGeometry, diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index e2d4d9829f..b2d72680e7 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -264,13 +264,15 @@ std::unordered_map> TilePyramid::queryRendered }); for (const RenderTile& renderTile : sortedTiles) { + auto queryPadding = renderTile.tile.getQueryPadding(layers); + GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); - if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { + if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) { continue; } GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); - if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { + if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) { continue; } diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 317c5454f6..92a226d3c1 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -201,6 +201,17 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const { return it->second.get(); } +float GeometryTile::getQueryPadding(const std::vector& layers) { + float queryPadding = 0; + for (const RenderLayer* layer : layers) { + auto bucket = getBucket(*layer->baseImpl); + if (bucket) { + queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer)); + } + } + return queryPadding; +} + void GeometryTile::queryRenderedFeatures( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, @@ -210,14 +221,7 @@ void GeometryTile::queryRenderedFeatures( if (!getData()) return; - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - for (const RenderLayer* layer : layers) { - auto bucket = getBucket(*layer->baseImpl); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer)); - } - } + const float queryPadding = getQueryPadding(layers); latestFeatureIndex->query(result, queryGeometry, @@ -227,7 +231,7 @@ void GeometryTile::queryRenderedFeatures( options, id.toUnwrapped(), layers, - additionalRadius); + queryPadding); } void GeometryTile::querySourceFeatures( diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index a3b2eb6492..a43bf44940 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -60,6 +60,8 @@ public: std::vector& result, const SourceQueryOptions&) override; + float getQueryPadding(const std::vector&) override; + void cancel() override; class LayoutResult { diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index e65bc39540..56698e84c4 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -39,6 +39,10 @@ void Tile::queryRenderedFeatures( const std::vector&, const RenderedQueryOptions&) {} +float Tile::getQueryPadding(const std::vector&) { + return 0; +} + void Tile::querySourceFeatures( std::vector&, const SourceQueryOptions&) {} diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index a0effa29a1..5f7a076850 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -63,6 +63,8 @@ public: std::vector& result, const SourceQueryOptions&); + virtual float getQueryPadding(const std::vector&); + void setTriedCache(); // Returns true when the tile source has received a first response, regardless of whether a load -- cgit v1.2.1 From f86fe44dbd4de44c9fc8cb364521f966039289d7 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 2 Apr 2018 17:41:55 -0700 Subject: [core] fix circle querying for scale and alignment This fixes circle querying for cases where either circle-pitch-alignment=map or circle-pitch-scaling=viewport --- src/mbgl/annotation/render_annotation_source.cpp | 5 +- src/mbgl/annotation/render_annotation_source.hpp | 3 +- src/mbgl/geometry/feature_index.cpp | 14 +++-- src/mbgl/geometry/feature_index.hpp | 10 +++- src/mbgl/map/transform_state.cpp | 13 +++++ src/mbgl/map/transform_state.hpp | 1 + src/mbgl/renderer/layers/render_circle_layer.cpp | 66 +++++++++++++++++++--- src/mbgl/renderer/layers/render_circle_layer.hpp | 3 +- .../layers/render_fill_extrusion_layer.cpp | 7 ++- .../layers/render_fill_extrusion_layer.hpp | 3 +- src/mbgl/renderer/layers/render_fill_layer.cpp | 7 ++- src/mbgl/renderer/layers/render_fill_layer.hpp | 3 +- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 6 +- src/mbgl/renderer/layers/render_heatmap_layer.hpp | 3 +- src/mbgl/renderer/layers/render_line_layer.cpp | 7 ++- src/mbgl/renderer/layers/render_line_layer.hpp | 3 +- src/mbgl/renderer/render_layer.hpp | 5 +- src/mbgl/renderer/render_source.hpp | 3 +- src/mbgl/renderer/renderer_impl.cpp | 5 +- .../sources/render_custom_geometry_source.cpp | 5 +- .../sources/render_custom_geometry_source.hpp | 3 +- .../renderer/sources/render_geojson_source.cpp | 5 +- .../renderer/sources/render_geojson_source.hpp | 3 +- src/mbgl/renderer/sources/render_image_source.cpp | 3 +- src/mbgl/renderer/sources/render_image_source.hpp | 3 +- .../renderer/sources/render_raster_dem_source.cpp | 3 +- .../renderer/sources/render_raster_dem_source.hpp | 3 +- src/mbgl/renderer/sources/render_raster_source.cpp | 3 +- src/mbgl/renderer/sources/render_raster_source.hpp | 3 +- src/mbgl/renderer/sources/render_vector_source.cpp | 5 +- src/mbgl/renderer/sources/render_vector_source.hpp | 3 +- src/mbgl/renderer/tile_pyramid.cpp | 11 +++- src/mbgl/renderer/tile_pyramid.hpp | 3 +- src/mbgl/tile/geometry_tile.cpp | 12 +++- src/mbgl/tile/geometry_tile.hpp | 3 +- src/mbgl/tile/tile.cpp | 3 +- src/mbgl/tile/tile.hpp | 3 +- src/mbgl/util/intersection_tests.cpp | 9 ++- src/mbgl/util/intersection_tests.hpp | 1 + 39 files changed, 186 insertions(+), 68 deletions(-) diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index 19d19d2901..7d776f21c4 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -64,8 +64,9 @@ std::unordered_map> RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); + const RenderedQueryOptions& options, + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index da5376ab2d..da87d13814 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -27,7 +27,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 728996a2eb..e7759b0868 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -36,7 +36,8 @@ void FeatureIndex::insert(const GeometryCollection& geometries, void FeatureIndex::query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState& transformState, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& queryOptions, @@ -68,7 +69,7 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix); } } @@ -105,7 +106,7 @@ std::unordered_map> FeatureIndex::lookupSymbol }); for (const auto& symbolFeature : sortedFeatures) { - addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), 0, 0); + addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, {}); } return result; } @@ -117,8 +118,9 @@ void FeatureIndex::addFeature( const CanonicalTileID& tileID, const std::vector& layers, const GeometryCoordinates& queryGeometry, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { for (const auto& layer : layers) { @@ -148,7 +150,7 @@ void FeatureIndex::addFeature( } if (!renderLayer->is() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) { continue; } diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index f105260c0c..cc91791d36 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ namespace mbgl { class RenderedQueryOptions; class RenderLayer; +class TransformState; class CollisionIndex; @@ -56,7 +58,8 @@ public: void query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, - const float bearing, + const TransformState&, + const mat4& posMatrix, const double tileSize, const double scale, const RenderedQueryOptions& options, @@ -88,8 +91,9 @@ private: const CanonicalTileID&, const std::vector&, const GeometryCoordinates& queryGeometry, - const float bearing, - const float pixelsToTileUnits) const; + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const; GridIndex grid; unsigned int sortIndex = 0; diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 18d2c24aee..a85b251fb4 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -421,4 +421,17 @@ float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) con return projectedCenter[3]; } +float TransformState::maxPitchScaleFactor() const { + if (size.isEmpty()) { + return {}; + } + auto latLng = screenCoordinateToLatLng({ 0, static_cast(getSize().height) }); + mat4 mat = coordinatePointMatrix(getZoom()); + Point pt = Projection::project(latLng, scale) / double(util::tileSize); + vec4 p = {{ pt.x, pt.y, 0, 1 }}; + vec4 topPoint; + matrix::transformMat4(topPoint, p, mat); + return topPoint[3] / getCameraToCenterDistance(); +} + } // namespace mbgl diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 451802034d..b6f8ae4424 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -87,6 +87,7 @@ public: } float getCameraToTileDistance(const UnwrappedTileID&) const; + float maxPitchScaleFactor() const; private: bool rotatedNorth() const; diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 6092ff5452..6e1c867a30 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -93,27 +93,75 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { } } +GeometryCoordinate projectPoint(const GeometryCoordinate& p, const mat4& posMatrix, const Size& size) { + vec4 pos = {{ static_cast(p.x), static_cast(p.y), 0, 1 }}; + matrix::transformMat4(pos, pos, posMatrix); + return { + static_cast((static_cast(pos[0] / pos[3]) + 1) * size.width * 0.5), + static_cast((static_cast(pos[1] / pos[3]) + 1) * size.height * 0.5) + }; +} + +GeometryCoordinates projectQueryGeometry(const GeometryCoordinates& queryGeometry, const mat4& posMatrix, const Size& size) { + GeometryCoordinates projectedGeometry; + for (auto& p : queryGeometry) { + projectedGeometry.push_back(projectPoint(p, posMatrix, size)); + } + return projectedGeometry; +} + bool RenderCircleLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4& posMatrix) const { // Translate query geometry - auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + const GeometryCoordinates& translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, - pixelsToTileUnits); + transformState.getAngle(), + pixelsToTileUnits).value_or(queryGeometry); // Evaluate functions - auto radius = evaluated.evaluate(zoom, feature) * pixelsToTileUnits; - auto stroke = evaluated.evaluate(zoom, feature) * pixelsToTileUnits; + auto radius = evaluated.evaluate(zoom, feature); + auto stroke = evaluated.evaluate(zoom, feature); + auto size = radius + stroke; + + // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile + // Otherwise, compare geometry in the plane of the viewport + // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance + // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance + bool alignWithMap = evaluated.evaluate(zoom, feature) == AlignmentType::Map; + const GeometryCoordinates& transformedQueryGeometry = alignWithMap ? + translatedQueryGeometry : + projectQueryGeometry(translatedQueryGeometry, posMatrix, transformState.getSize()); + auto transformedSize = alignWithMap ? size * pixelsToTileUnits : size; + + auto geometry = feature.getGeometries(); + for (auto& ring : geometry) { + for (auto& point : ring) { + const GeometryCoordinate& transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transformState.getSize()); + + float adjustedSize = transformedSize; + vec4 center = {{ static_cast(point.x), static_cast(point.y), 0, 1 }}; + matrix::transformMat4(center, center, posMatrix); + auto pitchScale = evaluated.evaluate(zoom, feature); + auto pitchAlignment = evaluated.evaluate(zoom, feature); + if (pitchScale == CirclePitchScaleType::Viewport && pitchAlignment == AlignmentType::Map) { + adjustedSize *= center[3] / transformState.getCameraToCenterDistance(); + } else if (pitchScale == CirclePitchScaleType::Map && pitchAlignment == AlignmentType::Viewport) { + adjustedSize *= transformState.getCameraToCenterDistance() / center[3]; + } + + if (util::polygonIntersectsBufferedPoint(transformedQueryGeometry, transformedPoint, adjustedSize)) return true; + } + } - // Test intersection - return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), radius + stroke); + return false; } } // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp index f31715f98f..c9eeae4652 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.hpp +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index fbd6160e8a..871464223c 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -151,14 +151,15 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp index 838494cf91..f7ba13c267 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 22cb9563c1..efd3f4215c 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -188,14 +188,15 @@ bool RenderFillLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp index a51865698f..bd195fb828 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.hpp +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -20,8 +20,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 4f2e899220..72c60446aa 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -165,12 +165,12 @@ bool RenderHeatmapLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState&, + const float pixelsToTileUnits, + const mat4&) const { (void) queryGeometry; (void) feature; (void) zoom; - (void) bearing; (void) pixelsToTileUnits; return false; } diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.hpp b/src/mbgl/renderer/layers/render_heatmap_layer.hpp index 3f0b1f91b4..29fad7d8b8 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.hpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp @@ -22,8 +22,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; void updateColorRamp(); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 1b4a1c0ff7..02f61af0fa 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -162,15 +162,16 @@ bool RenderLineLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, const float zoom, - const float bearing, - const float pixelsToTileUnits) const { + const TransformState& transformState, + const float pixelsToTileUnits, + const mat4&) const { // Translate query geometry auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( queryGeometry, evaluated.get(), evaluated.get(), - bearing, + transformState.getAngle(), pixelsToTileUnits); // Evaluate function diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 8bf7e2329d..5d5d79c044 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -29,8 +29,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const override; + const mat4&) const override; std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const override; diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 55831cb72c..04a1608564 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ class PropertyEvaluationParameters; class PaintParameters; class RenderSource; class RenderTile; +class TransformState; class RenderLayer { protected: @@ -69,8 +71,9 @@ public: const GeometryCoordinates&, const GeometryTileFeature&, const float, + const TransformState&, const float, - const float) const { return false; }; + const mat4&) const { return false; }; virtual std::unique_ptr createBucket(const BucketParameters&, const std::vector&) const = 0; diff --git a/src/mbgl/renderer/render_source.hpp b/src/mbgl/renderer/render_source.hpp index 54a0b5db43..dc80cb1dc6 100644 --- a/src/mbgl/renderer/render_source.hpp +++ b/src/mbgl/renderer/render_source.hpp @@ -64,7 +64,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const = 0; + const RenderedQueryOptions& options, + const mat4& projMatrix) const = 0; virtual std::vector querySourceFeatures(const SourceQueryOptions&) const = 0; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 5b739678fb..ded07a0909 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -704,10 +704,13 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin sourceIDs.emplace(layer->baseImpl->source); } + mat4 projMatrix; + transformState.getProjMatrix(projMatrix); + std::unordered_map> resultsByLayer; for (const auto& sourceID : sourceIDs) { if (RenderSource* renderSource = getRenderSource(sourceID)) { - auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options); + auto sourceResults = renderSource->queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin())); } } diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp index 88792db00b..2d28b8dd84 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.cpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.cpp @@ -67,8 +67,9 @@ std::unordered_map> RenderCustomGeometrySource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); + const RenderedQueryOptions& options, + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderCustomGeometrySource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp index 9e47b9e392..5533fe2b83 100644 --- a/src/mbgl/renderer/sources/render_custom_geometry_source.hpp +++ b/src/mbgl/renderer/sources/render_custom_geometry_source.hpp @@ -27,7 +27,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_geojson_source.cpp b/src/mbgl/renderer/sources/render_geojson_source.cpp index 7492a4cf16..0e265efff4 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.cpp +++ b/src/mbgl/renderer/sources/render_geojson_source.cpp @@ -85,8 +85,9 @@ std::unordered_map> RenderGeoJSONSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); + const RenderedQueryOptions& options, + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderGeoJSONSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_geojson_source.hpp b/src/mbgl/renderer/sources/render_geojson_source.hpp index bcdc109953..297fa09a29 100644 --- a/src/mbgl/renderer/sources/render_geojson_source.hpp +++ b/src/mbgl/renderer/sources/render_geojson_source.hpp @@ -31,7 +31,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index b35e151f82..5c497e8144 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -84,7 +84,8 @@ std::unordered_map> RenderImageSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&) const { + const RenderedQueryOptions&, + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_image_source.hpp b/src/mbgl/renderer/sources/render_image_source.hpp index 7dc89d3591..cf14e180fd 100644 --- a/src/mbgl/renderer/sources/render_image_source.hpp +++ b/src/mbgl/renderer/sources/render_image_source.hpp @@ -32,7 +32,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.cpp b/src/mbgl/renderer/sources/render_raster_dem_source.cpp index 58bdba1840..fbf2c09d19 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.cpp @@ -146,7 +146,8 @@ std::unordered_map> RenderRasterDEMSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&) const { + const RenderedQueryOptions&, + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_dem_source.hpp b/src/mbgl/renderer/sources/render_raster_dem_source.hpp index 512fe6367c..48c7803e92 100644 --- a/src/mbgl/renderer/sources/render_raster_dem_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_dem_source.hpp @@ -27,7 +27,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_raster_source.cpp b/src/mbgl/renderer/sources/render_raster_source.cpp index 3162acc7b4..f97ce4e65b 100644 --- a/src/mbgl/renderer/sources/render_raster_source.cpp +++ b/src/mbgl/renderer/sources/render_raster_source.cpp @@ -76,7 +76,8 @@ std::unordered_map> RenderRasterSource::queryRenderedFeatures(const ScreenLineString&, const TransformState&, const std::vector&, - const RenderedQueryOptions&) const { + const RenderedQueryOptions&, + const mat4&) const { return std::unordered_map> {}; } diff --git a/src/mbgl/renderer/sources/render_raster_source.hpp b/src/mbgl/renderer/sources/render_raster_source.hpp index c60a51c63b..32539a046d 100644 --- a/src/mbgl/renderer/sources/render_raster_source.hpp +++ b/src/mbgl/renderer/sources/render_raster_source.hpp @@ -27,7 +27,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/sources/render_vector_source.cpp b/src/mbgl/renderer/sources/render_vector_source.cpp index c9c47359f4..4de4f01e3f 100644 --- a/src/mbgl/renderer/sources/render_vector_source.cpp +++ b/src/mbgl/renderer/sources/render_vector_source.cpp @@ -79,8 +79,9 @@ std::unordered_map> RenderVectorSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); + const RenderedQueryOptions& options, + const mat4& projMatrix) const { + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix); } std::vector RenderVectorSource::querySourceFeatures(const SourceQueryOptions& options) const { diff --git a/src/mbgl/renderer/sources/render_vector_source.hpp b/src/mbgl/renderer/sources/render_vector_source.hpp index a0351c226c..6fd2425aa3 100644 --- a/src/mbgl/renderer/sources/render_vector_source.hpp +++ b/src/mbgl/renderer/sources/render_vector_source.hpp @@ -27,7 +27,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const final; + const RenderedQueryOptions& options, + const mat4& projMatrix) const final; std::vector querySourceFeatures(const SourceQueryOptions&) const final; diff --git a/src/mbgl/renderer/tile_pyramid.cpp b/src/mbgl/renderer/tile_pyramid.cpp index b2d72680e7..d28e95181b 100644 --- a/src/mbgl/renderer/tile_pyramid.cpp +++ b/src/mbgl/renderer/tile_pyramid.cpp @@ -241,7 +241,8 @@ void TilePyramid::update(const std::vector>& layer std::unordered_map> TilePyramid::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) const { + const RenderedQueryOptions& options, + const mat4& projMatrix) const { std::unordered_map> result; if (renderTiles.empty() || geometry.empty()) { return result; @@ -263,8 +264,11 @@ std::unordered_map> TilePyramid::queryRendered std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x); }); + auto maxPitchScaleFactor = transformState.maxPitchScaleFactor(); + for (const RenderTile& renderTile : sortedTiles) { - auto queryPadding = renderTile.tile.getQueryPadding(layers); + const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z); + auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale; GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) { @@ -286,7 +290,8 @@ std::unordered_map> TilePyramid::queryRendered tileSpaceQueryGeometry, transformState, layers, - options); + options, + projMatrix); } return result; diff --git a/src/mbgl/renderer/tile_pyramid.hpp b/src/mbgl/renderer/tile_pyramid.hpp index bf3ba0d7b8..0cef9e2c40 100644 --- a/src/mbgl/renderer/tile_pyramid.hpp +++ b/src/mbgl/renderer/tile_pyramid.hpp @@ -53,7 +53,8 @@ public: queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, const std::vector&, - const RenderedQueryOptions& options) const; + const RenderedQueryOptions& options, + const mat4& projMatrix) const; std::vector querySourceFeatures(const SourceQueryOptions&) const; diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index 92a226d3c1..cb4b6aa39c 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -217,21 +217,27 @@ void GeometryTile::queryRenderedFeatures( const GeometryCoordinates& queryGeometry, const TransformState& transformState, const std::vector& layers, - const RenderedQueryOptions& options) { + const RenderedQueryOptions& options, + const mat4& projMatrix) { if (!getData()) return; const float queryPadding = getQueryPadding(layers); + mat4 posMatrix; + transformState.matrixFor(posMatrix, id.toUnwrapped()); + matrix::multiply(posMatrix, projMatrix, posMatrix); + latestFeatureIndex->query(result, queryGeometry, - transformState.getAngle(), + transformState, + posMatrix, util::tileSize * id.overscaleFactor(), std::pow(2, transformState.getZoom() - id.overscaledZ), options, id.toUnwrapped(), layers, - queryPadding); + queryPadding * transformState.maxPitchScaleFactor()); } void GeometryTile::querySourceFeatures( diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index a43bf44940..d0490f1009 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -54,7 +54,8 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector& layers, - const RenderedQueryOptions& options) override; + const RenderedQueryOptions& options, + const mat4& projMatrix) override; void querySourceFeatures( std::vector& result, diff --git a/src/mbgl/tile/tile.cpp b/src/mbgl/tile/tile.cpp index 56698e84c4..b95944f10e 100644 --- a/src/mbgl/tile/tile.cpp +++ b/src/mbgl/tile/tile.cpp @@ -37,7 +37,8 @@ void Tile::queryRenderedFeatures( const GeometryCoordinates&, const TransformState&, const std::vector&, - const RenderedQueryOptions&) {} + const RenderedQueryOptions&, + const mat4&) {} float Tile::getQueryPadding(const std::vector&) { return 0; diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp index 5f7a076850..23d6864205 100644 --- a/src/mbgl/tile/tile.hpp +++ b/src/mbgl/tile/tile.hpp @@ -57,7 +57,8 @@ public: const GeometryCoordinates& queryGeometry, const TransformState&, const std::vector&, - const RenderedQueryOptions& options); + const RenderedQueryOptions& options, + const mat4& projMatrix); virtual void querySourceFeatures( std::vector& result, diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp index e6ce245c0e..780fce98f9 100644 --- a/src/mbgl/util/intersection_tests.cpp +++ b/src/mbgl/util/intersection_tests.cpp @@ -82,11 +82,16 @@ bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const Geometry return false; } +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) { + if (polygonContainsPoint(polygon, point)) return true; + if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + return false; +} + bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) { for (auto& ring : rings) { for (auto& point : ring) { - if (polygonContainsPoint(polygon, point)) return true; - if (pointIntersectsBufferedLine(point, polygon, radius)) return true; + if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true; } } return false; diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp index 5bcb29c767..c105fe4dd0 100644 --- a/src/mbgl/util/intersection_tests.hpp +++ b/src/mbgl/util/intersection_tests.hpp @@ -9,6 +9,7 @@ bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const Geome bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius); bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&); bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&); +bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius); } // namespace util } // namespace mbgl -- cgit v1.2.1 From 0ca53ea5a83efcc44abbf1c2a4b3001001e6d14e Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Fri, 30 Mar 2018 16:28:05 -0400 Subject: [core] only index features within tile boundaries Previously we relied on tile buffers for querying features who's rendered representations cross tile boundaries. Now we query multiple tiles making it unnecessary to index features that are completely outside a tile's boundaries. --- src/mbgl/geometry/feature_index.cpp | 12 +++++++++--- src/mbgl/renderer/layers/render_circle_layer.cpp | 2 +- src/mbgl/tile/geometry_tile.cpp | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index e7759b0868..520fd313a2 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -28,8 +28,13 @@ void FeatureIndex::insert(const GeometryCollection& geometries, const std::string& bucketName) { for (const auto& ring : geometries) { auto envelope = mapbox::geometry::envelope(ring); - grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++), - {convertPoint(envelope.min), convertPoint(envelope.max)}); + if (envelope.min.x < util::EXTENT && + envelope.min.y < util::EXTENT && + envelope.max.x >= 0 && + envelope.max.y >= 0) { + grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++), + {convertPoint(envelope.min), convertPoint(envelope.max)}); + } } } @@ -106,7 +111,8 @@ std::unordered_map> FeatureIndex::lookupSymbol }); for (const auto& symbolFeature : sortedFeatures) { - addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, {}); + mat4 unusedMatrix; + addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix); } return result; } diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 6e1c867a30..56fccfe071 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -131,7 +131,7 @@ bool RenderCircleLayer::queryIntersectsFeature( auto stroke = evaluated.evaluate(zoom, feature); auto size = radius + stroke; - // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile + // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile // Otherwise, compare geometry in the plane of the viewport // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index cb4b6aa39c..8efe12d54f 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -205,7 +205,7 @@ float GeometryTile::getQueryPadding(const std::vector& layer float queryPadding = 0; for (const RenderLayer* layer : layers) { auto bucket = getBucket(*layer->baseImpl); - if (bucket) { + if (bucket && bucket->hasData()) { queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer)); } } -- cgit v1.2.1 From 1a39d7db483a26cd846c7875f3ccc9d54d5a0982 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 2 Apr 2018 18:03:13 -0700 Subject: [core] Remove circle-pitch-scale test ignores. --- platform/node/test/ignores.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 195c9161fb..98d7e016bc 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -17,10 +17,6 @@ "expression-tests/is-supported-script/default": "https://github.com/mapbox/mapbox-gl-native/issues/11693", "expression-tests/resolved-locale/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11692", "expression-tests/to-string/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11719", - "query-tests/circle-pitch-scale/viewport-inside-align-map": "https://github.com/mapbox/mapbox-gl-native/issues/10615", - "query-tests/circle-pitch-scale/viewport-inside-align-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/10615", - "query-tests/edge-cases/box-cutting-antimeridian-z0": "https://github.com/mapbox/mapbox-gl-native/issues/11607", - "query-tests/edge-cases/null-island": "https://github.com/mapbox/mapbox-gl-native/issues/11607", "query-tests/geometry/multilinestring": "needs investigation", "query-tests/geometry/multipolygon": "needs investigation", "query-tests/geometry/polygon": "needs investigation", -- cgit v1.2.1 From c067865e7bcb1f8a8eec483a572fa874e5d15111 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 27 Apr 2018 12:25:49 -0400 Subject: [ios, macos] Update minimum deployment versions to iOS 9, macOS 10.11 (#11776) * [ios] Remove iOS 8 conditionals * [ios, macos] Bump pod deployment targets * [ios, macos] Add changelog entries * [ios, macos] Update docs * Remove unnecessary selector-based version checks * Update SQLite note * [ios, macos] Replace lightweight generics macros with direct use Lightweight generics for Foundation collections are available everywhere with the bump to iOS 9 and macOS 10.11. * Update deprecated macOS method --- platform/darwin/src/MGLAttributionInfo.mm | 6 +- platform/darwin/src/MGLAttributionInfo_Private.h | 6 +- platform/darwin/src/MGLCompassDirectionFormatter.m | 4 +- platform/darwin/src/MGLComputedShapeSource.h | 4 +- platform/darwin/src/MGLComputedShapeSource.mm | 6 +- .../darwin/src/MGLComputedShapeSource_Private.h | 2 +- platform/darwin/src/MGLFeature.h | 8 +- platform/darwin/src/MGLFeature.mm | 6 +- platform/darwin/src/MGLFeature_Private.h | 4 +- platform/darwin/src/MGLMapSnapshotter.mm | 2 +- platform/darwin/src/MGLMultiPoint.mm | 2 +- platform/darwin/src/MGLOfflineStorage.h | 2 +- platform/darwin/src/MGLOfflineStorage.mm | 8 +- platform/darwin/src/MGLPolygon.h | 8 +- platform/darwin/src/MGLPolygon.mm | 8 +- platform/darwin/src/MGLPolygon_Private.h | 2 +- platform/darwin/src/MGLPolyline.h | 4 +- platform/darwin/src/MGLPolyline.mm | 8 +- platform/darwin/src/MGLPolyline_Private.h | 2 +- platform/darwin/src/MGLRasterTileSource.h | 2 +- platform/darwin/src/MGLRasterTileSource.mm | 2 +- platform/darwin/src/MGLShapeCollection.h | 4 +- platform/darwin/src/MGLShapeCollection.mm | 4 +- platform/darwin/src/MGLShapeSource.h | 10 +- platform/darwin/src/MGLShapeSource.mm | 12 +-- platform/darwin/src/MGLShapeSource_Private.h | 2 +- platform/darwin/src/MGLStyle.h | 6 +- platform/darwin/src/MGLStyle.mm | 24 ++--- platform/darwin/src/MGLStyle_Private.h | 10 +- platform/darwin/src/MGLTileSource.h | 2 +- platform/darwin/src/MGLTileSource.mm | 6 +- platform/darwin/src/MGLTileSource_Private.h | 4 +- platform/darwin/src/MGLTypes.h | 20 ---- platform/darwin/src/MGLVectorTileSource.h | 4 +- platform/darwin/src/MGLVectorTileSource.mm | 10 +- platform/darwin/src/MGLVectorTileSource_Private.h | 4 +- platform/darwin/src/NSBundle+MGLAdditions.h | 2 +- platform/darwin/src/NSBundle+MGLAdditions.m | 2 +- platform/darwin/src/NSExpression+MGLAdditions.mm | 42 +++----- platform/darwin/src/NSString+MGLAdditions.h | 3 - platform/darwin/src/NSString+MGLAdditions.m | 33 +++--- platform/darwin/test/MGLAttributionInfoTests.m | 6 +- platform/darwin/test/MGLCoordinateFormatterTests.m | 21 +--- platform/darwin/test/MGLExpressionTests.mm | 63 ++++------- platform/darwin/test/MGLFeatureTests.mm | 6 +- platform/darwin/test/MGLNSStringAdditionsTests.m | 54 ++++------ platform/darwin/test/MGLStyleLayerTests.h | 2 +- platform/darwin/test/MGLStyleLayerTests.m | 4 +- platform/default/sqlite3.cpp | 11 +- platform/ios/CHANGELOG.md | 4 + platform/ios/DEVELOPING.md | 2 +- platform/ios/INSTALL.md | 3 +- .../ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-static-part.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- .../ios/app/MBXOfflinePacksTableViewController.m | 6 +- platform/ios/app/MBXViewController.m | 11 +- platform/ios/benchmark/MBXBenchViewController.mm | 4 +- platform/ios/docs/doc-README.md | 2 +- platform/ios/docs/pod-README.md | 4 +- platform/ios/ios.xcodeproj/project.pbxproj | 16 +-- platform/ios/src/MGLAnnotationContainerView.h | 2 +- platform/ios/src/MGLAnnotationContainerView.m | 4 +- .../ios/src/MGLAnnotationContainerView_Private.h | 2 +- platform/ios/src/MGLMapAccessibilityElement.mm | 6 +- platform/ios/src/MGLMapView.h | 38 +++---- platform/ios/src/MGLMapView.mm | 118 ++++++++++----------- platform/ios/src/MGLMapViewDelegate.h | 2 +- platform/ios/src/MGLScaleBar.mm | 7 +- platform/ios/src/MGLUserLocation.m | 2 +- .../ios/test/MGLMapAccessibilityElementTests.m | 6 +- platform/macos/CHANGELOG.md | 6 ++ platform/macos/Mapbox-macOS-SDK-symbols.podspec | 2 +- platform/macos/Mapbox-macOS-SDK.podspec | 2 +- platform/macos/app/AppDelegate.m | 14 +-- platform/macos/app/MGLStyle+MBXAdditions.h | 2 +- platform/macos/app/MGLStyle+MBXAdditions.m | 6 +- platform/macos/app/MapDocument.m | 12 +-- platform/macos/docs/doc-README.md | 2 +- platform/macos/docs/pod-README.md | 4 +- platform/macos/macos.xcodeproj/project.pbxproj | 4 +- platform/macos/src/MGLMapView+IBAdditions.mm | 14 +-- platform/macos/src/MGLMapView.h | 34 +++--- platform/macos/src/MGLMapView.mm | 50 ++++----- 85 files changed, 377 insertions(+), 489 deletions(-) diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm index 07d10e852b..e8d6a203d0 100644 --- a/platform/darwin/src/MGLAttributionInfo.mm +++ b/platform/darwin/src/MGLAttributionInfo.mm @@ -16,7 +16,7 @@ @implementation MGLAttributionInfo -+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { ++ (NSArray *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { if (!htmlString) { return @[]; } @@ -112,7 +112,7 @@ return infos; } -+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos { ++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray *)attributionInfos { NSMutableArray *titles = [NSMutableArray arrayWithCapacity:attributionInfos.count]; for (MGLAttributionInfo *info in attributionInfos) { NSMutableAttributedString *title = info.title.mutableCopy; @@ -259,7 +259,7 @@ } } -- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos { +- (void)growArrayByAddingAttributionInfosFromArray:(NSArray *)infos { for (MGLAttributionInfo *info in infos) { [self growArrayByAddingAttributionInfo:info]; } diff --git a/platform/darwin/src/MGLAttributionInfo_Private.h b/platform/darwin/src/MGLAttributionInfo_Private.h index c639752ac3..85c9ed796f 100644 --- a/platform/darwin/src/MGLAttributionInfo_Private.h +++ b/platform/darwin/src/MGLAttributionInfo_Private.h @@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN @param fontSize The default text size in points. @param linkColor The default link color. */ -+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; ++ (NSArray *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; -+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos; ++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray *)attributionInfos; /** Returns a copy of the `URL` property modified to account for the given style @@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN @param infos An array of info objects to add to the receiver. */ -- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos; +- (void)growArrayByAddingAttributionInfosFromArray:(NSArray *)infos; @end diff --git a/platform/darwin/src/MGLCompassDirectionFormatter.m b/platform/darwin/src/MGLCompassDirectionFormatter.m index 5f0cfae6f7..1ac6a82162 100644 --- a/platform/darwin/src/MGLCompassDirectionFormatter.m +++ b/platform/darwin/src/MGLCompassDirectionFormatter.m @@ -15,8 +15,8 @@ } - (NSString *)stringFromDirection:(CLLocationDirection)direction { - static NS_ARRAY_OF(NSString *) *shortStrings; - static NS_ARRAY_OF(NSString *) *longStrings; + static NSArray *shortStrings; + static NSArray *longStrings; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shortStrings = @[ diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h index 068c49245c..7e0037df89 100644 --- a/platform/darwin/src/MGLComputedShapeSource.h +++ b/platform/darwin/src/MGLComputedShapeSource.h @@ -101,7 +101,7 @@ MGL_EXPORT @param identifier A string that uniquely identifies the source. @param options An `NSDictionary` of options for this source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NSDictionary *)options NS_DESIGNATED_INITIALIZER; /** Returns a custom shape data source initialized with an identifier, data source, and a @@ -120,7 +120,7 @@ MGL_EXPORT @param identifier A string that uniquely identifies the source. @param options An `NSDictionary` of options for this source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id)dataSource options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id)dataSource options:(nullable NSDictionary *)options; /** Invalidates all the features and properties intersecting with or contained in diff --git a/platform/darwin/src/MGLComputedShapeSource.mm b/platform/darwin/src/MGLComputedShapeSource.mm index fb25eb8eb4..04734d0ef5 100644 --- a/platform/darwin/src/MGLComputedShapeSource.mm +++ b/platform/darwin/src/MGLComputedShapeSource.mm @@ -13,7 +13,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates"; const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates"; -mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) { +mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary *options) { mbgl::style::CustomGeometrySource::Options sourceOptions; if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) { @@ -148,7 +148,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi @implementation MGLComputedShapeSource -- (instancetype)initWithIdentifier:(NSString *)identifier options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier options:(NSDictionary *)options { NSOperationQueue *requestQueue = [[NSOperationQueue alloc] init]; requestQueue.name = [NSString stringWithFormat:@"mgl.MGLComputedShapeSource.%@", identifier]; requestQueue.qualityOfService = NSQualityOfServiceUtility; @@ -176,7 +176,7 @@ mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDi return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id)dataSource options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier dataSource:(id)dataSource options:(NSDictionary *)options { if (self = [self initWithIdentifier:identifier options:options]) { [self setDataSource:dataSource]; } diff --git a/platform/darwin/src/MGLComputedShapeSource_Private.h b/platform/darwin/src/MGLComputedShapeSource_Private.h index e1887caf8d..ec075e4bd7 100644 --- a/platform/darwin/src/MGLComputedShapeSource_Private.h +++ b/platform/darwin/src/MGLComputedShapeSource_Private.h @@ -7,6 +7,6 @@ NS_ASSUME_NONNULL_BEGIN MGL_EXPORT -mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options); +mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary *options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLFeature.h b/platform/darwin/src/MGLFeature.h index d0c9e26062..62471abb16 100644 --- a/platform/darwin/src/MGLFeature.h +++ b/platform/darwin/src/MGLFeature.h @@ -148,7 +148,7 @@ NS_ASSUME_NONNULL_BEGIN when the feature instance is used to initialize an `MGLShapeSource` and that source is added to the map and styled. */ -@property (nonatomic, copy) NS_DICTIONARY_OF(NSString *, id) *attributes; +@property (nonatomic, copy) NSDictionary *attributes; /** Returns the feature attribute for the given attribute name. @@ -167,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN `attributes` property, and an `id` key corresponding to the receiver’s `identifier` property. */ -- (NS_DICTIONARY_OF(NSString *, id) *)geoJSONDictionary; +- (NSDictionary *)geoJSONDictionary; @end @@ -252,9 +252,9 @@ MGL_EXPORT MGL_EXPORT @interface MGLShapeCollectionFeature : MGLShapeCollection -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape *) *shapes; +@property (nonatomic, copy, readonly) NSArray *> *shapes; -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes; ++ (instancetype)shapeCollectionWithShapes:(NSArray *> *)shapes; @end diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm index ee2c71be21..02f67dca6e 100644 --- a/platform/darwin/src/MGLFeature.mm +++ b/platform/darwin/src/MGLFeature.mm @@ -233,7 +233,7 @@ MGL_DEFINE_FEATURE_IS_EQUAL(); @dynamic shapes; -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes { ++ (instancetype)shapeCollectionWithShapes:(NSArray *> *)shapes { return [super shapeCollectionWithShapes:shapes]; } @@ -373,7 +373,7 @@ public: } }; -NS_ARRAY_OF(MGLShape *) *MGLFeaturesFromMBGLFeatures(const std::vector &features) { +NSArray *> *MGLFeaturesFromMBGLFeatures(const std::vector &features) { NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()]; for (const auto &feature : features) { [shapes addObject:MGLFeatureFromMBGLFeature(feature)]; @@ -414,7 +414,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at return feature; } -NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) { +NSDictionary *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) { NSMutableDictionary *feature = [@{@"type": @"Feature", @"properties": (attributes) ?: [NSNull null], @"geometry": geometry} mutableCopy]; diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h index 4137200b98..d4074b53ed 100644 --- a/platform/darwin/src/MGLFeature_Private.h +++ b/platform/darwin/src/MGLFeature_Private.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN vector tile features. */ MGL_EXPORT -NS_ARRAY_OF(MGLShape *) *MGLFeaturesFromMBGLFeatures(const std::vector &features); +NSArray *> *MGLFeaturesFromMBGLFeatures(const std::vector &features); /** Returns an `MGLFeature` object converted from the given mbgl::Feature @@ -36,7 +36,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at /** Returns an `NSDictionary` representation of an `MGLFeature`. */ -NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier); +NSDictionary *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 11a5442761..19fa0223a4 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -89,7 +89,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; std::shared_ptr _mbglThreadPool; std::unique_ptr _mbglMapSnapshotter; std::unique_ptr> _snapshotCallback; - NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfo; + NSArray *_attributionInfo; } diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index 240dad9614..5a7262b0a4 100644 --- a/platform/darwin/src/MGLMultiPoint.mm +++ b/platform/darwin/src/MGLMultiPoint.mm @@ -70,7 +70,7 @@ return _coordinates.size(); } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPointCount ++ (NSSet *)keyPathsForValuesAffectingPointCount { return [NSSet setWithObjects:@"coordinates", nil]; } diff --git a/platform/darwin/src/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h index a1f3e686c2..ab36592634 100644 --- a/platform/darwin/src/MGLOfflineStorage.h +++ b/platform/darwin/src/MGLOfflineStorage.h @@ -199,7 +199,7 @@ MGL_EXPORT `packs` property, observe KVO change notifications on the `packs` key path. The initial load results in an `NSKeyValueChangeSetting` change. */ -@property (nonatomic, strong, readonly, nullable) NS_ARRAY_OF(MGLOfflinePack *) *packs; +@property (nonatomic, strong, readonly, nullable) NSArray *packs; /** Creates and registers an offline pack that downloads the resources needed to diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm index 4d999144e8..f4e454534d 100644 --- a/platform/darwin/src/MGLOfflineStorage.mm +++ b/platform/darwin/src/MGLOfflineStorage.mm @@ -32,7 +32,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu @interface MGLOfflineStorage () -@property (nonatomic, strong, readwrite) NS_MUTABLE_ARRAY_OF(MGLOfflinePack *) *packs; +@property (nonatomic, strong, readwrite) NSMutableArray *packs; @property (nonatomic) mbgl::DefaultFileSource *mbglFileSource; @property (nonatomic, getter=isPaused) BOOL paused; @@ -243,7 +243,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu _mbglFileSource = nullptr; } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context { +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Synchronize the file source’s access token with the global one in MGLAccountManager. if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager]) { NSString *accessToken = change[NSKeyValueChangeNewKey]; @@ -336,7 +336,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu } - (void)reloadPacks { - [self getPacksWithCompletionHandler:^(NS_ARRAY_OF(MGLOfflinePack *) *packs, __unused NSError * _Nullable error) { + [self getPacksWithCompletionHandler:^(NSArray *packs, __unused NSError * _Nullable error) { for (MGLOfflinePack *pack in self.packs) { [pack invalidate]; } @@ -344,7 +344,7 @@ const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"Maximu }]; } -- (void)getPacksWithCompletionHandler:(void (^)(NS_ARRAY_OF(MGLOfflinePack *) *packs, NSError * _Nullable error))completion { +- (void)getPacksWithCompletionHandler:(void (^)(NSArray *packs, NSError * _Nullable error))completion { self.mbglFileSource->listOfflineRegions([&, completion](std::exception_ptr exception, mbgl::optional> regions) { NSError *error; if (exception) { diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h index 190b6df9c5..810a8b78ae 100644 --- a/platform/darwin/src/MGLPolygon.h +++ b/platform/darwin/src/MGLPolygon.h @@ -57,7 +57,7 @@ MGL_EXPORT If there are no interior polygons, the value of this property is `nil`. */ -@property (nonatomic, nullable, readonly) NS_ARRAY_OF(MGLPolygon *) *interiorPolygons; +@property (nonatomic, nullable, readonly) NSArray *interiorPolygons; /** Creates and returns an `MGLPolygon` object from the specified set of @@ -82,7 +82,7 @@ MGL_EXPORT is considered to have no interior polygons. @return A new polygon object. */ -+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NS_ARRAY_OF(MGLPolygon *) *)interiorPolygons; ++ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NSArray *)interiorPolygons; @end @@ -109,7 +109,7 @@ MGL_EXPORT /** An array of polygons forming the multipolygon. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolygon *) *polygons; +@property (nonatomic, copy, readonly) NSArray *polygons; /** Creates and returns a multipolygon object consisting of the given polygons. @@ -117,7 +117,7 @@ MGL_EXPORT @param polygons The array of polygons defining the shape. @return A new multipolygon object. */ -+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons; ++ (instancetype)multiPolygonWithPolygons:(NSArray *)polygons; @end diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index 2af768d514..b80504707b 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -102,7 +102,7 @@ @"coordinates": self.mgl_coordinates}; } -- (NS_ARRAY_OF(id) *)mgl_coordinates { +- (NSArray *)mgl_coordinates { NSMutableArray *coordinates = [NSMutableArray array]; NSMutableArray *exteriorRing = [NSMutableArray array]; @@ -128,7 +128,7 @@ @interface MGLMultiPolygon () -@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolygon *) *polygons; +@property (nonatomic, copy, readwrite) NSArray *polygons; @end @@ -138,11 +138,11 @@ @synthesize overlayBounds = _overlayBounds; -+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons { ++ (instancetype)multiPolygonWithPolygons:(NSArray *)polygons { return [[self alloc] initWithPolygons:polygons]; } -- (instancetype)initWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons { +- (instancetype)initWithPolygons:(NSArray *)polygons { if (self = [super init]) { _polygons = polygons; diff --git a/platform/darwin/src/MGLPolygon_Private.h b/platform/darwin/src/MGLPolygon_Private.h index 75afcd61f6..b006f2d77f 100644 --- a/platform/darwin/src/MGLPolygon_Private.h +++ b/platform/darwin/src/MGLPolygon_Private.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLPolygon (Private) -- (NS_ARRAY_OF(id) *)mgl_coordinates; +- (NSArray *)mgl_coordinates; @end diff --git a/platform/darwin/src/MGLPolyline.h b/platform/darwin/src/MGLPolyline.h index b1fca5bf28..8e9007686b 100644 --- a/platform/darwin/src/MGLPolyline.h +++ b/platform/darwin/src/MGLPolyline.h @@ -92,7 +92,7 @@ MGL_EXPORT /** An array of polygons forming the multipolyline. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolyline *) *polylines; +@property (nonatomic, copy, readonly) NSArray *polylines; /** Creates and returns a multipolyline object consisting of the given polylines. @@ -100,7 +100,7 @@ MGL_EXPORT @param polylines The array of polylines defining the shape. @return A new multipolyline object. */ -+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines; ++ (instancetype)multiPolylineWithPolylines:(NSArray *)polylines; @end diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm index 26e3518cd8..a028db8176 100644 --- a/platform/darwin/src/MGLPolyline.mm +++ b/platform/darwin/src/MGLPolyline.mm @@ -49,7 +49,7 @@ @"coordinates": self.mgl_coordinates}; } -- (NS_ARRAY_OF(id) *)mgl_coordinates { +- (NSArray *)mgl_coordinates { NSMutableArray *coordinates = [[NSMutableArray alloc] initWithCapacity:self.pointCount]; for (NSUInteger index = 0; index < self.pointCount; index++) { CLLocationCoordinate2D coordinate = self.coordinates[index]; @@ -123,7 +123,7 @@ @interface MGLMultiPolyline () -@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolyline *) *polylines; +@property (nonatomic, copy, readwrite) NSArray *polylines; @end @@ -133,11 +133,11 @@ @synthesize overlayBounds = _overlayBounds; -+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines { ++ (instancetype)multiPolylineWithPolylines:(NSArray *)polylines { return [[self alloc] initWithPolylines:polylines]; } -- (instancetype)initWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines { +- (instancetype)initWithPolylines:(NSArray *)polylines { if (self = [super init]) { _polylines = polylines; diff --git a/platform/darwin/src/MGLPolyline_Private.h b/platform/darwin/src/MGLPolyline_Private.h index 405a0c5bc9..ff4fabaa78 100644 --- a/platform/darwin/src/MGLPolyline_Private.h +++ b/platform/darwin/src/MGLPolyline_Private.h @@ -4,7 +4,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLPolyline (Private) -- (NS_ARRAY_OF(id) *)mgl_coordinates; +- (NSArray *)mgl_coordinates; @end diff --git a/platform/darwin/src/MGLRasterTileSource.h b/platform/darwin/src/MGLRasterTileSource.h index 59b256d5e5..f27cbc285f 100644 --- a/platform/darwin/src/MGLRasterTileSource.h +++ b/platform/darwin/src/MGLRasterTileSource.h @@ -127,7 +127,7 @@ MGL_EXPORT the default values. @return An initialized tile source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray *)tileURLTemplates options:(nullable NSDictionary *)options NS_DESIGNATED_INITIALIZER; @end diff --git a/platform/darwin/src/MGLRasterTileSource.mm b/platform/darwin/src/MGLRasterTileSource.mm index 02cfef4ae8..61e9ef97fd 100644 --- a/platform/darwin/src/MGLRasterTileSource.mm +++ b/platform/darwin/src/MGLRasterTileSource.mm @@ -44,7 +44,7 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512; uint16_t(round(tileSize))); } -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray *)tileURLTemplates options:(nullable NSDictionary *)options { mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options); uint16_t tileSize = MGLRasterTileSourceRetinaTileSize; diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h index bec482ca61..08f3276496 100644 --- a/platform/darwin/src/MGLShapeCollection.h +++ b/platform/darwin/src/MGLShapeCollection.h @@ -40,7 +40,7 @@ MGL_EXPORT /** An array of shapes forming the shape collection. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape *) *shapes; +@property (nonatomic, copy, readonly) NSArray *shapes; /** Creates and returns a shape collection consisting of the given shapes. @@ -49,7 +49,7 @@ MGL_EXPORT this array is copied to the new object. @return A new shape collection object. */ -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes; ++ (instancetype)shapeCollectionWithShapes:(NSArray *)shapes; @end diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm index 03cab0043f..74e78a764a 100644 --- a/platform/darwin/src/MGLShapeCollection.mm +++ b/platform/darwin/src/MGLShapeCollection.mm @@ -6,11 +6,11 @@ @implementation MGLShapeCollection -+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes { ++ (instancetype)shapeCollectionWithShapes:(NSArray *)shapes { return [[self alloc] initWithShapes:shapes]; } -- (instancetype)initWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes { +- (instancetype)initWithShapes:(NSArray *)shapes { if (self = [super init]) { _shapes = shapes.copy; } diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h index 6fa93476be..1fc00d4de0 100644 --- a/platform/darwin/src/MGLShapeSource.h +++ b/platform/darwin/src/MGLShapeSource.h @@ -154,7 +154,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NSDictionary *)options NS_DESIGNATED_INITIALIZER; /** Returns a shape source with an identifier, a shape, and dictionary of options @@ -183,7 +183,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NSDictionary *)options NS_DESIGNATED_INITIALIZER; /** Returns a shape source with an identifier, an array of features, and a dictionary @@ -210,7 +210,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray *> *)features options:(nullable NSDictionary *)options; /** Returns a shape source with an identifier, an array of shapes, and a dictionary of @@ -238,7 +238,7 @@ MGL_EXPORT @param options An `NSDictionary` of options for this source. @return An initialized shape source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options; +- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray *)shapes options:(nullable NSDictionary *)options; #pragma mark Accessing a Source’s Content @@ -291,7 +291,7 @@ MGL_EXPORT @return An array of objects conforming to the `MGLFeature` protocol that represent features in the source that match the predicate. */ -- (NS_ARRAY_OF(id ) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate; +- (NSArray> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate; @end diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index dcc3fd97f5..31e5867703 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -21,7 +21,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @ const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel"; const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance"; -mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options) { +mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary *options) { auto geoJSONOptions = mbgl::style::GeoJSONOptions(); if (NSNumber *value = options[MGLShapeSourceOptionMinimumZoomLevel]) { @@ -92,7 +92,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL @implementation MGLShapeSource -- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NSDictionary *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { @@ -101,7 +101,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NSDictionary *)options { auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options); auto source = std::make_unique(identifier.UTF8String, geoJSONOptions); if (self = [super initWithPendingSource:std::move(source)]) { @@ -110,7 +110,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return self; } -- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray *> *)features options:(nullable NSDictionary *)options { for (id feature in features) { if (![feature conformsToProtocol:@protocol(MGLFeature)]) { [NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MGLFeature protocol.", feature]; @@ -120,7 +120,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options]; } -- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray *)shapes options:(nullable NSDictionary *)options { MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:shapes]; return [self initWithIdentifier:identifier shape:shapeCollection options:options]; } @@ -153,7 +153,7 @@ mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGL NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape]; } -- (NS_ARRAY_OF(id ) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate { +- (NSArray> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate { mbgl::optional optionalFilter; if (predicate) { diff --git a/platform/darwin/src/MGLShapeSource_Private.h b/platform/darwin/src/MGLShapeSource_Private.h index 0720074d1d..83872afcbc 100644 --- a/platform/darwin/src/MGLShapeSource_Private.h +++ b/platform/darwin/src/MGLShapeSource_Private.h @@ -10,6 +10,6 @@ namespace mbgl { } MGL_EXPORT -mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options); +mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary *options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h index 6df627d7af..814a09ed21 100644 --- a/platform/darwin/src/MGLStyle.h +++ b/platform/darwin/src/MGLStyle.h @@ -241,7 +241,7 @@ MGL_EXPORT /** A set containing the style’s sources. */ -@property (nonatomic, strong) NS_SET_OF(__kindof MGLSource *) *sources; +@property (nonatomic, strong) NSSet<__kindof MGLSource *> *sources; /** Values describing animated transitions to changes on a style's individual @@ -303,7 +303,7 @@ MGL_EXPORT The layers included in the style, arranged according to their back-to-front ordering on the screen. */ -@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *layers; +@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *layers; /** Returns a style layer with the given identifier in the current style. @@ -417,7 +417,7 @@ MGL_EXPORT #pragma mark Managing Style Classes -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); +@property (nonatomic) NSArray *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); - (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed."))); diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm index 867ac6c451..3f9bfbf8ca 100644 --- a/platform/darwin/src/MGLStyle.mm +++ b/platform/darwin/src/MGLStyle.mm @@ -82,8 +82,8 @@ @property (nonatomic, readonly, weak) MGLMapView *mapView; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; @property (readonly, copy, nullable) NSURL *URL; -@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier; +@property (nonatomic, readwrite, strong) NSMutableDictionary *openGLLayers; +@property (nonatomic) NSMutableDictionary *> *localizedLayersByIdentifier; @end @@ -142,9 +142,9 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, #pragma mark Sources -- (NS_SET_OF(__kindof MGLSource *) *)sources { +- (NSSet<__kindof MGLSource *> *)sources { auto rawSources = self.rawStyle->getSources(); - NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()]; + NSMutableSet<__kindof MGLSource *> *sources = [NSMutableSet setWithCapacity:rawSources.size()]; for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) { MGLSource *source = [self sourceFromMBGLSource:*rawSource]; [sources addObject:source]; @@ -152,7 +152,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return sources; } -- (void)setSources:(NS_SET_OF(__kindof MGLSource *) *)sources { +- (void)setSources:(NSSet<__kindof MGLSource *> *)sources { for (MGLSource *source in self.sources) { [self removeSource:source]; } @@ -225,7 +225,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, [source removeFromMapView:self.mapView]; } -- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { +- (nullable NSArray *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { // It’d be incredibly convenient to use -sources here, but this operation // depends on the sources being sorted in ascending order by creation, as // with the std::vector used in mbgl. @@ -245,10 +245,10 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, #pragma mark Style layers -- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers +- (NSArray<__kindof MGLStyleLayer *> *)layers { auto layers = self.rawStyle->getLayers(); - NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()]; + NSMutableArray<__kindof MGLStyleLayer *> *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()]; for (auto layer : layers) { MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer]; [styleLayers addObject:styleLayer]; @@ -256,7 +256,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return styleLayers; } -- (void)setLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers { +- (void)setLayers:(NSArray<__kindof MGLStyleLayer *> *)layers { for (MGLStyleLayer *layer in self.layers) { [self removeLayer:layer]; } @@ -594,13 +594,13 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, } } -- (NS_SET_OF(MGLVectorTileSource *) *)mapboxStreetsSources { +- (NSSet *)mapboxStreetsSources { return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorTileSource * _Nonnull source, BOOL * _Nonnull stop) { return [source isKindOfClass:[MGLVectorTileSource class]] && source.mapboxStreets; }]; } -- (NS_ARRAY_OF(MGLStyleLayer *) *)placeStyleLayers { +- (NSArray *)placeStyleLayers { NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"]; NSSet *placeSourceLayerIdentifiers = [NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label", @"water_label", @"poi_label", @"rail_station_label", @"mountain_peak_label", nil]; @@ -610,7 +610,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles, return [self.layers filteredArrayUsingPredicate:isPlacePredicate]; } -- (NS_ARRAY_OF(MGLStyleLayer *) *)roadStyleLayers { +- (NSArray *)roadStyleLayers { NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"]; NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary * _Nullable bindings) { diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h index 24466b8018..1294b9ad1c 100644 --- a/platform/darwin/src/MGLStyle_Private.h +++ b/platform/darwin/src/MGLStyle_Private.h @@ -24,16 +24,16 @@ namespace mbgl { @property (nonatomic, readonly, weak) MGLMapView *mapView; @property (nonatomic, readonly) mbgl::style::Style *rawStyle; -- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; -@property (nonatomic, readonly, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers; -- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration; +- (nullable NSArray *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; +@property (nonatomic, readonly, strong) NSMutableDictionary *openGLLayers; +- (void)setStyleClasses:(NSArray *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration; @end @interface MGLStyle (MGLStreetsAdditions) -@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *placeStyleLayers; -@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *roadStyleLayers; +@property (nonatomic, readonly, copy) NSArray *placeStyleLayers; +@property (nonatomic, readonly, copy) NSArray *roadStyleLayers; @end diff --git a/platform/darwin/src/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h index 4bf09026f2..667e1e66ca 100644 --- a/platform/darwin/src/MGLTileSource.h +++ b/platform/darwin/src/MGLTileSource.h @@ -212,7 +212,7 @@ MGL_EXPORT configuration URL, this array is also empty until the configuration JSON file is loaded. */ -@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLAttributionInfo *) *attributionInfos; +@property (nonatomic, copy, readonly) NSArray *attributionInfos; @end diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm index 87ac5be9c2..2fafc6fb51 100644 --- a/platform/darwin/src/MGLTileSource.mm +++ b/platform/darwin/src/MGLTileSource.mm @@ -30,11 +30,11 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption return nil; } -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos { +- (NSArray *)attributionInfos { return [self attributionInfosWithFontSize:0 linkColor:nil]; } -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { +- (NSArray *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor { return [MGLAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString fontSize:fontSize linkColor:linkColor]; @@ -48,7 +48,7 @@ const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOption @end -mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options) { +mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray *tileURLTemplates, NSDictionary * _Nullable options) { mbgl::Tileset tileSet; for (NSString *tileURLTemplate in tileURLTemplates) { diff --git a/platform/darwin/src/MGLTileSource_Private.h b/platform/darwin/src/MGLTileSource_Private.h index 0d9876d412..1b260ca86a 100644 --- a/platform/darwin/src/MGLTileSource_Private.h +++ b/platform/darwin/src/MGLTileSource_Private.h @@ -28,11 +28,11 @@ namespace mbgl { @param fontSize The default text size in points, or 0 to use the default. @param linkColor The default link color, or `nil` to use the default. */ -- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; +- (NSArray *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor; @end MGL_EXPORT -mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options); +mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray *tileURLTemplates, NSDictionary * _Nullable options); NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h index 5c32791c2f..fbc3a43ea2 100644 --- a/platform/darwin/src/MGLTypes.h +++ b/platform/darwin/src/MGLTypes.h @@ -111,23 +111,3 @@ NS_INLINE MGLTransition MGLTransitionMake(NSTimeInterval duration, NSTimeInterva } NS_ASSUME_NONNULL_END - -#ifndef NS_ARRAY_OF - // Foundation collection classes adopted lightweight generics in iOS 9.0 and OS X 10.11 SDKs. - #if __has_feature(objc_generics) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100) - /** Inserts a type specifier for a pointer to a lightweight generic with the given collection and object classes. Use a `*` for any non-`id` object classes but no `*` for the collection class. */ - #define NS_ARRAY_OF(ObjectClass...) NSArray - #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray - #define NS_SET_OF(ObjectClass...) NSSet - #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet - #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary - #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary - #else - #define NS_ARRAY_OF(ObjectClass...) NSArray - #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray - #define NS_SET_OF(ObjectClass...) NSSet - #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet - #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary - #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary - #endif -#endif diff --git a/platform/darwin/src/MGLVectorTileSource.h b/platform/darwin/src/MGLVectorTileSource.h index 790c9d4d42..70d2f6e8ec 100644 --- a/platform/darwin/src/MGLVectorTileSource.h +++ b/platform/darwin/src/MGLVectorTileSource.h @@ -99,7 +99,7 @@ MGL_EXPORT the default values. @return An initialized tile source. */ -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray *)tileURLTemplates options:(nullable NSDictionary *)options NS_DESIGNATED_INITIALIZER; #pragma mark Accessing a Source’s Content @@ -138,7 +138,7 @@ MGL_EXPORT @return An array of objects conforming to the `MGLFeature` protocol that represent features loaded by the source that match the predicate. */ -- (NS_ARRAY_OF(id ) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:)); +- (NSArray> *)featuresInSourceLayersWithIdentifiers:(NSSet *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:)); @end diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm index c1f7267e4a..c6715d1759 100644 --- a/platform/darwin/src/MGLVectorTileSource.mm +++ b/platform/darwin/src/MGLVectorTileSource.mm @@ -27,7 +27,7 @@ return self = [super initWithPendingSource:std::move(source)]; } -- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options { +- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray *)tileURLTemplates options:(nullable NSDictionary *)options { mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options); auto source = std::make_unique(identifier.UTF8String, tileSet); return self = [super initWithPendingSource:std::move(source)]; @@ -47,7 +47,7 @@ return attribution ? @(attribution->c_str()) : nil; } -- (NS_ARRAY_OF(id ) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate { +- (NSArray> *)featuresInSourceLayersWithIdentifiers:(NSSet *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate { mbgl::optional> optionalSourceLayerIDs; if (sourceLayerIdentifiers) { @@ -93,9 +93,9 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ @"mul", @"ar", @"de", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans", ]; -+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages { ++ (NSSet *)mapboxStreetsLanguages { static dispatch_once_t onceToken; - static NS_SET_OF(NSString *) *mapboxStreetsLanguages; + static NSSet *mapboxStreetsLanguages; dispatch_once(&onceToken, ^{ mapboxStreetsLanguages = [NSSet setWithArray:MGLMapboxStreetsLanguages]; }); @@ -135,7 +135,7 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; } -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { +- (NSDictionary *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { if (!self.mapboxStreets) { return @{}; } diff --git a/platform/darwin/src/MGLVectorTileSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h index 77521869f1..8042e2cfee 100644 --- a/platform/darwin/src/MGLVectorTileSource_Private.h +++ b/platform/darwin/src/MGLVectorTileSource_Private.h @@ -6,12 +6,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets; -+ (NS_SET_OF(NSString *) *)mapboxStreetsLanguages; ++ (NSSet *)mapboxStreetsLanguages; + (nullable NSString *)preferredMapboxStreetsLanguage; + (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray *)preferencesArray; -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; +- (NSDictionary *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; @end diff --git a/platform/darwin/src/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h index ad5e9d5369..86dc27f22c 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.h +++ b/platform/darwin/src/NSBundle+MGLAdditions.h @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NSString *)mgl_frameworkBundleIdentifier; -+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary; ++ (nullable NSDictionary *)mgl_frameworkInfoDictionary; + (nullable NSString *)mgl_applicationBundleIdentifier; diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m index f55059324e..37f78963d3 100644 --- a/platform/darwin/src/NSBundle+MGLAdditions.m +++ b/platform/darwin/src/NSBundle+MGLAdditions.m @@ -26,7 +26,7 @@ return self.mgl_frameworkInfoDictionary[@"CFBundleIdentifier"]; } -+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary { ++ (nullable NSDictionary *)mgl_frameworkInfoDictionary { NSBundle *bundle = self.mgl_frameworkBundle; return bundle.infoDictionary; } diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index c82a7dc008..a42a2d276e 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -403,7 +403,7 @@ static NSArray * const MGLTokenizedFunctions = @[ If no replacements take place, this method returns the original collection. */ -NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY_OF(NSExpression *) *collection) { +NSArray *MGLCollectionByReplacingTokensWithKeyPaths(NSArray *collection) { __block NSMutableArray *upgradedCollection; [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) { NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths; @@ -424,7 +424,7 @@ NS_ARRAY_OF(NSExpression *) *MGLCollectionByReplacingTokensWithKeyPaths(NS_ARRAY If no replacements take place, this method returns the original stop dictionary. */ -NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokensWithKeyPaths(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops) { +NSDictionary *MGLStopDictionaryByReplacingTokensWithKeyPaths(NSDictionary *stops) { __block NSMutableDictionary *upgradedStops; [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) { if (![value isKindOfClass:[NSExpression class]]) { @@ -665,11 +665,7 @@ NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLStopDictionaryByReplacingTokens } + (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression { - if (@available(iOS 9.0, *)) { - return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression]; - } else { - return [NSExpression expressionForFunction:@"MGL_IF" arguments:@[[NSExpression expressionWithFormat:@"%@", conditionPredicate], trueExpression, falseExpression]]; - } + return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression]; } + (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression *)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression *)stops { @@ -920,12 +916,10 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { [arguments addObject:[NSExpression expressionWithMGLJSONObject:argumentObjects[index]]]; } } - - if (@available(iOS 9.0, *)) { - if (arguments.count == 3) { - NSPredicate *conditional = [arguments.firstObject constantValue]; - return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]]; - } + + if (arguments.count == 3) { + NSPredicate *conditional = [arguments.firstObject constantValue]; + return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]]; } return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments]; } else if ([op isEqualToString:@"match"]) { @@ -1359,7 +1353,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { If no localization takes place, this method returns the original collection. */ -NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) *collection, NSLocale * _Nullable locale) { +NSArray *MGLLocalizedCollection(NSArray *collection, NSLocale * _Nullable locale) { __block NSMutableArray *localizedCollection; [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) { NSExpression *localizedItem = [item mgl_expressionLocalizedIntoLocale:locale]; @@ -1379,7 +1373,7 @@ NS_ARRAY_OF(NSExpression *) *MGLLocalizedCollection(NS_ARRAY_OF(NSExpression *) If no localization takes place, this method returns the original stop dictionary. */ -NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICTIONARY_OF(NSNumber *, NSExpression *) *stops, NSLocale * _Nullable locale) { +NSDictionary *MGLLocalizedStopDictionary(NSDictionary *stops, NSLocale * _Nullable locale) { __block NSMutableDictionary *localizedStops; [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) { if (![value isKindOfClass:[NSExpression class]]) { @@ -1444,16 +1438,14 @@ NS_DICTIONARY_OF(NSNumber *, NSExpression *) *MGLLocalizedStopDictionary(NS_DICT } case NSConditionalExpressionType: { - if (@available(iOS 9.0, *)) { - NSExpression *trueExpression = self.trueExpression; - NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale]; - NSExpression *falseExpression = self.falseExpression; - NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale]; - if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) { - return [NSExpression expressionForConditional:self.predicate - trueExpression:localizedTrueExpression - falseExpression:localizedFalseExpression]; - } + NSExpression *trueExpression = self.trueExpression; + NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale]; + NSExpression *falseExpression = self.falseExpression; + NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale]; + if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) { + return [NSExpression expressionForConditional:self.predicate + trueExpression:localizedTrueExpression + falseExpression:localizedFalseExpression]; } return self; } diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h index 75c593c10b..4888c7a00f 100644 --- a/platform/darwin/src/NSString+MGLAdditions.h +++ b/platform/darwin/src/NSString+MGLAdditions.h @@ -26,9 +26,6 @@ NS_ASSUME_NONNULL_BEGIN Only supports scripts for languages used by Mapbox Streets. - On iOS 8 or older, this will method will always return the untransliterated - receiver. - @param script The four-letter code representing the name of the script, as specified by ISO 15924. */ diff --git a/platform/darwin/src/NSString+MGLAdditions.m b/platform/darwin/src/NSString+MGLAdditions.m index a61f229511..d645490eb3 100644 --- a/platform/darwin/src/NSString+MGLAdditions.m +++ b/platform/darwin/src/NSString+MGLAdditions.m @@ -17,14 +17,9 @@ - (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale { NSMutableString *string = self.mutableCopy; NSOrthography *orthography; -#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability-new" - if ([NSOrthography respondsToSelector:@selector(defaultOrthographyForLanguage:)]) { + if (@available(iOS 11.0, macOS 10.13.0, *)) { orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier]; } -#pragma clang diagnostic pop -#endif [string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:orthography usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) { NSString *word = [string substringWithRange:tokenRange]; if (word.length > 3 @@ -46,22 +41,18 @@ } - (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script { - if (@available(iOS 9.0, *)) { - NSMutableString *string = self.mutableCopy; - NSStringTransform transform; - if ([script isEqualToString:@"Latn"]) { - transform = NSStringTransformToLatin; - } else if ([script isEqualToString:@"Hans"]) { - // No transform available. - } else if ([script isEqualToString:@"Cyrl"]) { - transform = @"Any-Latin; Latin-Cyrillic"; - } else if ([script isEqualToString:@"Arab"]) { - transform = @"Any-Latin; Latin-Arabic"; - } - return transform ? [string stringByApplyingTransform:transform reverse:NO] : string; - } else { - return self; + NSMutableString *string = self.mutableCopy; + NSStringTransform transform; + if ([script isEqualToString:@"Latn"]) { + transform = NSStringTransformToLatin; + } else if ([script isEqualToString:@"Hans"]) { + // No transform available. + } else if ([script isEqualToString:@"Cyrl"]) { + transform = @"Any-Latin; Latin-Cyrillic"; + } else if ([script isEqualToString:@"Arab"]) { + transform = @"Any-Latin; Latin-Arabic"; } + return transform ? [string stringByApplyingTransform:transform reverse:NO] : string; } @end diff --git a/platform/darwin/test/MGLAttributionInfoTests.m b/platform/darwin/test/MGLAttributionInfoTests.m index eccc6ceece..5961b61133 100644 --- a/platform/darwin/test/MGLAttributionInfoTests.m +++ b/platform/darwin/test/MGLAttributionInfoTests.m @@ -17,7 +17,7 @@ @"Improve this map", }; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:0 @@ -67,7 +67,7 @@ CGFloat fontSize = 72; MGLColor *color = [MGLColor redColor]; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:72 @@ -109,7 +109,7 @@ @"Hello World", }; - NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array]; + NSMutableArray *infos = [NSMutableArray array]; for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) { NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i] fontSize:0 diff --git a/platform/darwin/test/MGLCoordinateFormatterTests.m b/platform/darwin/test/MGLCoordinateFormatterTests.m index d693f739ec..ac083fa103 100644 --- a/platform/darwin/test/MGLCoordinateFormatterTests.m +++ b/platform/darwin/test/MGLCoordinateFormatterTests.m @@ -24,12 +24,7 @@ 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"); - 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"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees, 54 minutes, and 48 seconds north by 77 degrees, 1 minute, and 57 seconds west"); shortFormatter.allowsSeconds = NO; mediumFormatter.allowsSeconds = NO; @@ -38,12 +33,7 @@ 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"); - 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"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"38 degrees and 55 minutes north by 77 degrees and 2 minutes west"); shortFormatter.allowsMinutes = NO; mediumFormatter.allowsMinutes = NO; @@ -52,12 +42,7 @@ coordinate = CLLocationCoordinate2DMake(38.9131982, -77.0325453144239); XCTAssertEqualObjects([shortFormatter stringFromCoordinate:coordinate], @"39°N, 77°W"); XCTAssertEqualObjects([mediumFormatter stringFromCoordinate:coordinate], @"39° north, 77° 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"); - } + XCTAssertEqualObjects([longFormatter stringFromCoordinate:coordinate], @"39 degrees north by 77 degrees west"); } @end diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index d54e961b00..6d710fdcfe 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -757,30 +757,26 @@ using namespace std::string_literals; } - (void)testConditionalExpressionObject { - // This test crashes on iOS 8, which 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 expressionWithMGLJSONObject: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); - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)", - MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]), - MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])]; - XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); - } + { + 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 expressionWithMGLJSONObject: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); + expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)", + MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]), + MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])]; + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } { NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@)", @@ -791,9 +787,7 @@ using namespace std::string_literals; NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression); - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"]; XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO); } @@ -875,12 +869,7 @@ using namespace std::string_literals; } { NSExpression *expression; - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"]; - } else { - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)", - MGLConstantExpression([NSPredicate predicateWithFormat:@"key != nil"])]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"]; NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key"], [NSNull null]], @1, @0]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); @@ -890,13 +879,7 @@ using namespace std::string_literals; { NSDictionary *dictionary = @{@"key": @"🔑"}; NSExpression *expression; - if (@available(iOS 9.0, *)) { - expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary]; - } else { - NSPredicate *conditional = [NSPredicate predicateWithFormat:@"%@.key != nil", dictionary]; - expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, 1, 0)", - MGLConstantExpression(conditional)]; - } + expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary]; NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key", @[@"literal", dictionary]], [NSNull null]], @1, @0]; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); // The dictionary isn’t equal enough. diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm index 14b64be854..d135b018f4 100644 --- a/platform/darwin/test/MGLFeatureTests.mm +++ b/platform/darwin/test/MGLFeatureTests.mm @@ -38,7 +38,7 @@ }; features.push_back(mbgl::Feature { polygon }); - NS_ARRAY_OF(MGLShape *) *shapes = MGLFeaturesFromMBGLFeatures(features); + NSArray *> *shapes = MGLFeaturesFromMBGLFeatures(features); XCTAssertEqual(shapes.count, 3, @"All features should be converted into shapes"); MGLPointFeature *pointShape = (MGLPointFeature *)shapes[0]; @@ -69,7 +69,7 @@ [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 4)]); XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[3]], [NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 1)]); - NS_ARRAY_OF(MGLPolygon *) *interiorPolygons = polygonShape.interiorPolygons; + NSArray *interiorPolygons = polygonShape.interiorPolygons; XCTAssertEqual(interiorPolygons.count, 1); MGLPolygon *interiorPolygon = interiorPolygons.firstObject; XCTAssertEqual(interiorPolygon.pointCount, 4); @@ -103,7 +103,7 @@ vector.push_back(true); features.push_back(pointFeature); - NS_ARRAY_OF(MGLShape *) *shapes = MGLFeaturesFromMBGLFeatures(features); + NSArray *> *shapes = MGLFeaturesFromMBGLFeatures(features); XCTAssertEqual(shapes.count, 1, @"All features should be converted into shapes"); MGLShape *shape = shapes.firstObject; diff --git a/platform/darwin/test/MGLNSStringAdditionsTests.m b/platform/darwin/test/MGLNSStringAdditionsTests.m index 2a8715d991..a3ee7e3433 100644 --- a/platform/darwin/test/MGLNSStringAdditionsTests.m +++ b/platform/darwin/test/MGLNSStringAdditionsTests.m @@ -40,37 +40,29 @@ } - (void)testTransliteratedString { - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند"); - XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland"); - - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ"); - XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京"); - - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ"); - XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква"); - - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن"); - XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン"); - } else { - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Arab"], @"Made-up Place"); - XCTAssertEqualObjects([@"Made-up Place" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Made-up Place"); - } + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Portland"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Portland"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Портланд"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Arab"], @"پُرتلَند"); + XCTAssertEqualObjects([@"Portland" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Portland"); + + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Latn"], @"běi jīng"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Hans"], @"北京"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"бе̌и йӣнг"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Arab"], @"بِِ̌ جِينگ"); + XCTAssertEqualObjects([@"北京" mgl_stringByTransliteratingIntoScript:@"Fake"], @"北京"); + + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Latn"], @"Moskva"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Hans"], @"Mосква"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"Москва"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Arab"], @"مُسكڤَ"); + XCTAssertEqualObjects([@"Mосква" mgl_stringByTransliteratingIntoScript:@"Fake"], @"Mосква"); + + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Latn"], @"rondon"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Hans"], @"ロンドン"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Cyrl"], @"рондон"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Arab"], @"رُندُن"); + XCTAssertEqualObjects([@"ロンドン" mgl_stringByTransliteratingIntoScript:@"Fake"], @"ロンドン"); } @end diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h index 28c316c8ba..c7577819b8 100644 --- a/platform/darwin/test/MGLStyleLayerTests.h +++ b/platform/darwin/test/MGLStyleLayerTests.h @@ -14,7 +14,7 @@ @interface NSString (MGLStyleLayerTestAdditions) -@property (nonatomic, readonly, copy) NS_ARRAY_OF(NSString *) *lexicalClasses; +@property (nonatomic, readonly, copy) NSArray *lexicalClasses; @property (nonatomic, readonly, copy) NSString *lemma; @end diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m index b51fa02af4..52b36dba00 100644 --- a/platform/darwin/test/MGLStyleLayerTests.m +++ b/platform/darwin/test/MGLStyleLayerTests.m @@ -33,7 +33,7 @@ } - (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean { - NS_MUTABLE_ARRAY_OF(NSString *) *components = [name componentsSeparatedByString:@"-"].mutableCopy; + NSMutableArray *components = [name componentsSeparatedByString:@"-"].mutableCopy; if (isBoolean) { if ([components.firstObject isEqualToString:@"is"]) { [components removeObjectAtIndex:0]; @@ -67,7 +67,7 @@ @implementation NSString (MGLStyleLayerTestAdditions) -- (NS_ARRAY_OF(NSString *) *)lexicalClasses { +- (NSArray *)lexicalClasses { NSOrthography *orthography = [NSOrthography orthographyWithDominantScript:@"Latn" languageMap:@{@"Latn": @[@"en"]}]; NSLinguisticTaggerOptions options = (NSLinguisticTaggerOmitPunctuation diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 8a567d602e..8f9c34191f 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -291,11 +291,12 @@ template <> void Query::bind(int offset, const char *value) { } // We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they -// was introduced in SQLite 3.8.7, and we need to support earlier versions: -// iOS 8.0: 3.7.13 -// iOS 8.2: 3.8.5 -// According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide, -// the first iOS version with 3.8.7+ was 9.0, with 3.8.8. +// were introduced in SQLite 3.8.7, and we need to support earlier versions: +// Android 11: 3.7 +// Android 21: 3.8 +// Android 24: 3.9 +// Per https://developer.android.com/reference/android/database/sqlite/package-summary. +// The first iOS version with 3.8.7+ was 9.0, with 3.8.8. void Query::bind(int offset, const char * value, std::size_t length, bool retain) { assert(stmt.impl); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 1775d628f6..5c3686d7a8 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -4,6 +4,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## master +### Packaging + +* The minimum deployment target for this SDK is now iOS 9.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) + ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md index 7a97074d38..b407e78e4b 100644 --- a/platform/ios/DEVELOPING.md +++ b/platform/ios/DEVELOPING.md @@ -4,7 +4,7 @@ This document explains how to build the Mapbox Maps SDK for iOS from source. It ## Requirements -The Mapbox Maps SDK for iOS and iosapp demo application require iOS 8.0 or above. _Note: Support for iOS 8 will be removed in a future release and the minimum iOS deployment version will increase to iOS 9.0._ +The Mapbox Maps SDK for iOS and iosapp demo application require iOS 9.0 or above. The Mapbox Maps SDK for iOS requires Xcode 9.1 or above to compile from source. diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md index 01f4621b1e..f253d3019d 100644 --- a/platform/ios/INSTALL.md +++ b/platform/ios/INSTALL.md @@ -4,13 +4,12 @@ This document explains how to build a development version of Mapbox Maps SDK for ### Requirements -The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices: +The Mapbox Maps SDK for iOS is intended to run on iOS 9.0 and above on the following devices: * iPhone 4s and above (5, 5c, 5s, 6, 6 Plus, 7, 7 Plus, 8, 8 Plus, X) * iPad 2 and above (3, 4, Mini, Air, Mini 2, Air 2, Pro) * iPod touch 5th generation and above -_Note: Support for iOS 8 will be removed in a future release and the minimum iOS deployment version will increase to iOS 9.0._ Note that 32-bit simulators (such as the iPhone 5 or iPad 2) are not supported. diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 7ea1b993ef..e07178311b 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK-static-part.podspec b/platform/ios/Mapbox-iOS-SDK-static-part.podspec index b2a114f9d2..bd98bb7272 100644 --- a/platform/ios/Mapbox-iOS-SDK-static-part.podspec +++ b/platform/ios/Mapbox-iOS-SDK-static-part.podspec @@ -1,4 +1,4 @@ - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 47a021598f..fbf65f0b24 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 73412890c8..5a8e913dd9 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :ios - m.ios.deployment_target = '8.0' + m.ios.deployment_target = '9.0' m.requires_arc = true diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m index 26a15a0b95..959ae57548 100644 --- a/platform/ios/app/MBXOfflinePacksTableViewController.m +++ b/platform/ios/app/MBXOfflinePacksTableViewController.m @@ -45,7 +45,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context { +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"packs"]) { NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue]; NSIndexSet *indices = change[NSKeyValueChangeIndexesKey]; @@ -119,9 +119,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac }]; }]; [alertController addAction:downloadAction]; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) { - alertController.preferredAction = downloadAction; - } + alertController.preferredAction = downloadAction; [self presentViewController:alertController animated:YES completion:nil]; } diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 391af5ea05..80163c7d40 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -175,9 +175,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]; self.mapView.showsScale = YES; self.mapView.showsUserHeadingIndicator = YES; - if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) { - self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular]; - } + self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular]; if ([MGLAccountManager accessToken].length) { @@ -207,11 +205,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView reloadStyle:self]; }]; [alertController addAction:OKAction]; + alertController.preferredAction = OKAction; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) - { - alertController.preferredAction = OKAction; - } [self presentViewController:alertController animated:YES completion:nil]; } } @@ -1532,7 +1527,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self continueWorldTourWithRemainingAnnotations:annotations]; } -- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations +- (void)continueWorldTourWithRemainingAnnotations:(NSMutableArray *)annotations { MGLPointAnnotation *nextAnnotation = annotations.firstObject; if (!nextAnnotation || !_isTouringWorld) diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm index d4629e2521..84c2790d50 100644 --- a/platform/ios/benchmark/MBXBenchViewController.mm +++ b/platform/ios/benchmark/MBXBenchViewController.mm @@ -76,10 +76,8 @@ [self startBenchmarkIteration]; }]; [alertController addAction:OKAction]; + alertController.preferredAction = OKAction; - if ([alertController respondsToSelector:@selector(setPreferredAction:)]) { - alertController.preferredAction = OKAction; - } [self presentViewController:alertController animated:YES completion:nil]; } } diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md index 7cd0376d07..3a95aa96de 100644 --- a/platform/ios/docs/doc-README.md +++ b/platform/ios/docs/doc-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) -The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. ![Mapbox Maps SDK for iOS screenshots](img/screenshot.png) diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md index f94073bd9f..8a3080055d 100644 --- a/platform/ios/docs/pod-README.md +++ b/platform/ios/docs/pod-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/) -The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. For more information, check out the [Mapbox Maps SDK for iOS homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online. @@ -10,7 +10,7 @@ For more information, check out the [Mapbox Maps SDK for iOS homepage](https://w The Mapbox Maps SDK for iOS may be installed as either a dynamic framework or a static framework. (To reduce the download size, the static framework is omitted from some distributions; you may need to download the full package from the [release page](https://github.com/mapbox/mapbox-gl-native/releases/).) -Integrating the Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page. +Integrating the Mapbox Maps SDK for iOS requires Xcode 8.0 or higher. {{DYNAMIC}} diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index a9413accf2..cb5a5587a2 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -3371,7 +3371,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Tests/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3397,7 +3396,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Tests/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3423,7 +3421,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Test Harness/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3448,7 +3445,6 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "Integration Test Harness/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.Integration-Test-Harness"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -3506,7 +3502,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -3559,7 +3555,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -3575,7 +3571,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; @@ -3589,7 +3584,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL; PRODUCT_NAME = "Mapbox GL"; @@ -3624,7 +3618,6 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3649,7 +3642,6 @@ CLANG_ENABLE_MODULES = YES; HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = test/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3684,7 +3676,6 @@ ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3727,7 +3718,6 @@ ); INFOPLIST_FILE = framework/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_CFLAGS = "-fvisibility=hidden"; OTHER_CPLUSPLUSFLAGS = ( @@ -3841,7 +3831,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench; PRODUCT_NAME = "Bench GL"; @@ -3853,7 +3842,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench; PRODUCT_NAME = "Bench GL"; diff --git a/platform/ios/src/MGLAnnotationContainerView.h b/platform/ios/src/MGLAnnotationContainerView.h index 90d2964831..ccec3052a6 100644 --- a/platform/ios/src/MGLAnnotationContainerView.h +++ b/platform/ios/src/MGLAnnotationContainerView.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)annotationContainerViewWithAnnotationContainerView:(MGLAnnotationContainerView *)annotationContainerView; -- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews; +- (void)addSubviews:(NSArray *)subviews; @end diff --git a/platform/ios/src/MGLAnnotationContainerView.m b/platform/ios/src/MGLAnnotationContainerView.m index 9a823c839c..6c82a1836d 100644 --- a/platform/ios/src/MGLAnnotationContainerView.m +++ b/platform/ios/src/MGLAnnotationContainerView.m @@ -3,7 +3,7 @@ @interface MGLAnnotationContainerView () -@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews; +@property (nonatomic) NSMutableArray *annotationViews; @end @@ -26,7 +26,7 @@ return newAnnotationContainerView; } -- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews +- (void)addSubviews:(NSArray *)subviews { for (MGLAnnotationView *view in subviews) { diff --git a/platform/ios/src/MGLAnnotationContainerView_Private.h b/platform/ios/src/MGLAnnotationContainerView_Private.h index 007b03550b..9dce54842d 100644 --- a/platform/ios/src/MGLAnnotationContainerView_Private.h +++ b/platform/ios/src/MGLAnnotationContainerView_Private.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MGLAnnotationContainerView (Private) -@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews; +@property (nonatomic) NSMutableArray *annotationViews; @end diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm index 8bce38a145..c1cc5304d7 100644 --- a/platform/ios/src/MGLMapAccessibilityElement.mm +++ b/platform/ios/src/MGLMapAccessibilityElement.mm @@ -56,10 +56,8 @@ // may be in the local language, which may be written in another script. // Attempt to transform to the script of the preferred language, keeping // the original string if no transform exists or if transformation fails. - if (@available(iOS 9.0, *)) { - NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode]; - name = [name mgl_stringByTransliteratingIntoScript:dominantScript]; - } + NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode]; + name = [name mgl_stringByTransliteratingIntoScript:dominantScript]; self.accessibilityLabel = name; } diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h index 765f0f932b..bde8e6a71d 100644 --- a/platform/ios/src/MGLMapView.h +++ b/platform/ios/src/MGLMapView.h @@ -189,7 +189,7 @@ MGL_EXPORT IB_DESIGNABLE */ @property (nonatomic, readonly, nullable) MGLStyle *style; -@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style."))); +@property (nonatomic, readonly) NSArray *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style."))); /** URL of the style currently displayed in the receiver. @@ -286,7 +286,7 @@ MGL_EXPORT IB_DESIGNABLE */ - (IBAction)showAttribution:(id)sender; -@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); +@property (nonatomic) NSArray *styleClasses __attribute__((unavailable("Support for style classes has been removed."))); - (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed."))); @@ -773,7 +773,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations animated:(BOOL)animated; +- (void)showAnnotations:(NSArray> *)annotations animated:(BOOL)animated; /** Sets the visible region so that the map displays the specified annotations with @@ -788,7 +788,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; +- (void)showAnnotations:(NSArray> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated; /** A camera representing the current viewpoint of the map. @@ -1076,7 +1076,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *annotations; +@property (nonatomic, readonly, nullable) NSArray> *annotations; /** Adds an annotation to the map view. @@ -1104,7 +1104,7 @@ MGL_EXPORT IB_DESIGNABLE must conform to the `MGLAnnotation` protocol. The map view retains each individual annotation object. */ -- (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations; +- (void)addAnnotations:(NSArray> *)annotations; /** Removes an annotation from the map view, deselecting it if it is selected. @@ -1129,7 +1129,7 @@ MGL_EXPORT IB_DESIGNABLE @param annotations The array of annotation objects to remove. Objects in the array must conform to the `MGLAnnotation` protocol. */ -- (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations; +- (void)removeAnnotations:(NSArray> *)annotations; /** Returns an `MGLAnnotationView` if the given annotation is currently associated @@ -1180,7 +1180,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view or if no annotations associated with the map view are currently visible, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *visibleAnnotations; +@property (nonatomic, readonly, nullable) NSArray> *visibleAnnotations; /** Returns the list of annotations associated with the receiver that intersect with @@ -1191,7 +1191,7 @@ MGL_EXPORT IB_DESIGNABLE no annotations associated with the map view are currently visible in the rectangle. */ -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotationsInRect:(CGRect)rect; +- (nullable NSArray> *)visibleAnnotationsInRect:(CGRect)rect; #pragma mark Managing Annotation Selections @@ -1208,7 +1208,7 @@ MGL_EXPORT IB_DESIGNABLE @note In versions prior to `4.0.0` if the annotation was offscreen it was not selected. */ -@property (nonatomic, copy) NS_ARRAY_OF(id ) *selectedAnnotations; +@property (nonatomic, copy) NSArray> *selectedAnnotations; /** Selects an annotation and displays its callout view. @@ -1246,7 +1246,7 @@ MGL_EXPORT IB_DESIGNABLE overlays are associated with the map view, the value of this property is empty array. */ -@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id ) *overlays; +@property (nonatomic, readonly, nonnull) NSArray> *overlays; /** Adds a single overlay object to the map. @@ -1265,7 +1265,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which must conform to the `MGLOverlay` protocol. */ -- (void)addOverlays:(NS_ARRAY_OF(id ) *)overlays; +- (void)addOverlays:(NSArray> *)overlays; /** Removes a single overlay object from the map. @@ -1285,7 +1285,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which conforms to the `MGLOverlay` protocol. */ -- (void)removeOverlays:(NS_ARRAY_OF(id ) *)overlays; +- (void)removeOverlays:(NSArray> *)overlays; #pragma mark Accessing the Underlying Map Data @@ -1301,7 +1301,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); /** Returns an array of rendered map features that intersect with a given point, @@ -1320,7 +1320,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with a given point, @@ -1387,7 +1387,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); /** Returns an array of rendered map features that intersect with the given @@ -1402,7 +1402,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); /** Returns an array of rendered map features that intersect with the given @@ -1421,7 +1421,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:)); +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with the given @@ -1493,7 +1493,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); #pragma mark Debugging the Map diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index b7d0974872..978547b9c6 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -194,14 +194,14 @@ public: @property (nonatomic) GLKView *glView; @property (nonatomic) UIImageView *glSnapshotView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints; +@property (nonatomic) NSMutableArray *scaleBarConstraints; @property (nonatomic, readwrite) MGLScaleBar *scaleBar; @property (nonatomic, readwrite) UIImageView *compassView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints; +@property (nonatomic) NSMutableArray *compassViewConstraints; @property (nonatomic, readwrite) UIImageView *logoView; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints; +@property (nonatomic) NSMutableArray *logoViewConstraints; @property (nonatomic, readwrite) UIButton *attributionButton; -@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints; +@property (nonatomic) NSMutableArray *attributionButtonConstraints; @property (nonatomic, readwrite) MGLStyle *style; @@ -217,7 +217,7 @@ public: @property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask; /// Mapping from reusable identifiers to annotation images. -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; +@property (nonatomic) NSMutableDictionary *annotationImagesByIdentifier; /// Currently shown popover representing the selected annotation. @property (nonatomic) UIView *calloutViewForSelectedAnnotation; @@ -235,7 +235,7 @@ public: @property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement; @property (nonatomic) MGLAnnotationContainerView *annotationContainerView; @property (nonatomic) MGLUserLocation *userLocation; -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *annotationViewReuseQueueByIdentifier; +@property (nonatomic) NSMutableDictionary *> *annotationViewReuseQueueByIdentifier; @end @@ -287,9 +287,9 @@ public: BOOL _delegateHasLineWidthsForShapeAnnotations; MGLCompassDirectionFormatter *_accessibilityCompassFormatter; - NS_ARRAY_OF(id ) *_visiblePlaceFeatures; - NS_ARRAY_OF(id ) *_visibleRoadFeatures; - NS_MUTABLE_SET_OF(MGLFeatureAccessibilityElement *) *_featureAccessibilityElements; + NSArray> *_visiblePlaceFeatures; + NSArray> *_visibleRoadFeatures; + NSMutableSet *_featureAccessibilityElements; BOOL _accessibilityValueAnnouncementIsPending; MGLReachability *_reachability; @@ -335,12 +335,12 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle ++ (NSSet *)keyPathsForValuesAffectingStyle { return [NSSet setWithObject:@"styleURL"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL ++ (NSSet *)keyPathsForValuesAffectingStyleURL { return [NSSet setWithObjects:@"styleURL__", nil]; } @@ -624,18 +624,8 @@ public: UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale); [scaleImage drawInRect:{ CGPointZero, scaleImage.size }]; - CGFloat northSize = 11; - UIFont *northFont; - if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) - { - northFont = [UIFont systemFontOfSize:northSize weight:UIFontWeightUltraLight]; - } - else - { - northFont = [UIFont systemFontOfSize:northSize]; - } NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{ - NSFontAttributeName: northFont, + NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight], NSForegroundColorAttributeName: [UIColor whiteColor], }]; CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2, @@ -1273,7 +1263,7 @@ public: } } -- (void)touchesBegan:(__unused NS_SET_OF(UITouch *) *)touches withEvent:(__unused UIEvent *)event +- (void)touchesBegan:(__unused NSSet *)touches withEvent:(__unused UIEvent *)event { _changeDelimiterSuppressionDepth = 0; _mbglMap->setGestureInProgress(false); @@ -2238,22 +2228,22 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled ++ (NSSet *)keyPathsForValuesAffectingZoomEnabled { return [NSSet setWithObject:@"allowsZooming"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingScrollEnabled ++ (NSSet *)keyPathsForValuesAffectingScrollEnabled { return [NSSet setWithObject:@"allowsScrolling"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingRotateEnabled ++ (NSSet *)keyPathsForValuesAffectingRotateEnabled { return [NSSet setWithObject:@"allowsRotating"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitchEnabled ++ (NSSet *)keyPathsForValuesAffectingPitchEnabled { return [NSSet setWithObject:@"allowsTilting"]; } @@ -2419,7 +2409,7 @@ public: return value; } -- (NS_ARRAY_OF(id ) *)visiblePlaceFeatures +- (NSArray> *)visiblePlaceFeatures { if (!_visiblePlaceFeatures) { @@ -2429,7 +2419,7 @@ public: return _visiblePlaceFeatures; } -- (NS_ARRAY_OF(id ) *)visibleRoadFeatures +- (NSArray> *)visibleRoadFeatures { if (!_visibleRoadFeatures) { @@ -2907,7 +2897,7 @@ public: #pragma mark - Geography - -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate ++ (NSSet *)keyPathsForValuesAffectingCenterCoordinate { return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil]; } @@ -2998,7 +2988,7 @@ public: _mbglMap->easeTo(cameraOptions, animationOptions); } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel ++ (NSSet *)keyPathsForValuesAffectingZoomLevel { return [NSSet setWithObject:@"camera"]; } @@ -3167,7 +3157,7 @@ public: [self didChangeValueForKey:@"visibleCoordinateBounds"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection ++ (NSSet *)keyPathsForValuesAffectingDirection { return [NSSet setWithObject:@"camera"]; } @@ -3217,12 +3207,12 @@ public: [self setDirection:direction animated:NO]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitch ++ (NSSet *)keyPathsForValuesAffectingPitch { return [NSSet setWithObject:@"camera"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera ++ (NSSet *)keyPathsForValuesAffectingCamera { return [NSSet setWithObjects:@"longitude", @"latitude", @"centerCoordinate", @"zoomLevel", @"direction", nil]; } @@ -3496,7 +3486,7 @@ public: #pragma mark - Annotations - -- (nullable NS_ARRAY_OF(id ) *)annotations +- (nullable NSArray> *)annotations { if (_annotationContextsByAnnotationTag.empty()) { @@ -3520,12 +3510,12 @@ public: return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()]; } -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotations +- (nullable NSArray> *)visibleAnnotations { return [self visibleAnnotationsInRect:self.bounds]; } -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotationsInRect:(CGRect)rect +- (nullable NSArray> *)visibleAnnotationsInRect:(CGRect)rect { if (_annotationContextsByAnnotationTag.empty()) { @@ -3599,7 +3589,7 @@ public: [self addAnnotations:@[ annotation ]]; } -- (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations +- (void)addAnnotations:(NSArray> *)annotations { if ( ! annotations) return; [self willChangeValueForKey:@"annotations"]; @@ -3740,7 +3730,7 @@ public: UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); } -- (void)updateAnnotationContainerViewWithAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews +- (void)updateAnnotationContainerViewWithAnnotationViews:(NSArray *)annotationViews { if (annotationViews.count == 0) return; @@ -3895,7 +3885,7 @@ public: [self removeAnnotations:@[ annotation ]]; } -- (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations +- (void)removeAnnotations:(NSArray> *)annotations { if ( ! annotations) return; @@ -3956,11 +3946,11 @@ public: } } -- (nonnull NS_ARRAY_OF(id ) *)overlays +- (nonnull NSArray> *)overlays { if (self.annotations == nil) { return @[]; } - NS_MUTABLE_ARRAY_OF(id ) *mutableOverlays = [NSMutableArray array]; + NSMutableArray> *mutableOverlays = [NSMutableArray array]; [self.annotations enumerateObjectsUsingBlock:^(id _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) { if ([annotation conformsToProtocol:@protocol(MGLOverlay)]) @@ -3977,7 +3967,7 @@ public: [self addOverlays:@[ overlay ]]; } -- (void)addOverlays:(NS_ARRAY_OF(id ) *)overlays +- (void)addOverlays:(NSArray> *)overlays { #if DEBUG for (id overlay in overlays) @@ -3994,7 +3984,7 @@ public: [self removeOverlays:@[ overlay ]]; } -- (void)removeOverlays:(NS_ARRAY_OF(id ) *)overlays +- (void)removeOverlays:(NSArray> *)overlays { #if DEBUG for (id overlay in overlays) @@ -4226,13 +4216,13 @@ public: [self didChangeValueForKey:@"selectedAnnotations"]; } -- (NS_ARRAY_OF(id ) *)selectedAnnotations +- (NSArray> *)selectedAnnotations { id selectedAnnotation = self.selectedAnnotation; return (selectedAnnotation ? @[ selectedAnnotation ] : @[]); } -- (void)setSelectedAnnotations:(NS_ARRAY_OF(id ) *)selectedAnnotations +- (void)setSelectedAnnotations:(NSArray> *)selectedAnnotations { if ( ! selectedAnnotations.count) return; @@ -4578,7 +4568,7 @@ public: completion:NULL]; } -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations animated:(BOOL)animated +- (void)showAnnotations:(NSArray> *)annotations animated:(BOOL)animated { CGFloat maximumPadding = 100; CGFloat yPadding = (self.frame.size.height / 5 <= maximumPadding) ? (self.frame.size.height / 5) : maximumPadding; @@ -4589,7 +4579,7 @@ public: [self showAnnotations:annotations edgePadding:edgeInsets animated:animated]; } -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated +- (void)showAnnotations:(NSArray> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated { if ( ! annotations || ! annotations.count) return; @@ -4777,7 +4767,7 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingUserLocation ++ (NSSet *)keyPathsForValuesAffectingUserLocation { return [NSSet setWithObject:@"userLocationAnnotationView"]; } @@ -5268,16 +5258,16 @@ public: #pragma mark Data -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { mbgl::ScreenCoordinate screenCoordinate = { point.x, point.y }; @@ -5302,15 +5292,15 @@ public: return MGLFeaturesFromMBGLFeatures(features); } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect { +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { mbgl::ScreenBox screenBox = { { CGRectGetMinX(rect), CGRectGetMinY(rect) }, { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, @@ -6040,7 +6030,7 @@ public: views:views]]; } -- (NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *)annotationViewReuseQueueForIdentifier:(NSString *)identifier { +- (NSMutableArray *)annotationViewReuseQueueForIdentifier:(NSString *)identifier { if (!_annotationViewReuseQueueByIdentifier[identifier]) { _annotationViewReuseQueueByIdentifier[identifier] = [NSMutableArray array]; @@ -6197,7 +6187,7 @@ private: @implementation MGLMapView (IBAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ ++ (NSSet *)keyPathsForValuesAffectingStyleURL__ { return [NSSet setWithObject:@"styleURL"]; } @@ -6220,7 +6210,7 @@ private: self.styleURL = url; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude ++ (NSSet *)keyPathsForValuesAffectingLatitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -6246,7 +6236,7 @@ private: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude ++ (NSSet *)keyPathsForValuesAffectingLongitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -6272,7 +6262,7 @@ private: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming ++ (NSSet *)keyPathsForValuesAffectingAllowsZooming { return [NSSet setWithObject:@"zoomEnabled"]; } @@ -6287,7 +6277,7 @@ private: self.zoomEnabled = allowsZooming; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling ++ (NSSet *)keyPathsForValuesAffectingAllowsScrolling { return [NSSet setWithObject:@"scrollEnabled"]; } @@ -6302,7 +6292,7 @@ private: self.scrollEnabled = allowsScrolling; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating ++ (NSSet *)keyPathsForValuesAffectingAllowsRotating { return [NSSet setWithObject:@"rotateEnabled"]; } @@ -6317,7 +6307,7 @@ private: self.rotateEnabled = allowsRotating; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting ++ (NSSet *)keyPathsForValuesAffectingAllowsTilting { return [NSSet setWithObject:@"pitchEnabled"]; } @@ -6332,7 +6322,7 @@ private: self.pitchEnabled = allowsTilting; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading ++ (NSSet *)keyPathsForValuesAffectingShowsHeading { return [NSSet setWithObject:@"showsUserHeadingIndicator"]; } diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h index 0368d8413c..201e3db84b 100644 --- a/platform/ios/src/MGLMapViewDelegate.h +++ b/platform/ios/src/MGLMapViewDelegate.h @@ -428,7 +428,7 @@ NS_ASSUME_NONNULL_BEGIN @param annotationViews An array of `MGLAnnotationView` objects representing the views that were added. */ -- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews; +- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray *)annotationViews; #pragma mark Selecting Annotations diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm index 139dffdfab..a2fc24c75c 100644 --- a/platform/ios/src/MGLScaleBar.mm +++ b/platform/ios/src/MGLScaleBar.mm @@ -178,12 +178,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084; #pragma mark - Convenience methods - (BOOL)usesRightToLeftLayout { - // semanticContentAttribute is iOS 9+ - if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) { - return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; - } else { - return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; - } + return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft; } - (BOOL)usesMetricSystem { diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m index 074d138a72..245cbf4371 100644 --- a/platform/ios/src/MGLUserLocation.m +++ b/platform/ios/src/MGLUserLocation.m @@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_END return ! [key isEqualToString:@"location"] && ! [key isEqualToString:@"heading"]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCoordinate ++ (NSSet *)keyPathsForValuesAffectingCoordinate { return [NSSet setWithObject:@"location"]; } diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m index 89a595421e..916461e708 100644 --- a/platform/ios/test/MGLMapAccessibilityElementTests.m +++ b/platform/ios/test/MGLMapAccessibilityElementTests.m @@ -25,11 +25,7 @@ @"name_en": @"Цинциннати", }; element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature]; - if (@available(iOS 9.0, *)) { - XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized."); - } else { - XCTAssertEqualObjects(element.accessibilityLabel, @"Цинциннати", @"Accessibility label should not be romanized."); - } + XCTAssertEqualObjects(element.accessibilityLabel, @"Cincinnati", @"Accessibility label should be romanized."); } - (void)testPlaceFeatureValues { diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 498ed3c379..00d242211c 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog for Mapbox Maps SDK for macOS +## master + +### Packaging + +* The minimum deployment target for this SDK is now macOS 10.11.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) + ## 0.7.1 ### Style layers diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec index 597f97d03c..2df93c6f42 100644 --- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec +++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :osx - m.osx.deployment_target = '10.10' + m.osx.deployment_target = '10.11' m.requires_arc = true diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec index 734ab19390..14b7426097 100644 --- a/platform/macos/Mapbox-macOS-SDK.podspec +++ b/platform/macos/Mapbox-macOS-SDK.podspec @@ -20,7 +20,7 @@ Pod::Spec.new do |m| } m.platform = :osx - m.osx.deployment_target = '10.10' + m.osx.deployment_target = '10.11' m.requires_arc = true diff --git a/platform/macos/app/AppDelegate.m b/platform/macos/app/AppDelegate.m index 0d780424f9..f7b35d0c83 100644 --- a/platform/macos/app/AppDelegate.m +++ b/platform/macos/app/AppDelegate.m @@ -29,7 +29,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesCompleted { ++ (NSSet *)keyPathsForValuesAffectingCountOfResourcesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -37,7 +37,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfResourcesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesExpected { ++ (NSSet *)keyPathsForValuesAffectingCountOfResourcesExpected { return [NSSet setWithObjects:@"progress", nil]; } @@ -45,7 +45,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfResourcesExpected; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfBytesCompleted { ++ (NSSet *)keyPathsForValuesAffectingCountOfBytesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -53,7 +53,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfBytesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTilesCompleted { ++ (NSSet *)keyPathsForValuesAffectingCountOfTilesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -61,7 +61,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; return self.progress.countOfTilesCompleted; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTileBytesCompleted { ++ (NSSet *)keyPathsForValuesAffectingCountOfTileBytesCompleted { return [NSSet setWithObjects:@"progress", nil]; } @@ -157,11 +157,11 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask"; - (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { // mapboxgl://?center=29.95,-90.066667&zoom=14&bearing=45&pitch=30 NSURL *url = [NSURL URLWithString:[event paramDescriptorForKeyword:keyDirectObject].stringValue]; - NS_MUTABLE_DICTIONARY_OF(NSString *, NSString *) *params = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; for (NSString *param in [url.query componentsSeparatedByString:@"&"]) { NSArray *parts = [param componentsSeparatedByString:@"="]; if (parts.count >= 2) { - params[parts[0]] = [parts[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + params[parts[0]] = [parts[1] stringByRemovingPercentEncoding]; } } diff --git a/platform/macos/app/MGLStyle+MBXAdditions.h b/platform/macos/app/MGLStyle+MBXAdditions.h index dcaf42af28..29c2cda867 100644 --- a/platform/macos/app/MGLStyle+MBXAdditions.h +++ b/platform/macos/app/MGLStyle+MBXAdditions.h @@ -2,6 +2,6 @@ @interface MGLStyle (MBXAdditions) -@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *reversedLayers; +@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *reversedLayers; @end diff --git a/platform/macos/app/MGLStyle+MBXAdditions.m b/platform/macos/app/MGLStyle+MBXAdditions.m index be571d8b30..a0773cc2c0 100644 --- a/platform/macos/app/MGLStyle+MBXAdditions.m +++ b/platform/macos/app/MGLStyle+MBXAdditions.m @@ -2,15 +2,15 @@ @implementation MGLStyle (MBXAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingReversedLayers { ++ (NSSet *)keyPathsForValuesAffectingReversedLayers { return [NSSet setWithObject:@"layers"]; } -- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { +- (NSArray<__kindof MGLStyleLayer *> *)reversedLayers { return self.layers.reverseObjectEnumerator.allObjects; } -- (void)setReversedLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers { +- (void)setReversedLayers:(NSArray<__kindof MGLStyleLayer *> *)reversedLayers { self.layers = reversedLayers.reverseObjectEnumerator.allObjects; } diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 9b18dbd761..7be500ae6a 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -19,7 +19,7 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = { { .latitude = -13.15589555, .longitude = -74.2178961777998 }, }; -NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *shapes) { +NSArray> *MBXFlattenedShapes(NSArray> *shapes) { NSMutableArray *flattenedShapes = [NSMutableArray arrayWithCapacity:shapes.count]; for (id shape in shapes) { NSArray *subshapes; @@ -340,7 +340,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; BOOL isVisible = layers.firstObject.visible; [self.undoManager registerUndoWithTarget:self handler:^(MapDocument * _Nonnull target) { [target toggleStyleLayersAtArrangedObjectIndexes:indices]; @@ -376,7 +376,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *)layers atArrangedObjectIndexes:(NSIndexSet *)indices { [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self deleteStyleLayersAtArrangedObjectIndexes:indices]; }]; @@ -396,7 +396,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices]; [self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) { [self insertStyleLayers:layers atArrangedObjectIndexes:indices]; }]; @@ -572,7 +572,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *)annotations { MGLPointAnnotation *nextAnnotation = annotations.firstObject; if (!nextAnnotation || !_isTouringWorld) { _isTouringWorld = NO; @@ -1236,7 +1236,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NSArray *)proposedServices { NSURL *shareURL = self.shareURL; NSURL *browserURL = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:shareURL]; NSImage *browserIcon = [[NSWorkspace sharedWorkspace] iconForFile:browserURL.path]; diff --git a/platform/macos/docs/doc-README.md b/platform/macos/docs/doc-README.md index 8089ae17d1..8ce2df39a4 100644 --- a/platform/macos/docs/doc-README.md +++ b/platform/macos/docs/doc-README.md @@ -1,6 +1,6 @@ # [Mapbox Maps SDK for macOS](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/) -The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. +The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.11.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL. ![](img/screenshot.jpg) diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md index 97e77673ee..4827124be0 100644 --- a/platform/macos/docs/pod-README.md +++ b/platform/macos/docs/pod-README.md @@ -10,7 +10,7 @@ Put interactive, scalable world maps into your native Cocoa application with the ![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/docs/img/screenshot.jpg) -The Mapbox Maps SDK for macOS is compatible with macOS 10.10.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/). +The Mapbox Maps SDK for macOS is compatible with macOS 10.11.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/). ## Installation @@ -37,7 +37,7 @@ After running `carthage update`, you’ll find Mapbox.framework in the Carthage/ Create a [Podfile](https://guides.cocoapods.org/syntax/podfile.html) with the following specification: ```rb -platform :osx, '10.10' +platform :osx, '10.11' target 'TargetNameForYourApp' do pod 'Mapbox-iOS-SDK', '~> x.y' diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index d49e5e6f06..002590d8ff 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -1816,7 +1816,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SYMROOT = "$(PROJECT_DIR)/cmake"; @@ -1867,7 +1867,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SYMROOT = "$(PROJECT_DIR)/cmake"; diff --git a/platform/macos/src/MGLMapView+IBAdditions.mm b/platform/macos/src/MGLMapView+IBAdditions.mm index eada47ef90..cef2863ee6 100644 --- a/platform/macos/src/MGLMapView+IBAdditions.mm +++ b/platform/macos/src/MGLMapView+IBAdditions.mm @@ -4,7 +4,7 @@ @implementation MGLMapView (IBAdditions) -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ { ++ (NSSet *)keyPathsForValuesAffectingStyleURL__ { return [NSSet setWithObject:@"styleURL"]; } @@ -23,7 +23,7 @@ self.styleURL = url; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude { ++ (NSSet *)keyPathsForValuesAffectingLatitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -45,7 +45,7 @@ } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude { ++ (NSSet *)keyPathsForValuesAffectingLongitude { return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil]; } @@ -67,7 +67,7 @@ } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming { ++ (NSSet *)keyPathsForValuesAffectingAllowsZooming { return [NSSet setWithObject:@"zoomEnabled"]; } @@ -79,7 +79,7 @@ self.zoomEnabled = allowsZooming; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling { ++ (NSSet *)keyPathsForValuesAffectingAllowsScrolling { return [NSSet setWithObject:@"scrollEnabled"]; } @@ -91,7 +91,7 @@ self.scrollEnabled = allowsScrolling; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating { ++ (NSSet *)keyPathsForValuesAffectingAllowsRotating { return [NSSet setWithObject:@"rotateEnabled"]; } @@ -103,7 +103,7 @@ self.rotateEnabled = allowsRotating; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting { ++ (NSSet *)keyPathsForValuesAffectingAllowsTilting { return [NSSet setWithObject:@"pitchEnabled"]; } diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h index e3de1069a9..824df827ac 100644 --- a/platform/macos/src/MGLMapView.h +++ b/platform/macos/src/MGLMapView.h @@ -443,7 +443,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations animated:(BOOL)animated; +- (void)showAnnotations:(NSArray> *)annotations animated:(BOOL)animated; /** Sets the visible region so that the map displays the specified annotations with @@ -458,7 +458,7 @@ MGL_EXPORT IB_DESIGNABLE @param animated `YES` if you want the map region change to be animated, or `NO` if you want the map to display the new region immediately without animations. */ -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated; +- (void)showAnnotations:(NSArray> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated; /** Returns the camera that best fits the given coordinate bounds. @@ -621,7 +621,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *annotations; +@property (nonatomic, readonly, nullable) NSArray> *annotations; /** Adds an annotation to the map view. @@ -651,7 +651,7 @@ MGL_EXPORT IB_DESIGNABLE must conform to the `MGLAnnotation` protocol. The map view retains each individual annotation object. */ -- (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations; +- (void)addAnnotations:(NSArray> *)annotations; /** The complete list of annotations associated with the receiver that are @@ -661,7 +661,7 @@ MGL_EXPORT IB_DESIGNABLE annotations are associated with the map view or if no annotations associated with the map view are currently visible, the value of this property is `nil`. */ -@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id ) *visibleAnnotations; +@property (nonatomic, readonly, nullable) NSArray> *visibleAnnotations; /** Removes an annotation from the map view, deselecting it if it is selected. @@ -686,7 +686,7 @@ MGL_EXPORT IB_DESIGNABLE @param annotations The array of annotation objects to remove. Objects in the array must conform to the `MGLAnnotation` protocol. */ -- (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations; +- (void)removeAnnotations:(NSArray> *)annotations; /** Returns a reusable annotation image object associated with its identifier. @@ -712,7 +712,7 @@ MGL_EXPORT IB_DESIGNABLE no annotations associated with the map view are currently visible in the rectangle. */ -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotationsInRect:(CGRect)rect; +- (nullable NSArray> *)visibleAnnotationsInRect:(CGRect)rect; #pragma mark Managing Annotation Selections @@ -729,7 +729,7 @@ MGL_EXPORT IB_DESIGNABLE @note In versions prior to `4.0.0` if the annotation was offscreen it was not selected. */ -@property (nonatomic, copy) NS_ARRAY_OF(id ) *selectedAnnotations; +@property (nonatomic, copy) NSArray> *selectedAnnotations; /** Selects an annotation and displays a callout popover for it. @@ -791,7 +791,7 @@ MGL_EXPORT IB_DESIGNABLE overlays are associated with the map view, the value of this property is empty array. */ -@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id ) *overlays; +@property (nonatomic, readonly, nonnull) NSArray> *overlays; /** Adds a single overlay to the map. @@ -811,7 +811,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which must conform to the `MGLOverlay` protocol. */ -- (void)addOverlays:(NS_ARRAY_OF(id ) *)overlays; +- (void)addOverlays:(NSArray> *)overlays; /** Removes a single overlay from the map. @@ -831,7 +831,7 @@ MGL_EXPORT IB_DESIGNABLE @param overlays An array of objects, each of which conforms to the `MGLOverlay` protocol. */ -- (void)removeOverlays:(NS_ARRAY_OF(id ) *)overlays; +- (void)removeOverlays:(NSArray> *)overlays; #pragma mark Accessing the Underlying Map Data @@ -847,7 +847,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); +- (NSArray> *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:)); /** Returns an array of rendered map features that intersect with a given point, @@ -866,7 +866,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with a given point, @@ -928,7 +928,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); +- (NSArray> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:)); /** Returns an array of rendered map features that intersect with the given @@ -943,7 +943,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); +- (NSArray> *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:)); /** Returns an array of rendered map features that intersect with the given @@ -962,7 +962,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); +- (NSArray> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:)); /** Returns an array of rendered map features that intersect with the given @@ -1026,7 +1026,7 @@ MGL_EXPORT IB_DESIGNABLE @return An array of objects conforming to the `MGLFeature` protocol that represent features in the sources used by the current style. */ -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); +- (NSArray> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:)); #pragma mark Converting Geographic Coordinates diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 9cab9a76da..42fd24653e 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -149,7 +149,7 @@ public: @property (nonatomic, readwrite) MGLStyle *style; /// Mapping from reusable identifiers to annotation images. -@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier; +@property (nonatomic) NSMutableDictionary *annotationImagesByIdentifier; /// Currently shown popover representing the selected annotation. @property (nonatomic) NSPopover *calloutForSelectedAnnotation; @@ -611,7 +611,7 @@ public: #pragma mark Style -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle { ++ (NSSet *)keyPathsForValuesAffectingStyle { return [NSSet setWithObject:@"styleURL"]; } @@ -965,7 +965,7 @@ public: #pragma mark Viewport -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate { ++ (NSSet *)keyPathsForValuesAffectingCenterCoordinate { return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil]; } @@ -1015,7 +1015,7 @@ public: _pendingLongitude = pendingLongitude; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel { ++ (NSSet *)keyPathsForValuesAffectingZoomLevel { return [NSSet setWithObject:@"camera"]; } @@ -1083,7 +1083,7 @@ public: } } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection { ++ (NSSet *)keyPathsForValuesAffectingDirection { return [NSSet setWithObject:@"camera"]; } @@ -1107,7 +1107,7 @@ public: [self setDirection:_mbglMap->getBearing() + delta animated:animated]; } -+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera { ++ (NSSet *)keyPathsForValuesAffectingCamera { return [NSSet setWithObjects:@"latitude", @"longitude", @"centerCoordinate", @"zoomLevel", @"direction", nil]; } @@ -1765,7 +1765,7 @@ public: #pragma mark Annotations -- (nullable NS_ARRAY_OF(id ) *)annotations { +- (nullable NSArray> *)annotations { if (_annotationContextsByAnnotationTag.empty()) { return nil; } @@ -1781,12 +1781,12 @@ public: return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()]; } -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotations +- (nullable NSArray> *)visibleAnnotations { return [self visibleFeaturesInRect:self.bounds]; } -- (nullable NS_ARRAY_OF(id ) *)visibleAnnotationsInRect:(CGRect)rect +- (nullable NSArray> *)visibleAnnotationsInRect:(CGRect)rect { if (_annotationContextsByAnnotationTag.empty()) { @@ -1852,7 +1852,7 @@ public: } } -- (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations { +- (void)addAnnotations:(NSArray> *)annotations { if (!annotations) { return; } @@ -1987,7 +1987,7 @@ public: } } -- (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations { +- (void)removeAnnotations:(NSArray> *)annotations { if (!annotations) { return; } @@ -2192,12 +2192,12 @@ public: [self didChangeValueForKey:@"selectedAnnotations"]; } -- (NS_ARRAY_OF(id ) *)selectedAnnotations { +- (NSArray> *)selectedAnnotations { id selectedAnnotation = self.selectedAnnotation; return selectedAnnotation ? @[selectedAnnotation] : @[]; } -- (void)setSelectedAnnotations:(NS_ARRAY_OF(id ) *)selectedAnnotations { +- (void)setSelectedAnnotations:(NSArray> *)selectedAnnotations { if (!selectedAnnotations.count) { return; } @@ -2330,7 +2330,7 @@ public: } } -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations animated:(BOOL)animated { +- (void)showAnnotations:(NSArray> *)annotations animated:(BOOL)animated { CGFloat maximumPadding = 100; CGFloat yPadding = (NSHeight(self.bounds) / 5 <= maximumPadding) ? (NSHeight(self.bounds) / 5) : maximumPadding; CGFloat xPadding = (NSWidth(self.bounds) / 5 <= maximumPadding) ? (NSWidth(self.bounds) / 5) : maximumPadding; @@ -2340,7 +2340,7 @@ public: [self showAnnotations:annotations edgePadding:edgeInsets animated:animated]; } -- (void)showAnnotations:(NS_ARRAY_OF(id ) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated { +- (void)showAnnotations:(NSArray> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated { if ( ! annotations || ! annotations.count) return; mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); @@ -2537,11 +2537,11 @@ public: #pragma mark Overlays -- (nonnull NS_ARRAY_OF(id ) *)overlays +- (nonnull NSArray> *)overlays { if (self.annotations == nil) { return @[]; } - NS_MUTABLE_ARRAY_OF(id ) *mutableOverlays = [NSMutableArray array]; + NSMutableArray> *mutableOverlays = [NSMutableArray array]; [self.annotations enumerateObjectsUsingBlock:^(id _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) { if ([annotation conformsToProtocol:@protocol(MGLOverlay)]) @@ -2557,7 +2557,7 @@ public: [self addOverlays:@[overlay]]; } -- (void)addOverlays:(NS_ARRAY_OF(id ) *)overlays +- (void)addOverlays:(NSArray> *)overlays { #if DEBUG for (id overlay in overlays) { @@ -2571,7 +2571,7 @@ public: [self removeOverlays:@[overlay]]; } -- (void)removeOverlays:(NS_ARRAY_OF(id ) *)overlays { +- (void)removeOverlays:(NSArray> *)overlays { #if DEBUG for (id overlay in overlays) { NSAssert([overlay conformsToProtocol:@protocol(MGLOverlay)], @"Overlay does not conform to MGLOverlay"); @@ -2652,15 +2652,15 @@ public: #pragma mark Data -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(NSPoint)point { +- (NSArray> *)visibleFeaturesAtPoint:(NSPoint)point { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers { return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { // Cocoa origin is at the lower-left corner. mbgl::ScreenCoordinate screenCoordinate = { point.x, NSHeight(self.bounds) - point.y }; @@ -2683,15 +2683,15 @@ public: return MGLFeaturesFromMBGLFeatures(features); } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(NSRect)rect { +- (NSArray> *)visibleFeaturesInRect:(NSRect)rect { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers { +- (NSArray> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers { return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil]; } -- (NS_ARRAY_OF(id ) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { +- (NSArray> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NSSet *)styleLayerIdentifiers predicate:(NSPredicate *)predicate { // Cocoa origin is at the lower-left corner. mbgl::ScreenBox screenBox = { { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) }, -- cgit v1.2.1 From 4f7999fd1cec34e9beaf130d30897548d8dbca4a Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Tue, 24 Apr 2018 19:21:50 -0400 Subject: [ios, macos] Accept Xcode 9.3 project suggestions - Fix duplicate NSExpression+MGLAdditions.h and fix target membership - Explicitly cast NSUInteger to unsigned long before printing --- platform/darwin/src/NSExpression+MGLAdditions.mm | 6 +++--- platform/ios/ios.xcodeproj/project.pbxproj | 16 +++++++--------- .../ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme | 2 +- .../xcschemes/Integration Test Harness.xcscheme | 4 +--- .../ios.xcodeproj/xcshareddata/xcschemes/bench.xcscheme | 4 +--- .../xcshareddata/xcschemes/dynamic+static.xcscheme | 4 +--- .../xcshareddata/xcschemes/dynamic.xcscheme | 4 +--- .../ios.xcodeproj/xcshareddata/xcschemes/iosapp.xcscheme | 4 +--- .../ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme | 4 +--- .../xcshareddata/xcschemes/Mapbox GL Tests.xcscheme | 4 +--- platform/macos/macos.xcodeproj/project.pbxproj | 6 +++++- .../macos.xcodeproj/xcshareddata/xcschemes/CI.xcscheme | 8 +++----- .../xcshareddata/xcschemes/dynamic.xcscheme | 4 +--- .../xcshareddata/xcschemes/macosapp.xcscheme | 2 +- 14 files changed, 28 insertions(+), 44 deletions(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 8db839c5da..633f433a8f 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -1218,7 +1218,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { case NSAnyKeyExpressionType: case NSBlockExpressionType: [NSException raise:NSInvalidArgumentException - format:@"Expression type %lu not yet implemented.", self.expressionType]; + format:@"Expression type %lu not yet implemented.", (unsigned long)self.expressionType]; } // NSKeyPathSpecifierExpression @@ -1240,11 +1240,11 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { if (self.arguments.count < expectedArgumentCount) { [NSException raise:NSInvalidArgumentException format: @"Too few arguments to ‘%@’ function; expected %lu arguments.", - self.function, expectedArgumentCount]; + self.function, (unsigned long)expectedArgumentCount]; } else if (self.arguments.count > expectedArgumentCount) { [NSException raise:NSInvalidArgumentException format: @"%lu unexpected arguments to ‘%@’ function; expected %lu arguments.", - self.arguments.count - expectedArgumentCount, self.function, expectedArgumentCount]; + self.arguments.count - (unsigned long)expectedArgumentCount, self.function, (unsigned long)expectedArgumentCount]; } BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"]; diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 6892760f59..80335b8bf0 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -53,7 +53,6 @@ 3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFE91D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm */; }; 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */; }; 3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFF71D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h */; }; @@ -307,6 +306,7 @@ 9620BB391E69FE1700705A1D /* MGLSDKUpdateChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */; }; 9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; }; 9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */; }; + 9621F2502091020E005B3800 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9654C1261FFC1AB900DB6A19 /* MGLPolyline_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1251FFC1AB900DB6A19 /* MGLPolyline_Private.h */; }; 9654C1291FFC1CCD00DB6A19 /* MGLPolygon_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 9654C1271FFC1CC000DB6A19 /* MGLPolygon_Private.h */; }; 9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */; }; @@ -511,8 +511,6 @@ DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; }; DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; }; DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; }; - DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; }; DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3557F7AF1E1D27D300CCA5E6 /* MGLDistanceFormatter.m */; }; DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -1188,7 +1186,6 @@ DA9C012C1E4C7ADB00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Foundation.stringsdict"; sourceTree = ""; }; DA9C012D1E4C7B1F00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; DA9C012E1E4C7B6100C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Root.strings"; sourceTree = ""; }; - DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = ""; }; DAA32CA11E4C44DB006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Foundation.stringsdict; sourceTree = ""; }; DAA32CA21E4C44DD006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; DAA32CA31E4C44F1006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Foundation.strings; sourceTree = ""; }; @@ -2099,7 +2096,6 @@ 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */, DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */, 3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */, - DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */, DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */, 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */, 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */, @@ -2289,7 +2285,6 @@ DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */, DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */, DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */, - DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */, DAF0D8101DFE0EA000B28378 /* MGLRasterTileSource_Private.h in Headers */, 350098BB1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */, @@ -2319,7 +2314,6 @@ DABFB8641CBE99E500D62B32 /* MGLOfflineStorage.h in Headers */, 96E516E32000552A00A02306 /* MGLAccountManager_Private.h in Headers */, 96E5170420005A6B00A02306 /* SMCalloutView.h in Headers */, - DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */, 96036A02200565C700510F3D /* NSOrthography+MGLAdditions.h in Headers */, DAD165791CF4CDFF001FF4B9 /* MGLShapeCollection.h in Headers */, 4049C29E1DB6CD6C00B3F799 /* MGLPointCollection.h in Headers */, @@ -2329,7 +2323,6 @@ 96E516F6200059EC00A02306 /* MGLRendererFrontend.h in Headers */, 071BBB041EE76147001FB02A /* MGLImageSource.h in Headers */, DABFB8611CBE99E500D62B32 /* MGLMultiPoint.h in Headers */, - 3510FFF11D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, 35D3A1E71E9BE7EC002B38EE /* MGLScaleBar.h in Headers */, 96E516EF2000594F00A02306 /* NSArray+MGLAdditions.h in Headers */, 96E516F12000596800A02306 /* NSString+MGLAdditions.h in Headers */, @@ -2434,6 +2427,7 @@ 96E516E12000551100A02306 /* MGLMultiPoint_Private.h in Headers */, 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */, DACA86272019218600E9693A /* MGLRasterDEMSource.h in Headers */, + 9621F2502091020E005B3800 /* NSExpression+MGLAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2609,7 +2603,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = MBX; - LastUpgradeCheck = 0910; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = Mapbox; TargetAttributes = { 16376B061FFD9DAF0000563E = { @@ -3474,6 +3468,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3481,6 +3476,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -3533,6 +3529,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -3540,6 +3537,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme index afba168676..01b565d5d9 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/CI.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Release" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme index e5f17124f2..325b58d690 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/dynamic+static.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme index fc385d3763..f88ec1a04c 100644 --- a/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme +++ b/platform/ios/ios.xcodeproj/xcshareddata/xcschemes/static.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme index b1a1db6ba1..2c8de87a0f 100644 --- a/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme +++ b/platform/ios/uitest/ios-tests.xcodeproj/xcshareddata/xcschemes/Mapbox GL Tests.xcscheme @@ -1,6 +1,6 @@ @@ -70,7 +70,7 @@ buildForAnalyzing = "NO"> @@ -82,7 +82,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" shouldUseLaunchSchemeArgsEnv = "YES"> Date: Tue, 24 Apr 2018 20:32:43 -0400 Subject: [ios, macos] Fix possible retain cycles in blocks Prompted by enabling CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF. --- platform/darwin/src/MGLMapSnapshotter.mm | 11 +++++++---- platform/ios/src/MGLMapView.mm | 14 +++++++++++--- platform/macos/app/MapDocument.m | 24 +++++++++++++++--------- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 11a5442761..6449a7fd4f 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -122,8 +122,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; _snapshotCallback = std::make_unique>(*mbgl::Scheduler::GetCurrent(), [=](std::exception_ptr mbglError, mbgl::PremultipliedImage image, mbgl::MapSnapshotter::Attributions attributions, mbgl::MapSnapshotter::PointForFn pointForFn) { __typeof__(self) strongSelf = weakSelf; strongSelf.loading = false; - - + if (mbglError) { NSString *description = @(mbgl::util::toString(mbglError).c_str()); NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description}; @@ -145,9 +144,13 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; } _snapshotCallback = NULL; }); - dispatch_async(queue, ^{ - _mbglMapSnapshotter->snapshot(_snapshotCallback->self()); + dispatch_async(queue, ^{ + __typeof__(self) strongSelf = weakSelf; + if (!strongSelf) { + return; + } + strongSelf->_mbglMapSnapshotter->snapshot(strongSelf->_snapshotCallback->self()); }); } diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index c679979d37..34269ce442 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -4570,6 +4570,8 @@ public: return; } + __weak __typeof__(self) weakSelf = self; + // The user location callout view initially points to the user location // annotation’s implicit (visual) frame, which is offset from the // annotation’s explicit frame. Now the callout view needs to rendezvous @@ -4583,10 +4585,16 @@ public: UIViewAnimationOptionBeginFromCurrentState) animations:^ { + __typeof__(self) strongSelf = weakSelf; + if ( ! strongSelf) + { + return; + } + calloutView.frame = CGRectOffset(calloutView.frame, - _initialImplicitCalloutViewOffset.x, - _initialImplicitCalloutViewOffset.y); - _initialImplicitCalloutViewOffset = CGPointZero; + strongSelf->_initialImplicitCalloutViewOffset.x, + strongSelf->_initialImplicitCalloutViewOffset.y); + strongSelf->_initialImplicitCalloutViewOffset = CGPointZero; } completion:NULL]; } diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m index 9b18dbd761..aee14c5c2f 100644 --- a/platform/macos/app/MapDocument.m +++ b/platform/macos/app/MapDocument.m @@ -93,9 +93,8 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id _snapshotter = nil; }]; } @@ -1179,7 +1185,7 @@ NS_ARRAY_OF(id ) *MBXFlattenedShapes(NS_ARRAY_OF(id Date: Thu, 26 Apr 2018 13:41:19 -0700 Subject: [core] Port is-supported-script to native. Native port is much simpler because RTL text support is always enabled. --- platform/node/test/ignores.json | 4 +-- src/mbgl/style/expression/compound_expression.cpp | 5 +++ src/mbgl/util/i18n.cpp | 37 +++++++++++++++++++++-- src/mbgl/util/i18n.hpp | 2 ++ test/style/expression/expression.test.cpp | 2 +- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 98d7e016bc..a1d4d68511 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -14,7 +14,7 @@ "expression-tests/collator/non-object-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692", "expression-tests/collator/variant-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", "expression-tests/collator/variant-gteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692", - "expression-tests/is-supported-script/default": "https://github.com/mapbox/mapbox-gl-native/issues/11693", + "expression-tests/is-supported-script/default": "This tests RTL text plugin behavior specific to GL JS", "expression-tests/resolved-locale/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11692", "expression-tests/to-string/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11719", "query-tests/geometry/multilinestring": "needs investigation", @@ -39,8 +39,6 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", - "render-tests/is-supported-script/filter": "https://github.com/mapbox/mapbox-gl-native/issues/11693", - "render-tests/is-supported-script/layout": "https://github.com/mapbox/mapbox-gl-native/issues/11693", "render-tests/line-gradient/gradient": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/line-gradient/translucent": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index c36ffa33e3..c257dbf2bb 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -379,6 +380,10 @@ std::unordered_map initiali define("!", [](bool e) -> Result { return !e; }); + define("is-supported-script", [](const std::string& x) -> Result { + return util::i18n::isStringInSupportedScript(x); + }); + define("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); diff --git a/src/mbgl/util/i18n.cpp b/src/mbgl/util/i18n.cpp index 1fc13bfb7d..5530796915 100644 --- a/src/mbgl/util/i18n.cpp +++ b/src/mbgl/util/i18n.cpp @@ -1,4 +1,5 @@ -#include "i18n.hpp" +#include +#include #include #include @@ -65,7 +66,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F) // DEFINE_IS_IN_UNICODE_BLOCK(Hanunoo, 0x1720, 0x173F) // DEFINE_IS_IN_UNICODE_BLOCK(Buhid, 0x1740, 0x175F) // DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F) -// DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) +DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF) // DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF) DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF) // DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F) @@ -581,6 +582,38 @@ std::u16string verticalizePunctuation(const std::u16string& input) { char16_t verticalizePunctuation(char16_t chr) { return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0; } + +bool charInSupportedScript(char16_t chr) { + // This is a rough heuristic: whether we "can render" a script + // actually depends on the properties of the font being used + // and whether differences from the ideal rendering are considered + // semantically significant. + + // Even in Latin script, we "can't render" combinations such as the fi + // ligature, but we don't consider that semantically significant.n false; + if ((chr >= 0x0900 && chr <= 0x0DFF) || + // Main blocks for Indic scripts and Sinhala + (chr >= 0x0F00 && chr <= 0x109F) || + // Main blocks for Tibetan and Myanmar + isInKhmer(chr)) { + // These blocks cover common scripts that require + // complex text shaping, based on unicode script metadata: + // http://www.unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt + // where "Web Rank <= 32" "Shaping Required = YES" + return false; + } + return true; +} + +bool isStringInSupportedScript(const std::string& input) { + auto u16string = util::utf8_to_utf16::convert(input); + for (char16_t chr : u16string) { + if (!charInSupportedScript(chr)) { + return false; + } + } + return true; +} } // namespace i18n } // namespace util diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp index b3960c743c..a74215a134 100644 --- a/src/mbgl/util/i18n.hpp +++ b/src/mbgl/util/i18n.hpp @@ -72,6 +72,8 @@ std::u16string verticalizePunctuation(const std::u16string& input); @return The character’s specialized vertical form; 0 if not applicable. */ char16_t verticalizePunctuation(char16_t chr); + +bool isStringInSupportedScript(const std::string& input); } // namespace i18n } // namespace util diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index d5598d873b..9d5e172888 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -29,7 +29,7 @@ TEST(Expression, IsExpression) { for(auto& entry : allExpressions.GetObject()) { const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; - if (name == "collator" || name == "line-progress" || name == "is-supported-script" || name == "resolved-locale") { + if (name == "collator" || name == "line-progress" || name == "resolved-locale") { // Not yet implemented continue; } -- cgit v1.2.1 From 9a4b806fa84bd3d2d1adb57eae4f9fdb5f79d9f9 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 29 Apr 2018 15:27:49 -0700 Subject: [docs] Make per-platform installation docs self-contained I've seen several issues where users followed platform-specific install docs, but were unaware of additional prerequisites spelled out in the top-level INSTALL.md. So let's try making each platform's installation documentation self contained. --- INSTALL.md | 61 ++++++++------------------------------------ platform/ios/DEVELOPING.md | 6 +---- platform/ios/INSTALL.md | 26 ++++++++++++++++--- platform/linux/README.md | 20 ++++++++++++--- platform/macos/DEVELOPING.md | 16 +----------- platform/macos/INSTALL.md | 36 +++++++++++++++++++++----- platform/node/DEVELOPING.md | 8 +++--- platform/node/README.md | 3 ++- platform/qt/README.md | 7 ++--- 9 files changed, 88 insertions(+), 95 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index e5d059e1bb..463d199d10 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,56 +1,15 @@ -# Building & Developing Mapbox GL Native from Source +# Building & Developing from Source -**Just trying to use Mapbox GL Native? You don't need to read this stuff! We +**Just trying to use one of the Mapbox Maps SDKs? You don't need to read this stuff! We provide [easy-to-install, prebuilt versions of the Mapbox Maps SDKs for iOS and Android that you can download instantly and get started with fast](https://www.mapbox.com/install/).** -Still with us? These are the instructions you'll need to build Mapbox GL Native -from source on a variety of platforms and set up a development environment. +If you're certain you need to build from source, rather than using a prebuilt SDK, please +refer to the installation instructions for the platform of interest: -Your journey will start with installing dependencies, then getting the source code, and -then setting up a development environment, which varies depending on your -operating system and what platform you want to develop for. - -## 1: Installing dependencies - -### macOS - - 1. Install [Xcode](https://developer.apple.com/xcode/) - 2. Launch Xcode and install any updates - 3. Install [Homebrew](http://brew.sh) - 4. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org) with `brew install nodejs cmake ccache` - 5. Install [xcpretty](https://github.com/supermarin/xcpretty) with `[sudo] gem install xcpretty` (optional, used for prettifying command line builds) - -### Linux - -Install the following: - - - `clang++` 3.5 or later or `g++` 4.9 or later - - [git](https://git-scm.com/) - - [CMake](https://cmake.org/) 3.1 or later - - [cURL](https://curl.haxx.se) - - [Node.js](https://nodejs.org/) 4.2.1 or later - - [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL) - - [ccache](https://ccache.samba.org) (optional, improves recompilation performance) - -## 2: Getting the source - - Clone the git repository: - - git clone https://github.com/mapbox/mapbox-gl-native.git - cd mapbox-gl-native - -## 3: Setting up a development environment & building - -See the relevant SDK documentation for next steps: - -* [Maps SDK for Android](platform/android/) -* [Maps SDK for iOS](platform/ios/) -* [Maps SDK for macOS](platform/macos/) -* [Maps SDK for Qt](platform/qt/) -* [Mapbox GL Native on Linux](platform/linux/) -* [node-mapbox-gl-native](platform/node/) - -## 4: Keeping up to date - -This repository uses Git submodules, which should be automatically checked out when you first run a `make` command for one of the above platforms. These submodules are not updated automatically and we recommended that you run `git submodule update` after pulling down new commits to this repository. +* [Mapbox Maps SDK for Android](platform/android/README.md) +* [Mapbox Maps SDK for iOS](platform/ios/INSTALL.md) +* [Mapbox Maps SDK for macOS](platform/macos/INSTALL.md) +* [Mapbox Maps SDK for Qt](platform/qt/README.md) +* [Mapbox GL Native on Linux](platform/linux/README.md) +* [node-mapbox-gl-native](platform/node/DEVELOPING.md) diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md index b407e78e4b..34388c2589 100644 --- a/platform/ios/DEVELOPING.md +++ b/platform/ios/DEVELOPING.md @@ -4,14 +4,10 @@ This document explains how to build the Mapbox Maps SDK for iOS from source. It ## Requirements -The Mapbox Maps SDK for iOS and iosapp demo application require iOS 9.0 or above. - -The Mapbox Maps SDK for iOS requires Xcode 9.1 or above to compile from source. +See the "Requirements" section in [INSTALL.md](INSTALL.md). ## Building the SDK -Make sure that you have the [core dependencies](../../INSTALL.md) installed. - Create and open an Xcode workspace that includes both the SDK source and some Objective-C test applications by running: ```bash diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md index f253d3019d..3c79e4dcf8 100644 --- a/platform/ios/INSTALL.md +++ b/platform/ios/INSTALL.md @@ -18,16 +18,34 @@ The Mapbox Maps SDK for iOS requires: * Xcode 9.1 or higher to compile from source * Xcode 8.0 or higher to integrate the compiled framework into an application -### Building the SDK - -1. [Install core dependencies](../../INSTALL.md). +Before building, follow these steps to install prerequisites: +1. Install [Xcode](https://developer.apple.com/xcode/) +1. Launch Xcode and install any updates +1. Install [Homebrew](http://brew.sh) +1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org): + ``` + brew install node cmake ccache + ``` +1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds): + ``` + [sudo] gem install xcpretty + ``` 1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - ``` [sudo] gem install jazzy ``` +### Building the SDK + +1. Clone the git repository: + ``` + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + ``` + Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, + but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to + this repository. 1. Run `make iframework BUILDTYPE=Release`. The packaging script will produce a `build/ios/pkg/` folder containing: - a `dynamic` folder containing a dynamically-linked fat framework with debug symbols for devices and the iOS Simulator - a `documentation` folder with HTML API documentation diff --git a/platform/linux/README.md b/platform/linux/README.md index 8b8ac9d089..a8a4d459ee 100644 --- a/platform/linux/README.md +++ b/platform/linux/README.md @@ -6,7 +6,7 @@ We are using Ubuntu for development. While the software should work on other dis This process gives you a Linux desktop app built on a Linux host system. -### Build +### Prerequisites Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/). @@ -15,9 +15,6 @@ Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you ca sudo apt-get install gcc-4.9 g++-4.9 export CXX=g++-4.9 -**Note**: We partially support C++14 because GCC 4.9 does not fully implement the -final draft of the C++14 standard. More information in [DEVELOPING.md](DEVELOPING.md). - Ensure you have git and other build essentials: sudo apt-get install curl git build-essential zlib1g-dev automake \ @@ -38,6 +35,21 @@ Install glfw3 dependencies: x11proto-xf86vidmode-dev libxxf86vm-dev \ libxcursor-dev libxinerama-dev +[Node.js](https://nodejs.org/) 4.2.1 or later is also required. + +[ccache](https://ccache.samba.org) is optional, but improves recompilation performance. + +## Build + +Clone the git repository: + + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + +Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, +but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to +this repository. + Set the environment variable `MAPBOX_ACCESS_TOKEN` to your [Mapbox access token](ACCESS_TOKEN.md): export MAPBOX_ACCESS_TOKEN=MYTOKEN diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md index bac5683266..f4d946c527 100644 --- a/platform/macos/DEVELOPING.md +++ b/platform/macos/DEVELOPING.md @@ -4,24 +4,10 @@ This document explains how to build the Mapbox Maps SDK for macOS from source. I ## Requirements -The Mapbox Maps SDK for macOS and the macosapp demo application run on macOS 10.10.0 or above. - -The Mapbox Maps SDK for macOS requires Xcode 8.0 or above. +See the "Requirements" section in [INSTALL.md](INSTALL.md). ## Building the SDK -1. [Install core dependencies](../../INSTALL.md). -1. Run `make xproj`. -1. Switch to the “dynamic” or “macosapp” scheme. The former builds just the Cocoa framework, while the latter also builds a Cocoa demo application based on it. - -### Packaging builds - -Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - -```bash -[sudo] gem install jazzy -``` - Build and package the SDK by using one of the following commands: * `make xpackage` builds a dynamic framework in the Debug configuration, including debug symbols. diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md index c723d3e062..f0fb0278be 100644 --- a/platform/macos/INSTALL.md +++ b/platform/macos/INSTALL.md @@ -6,19 +6,41 @@ This document explains how to build a development version of the Mapbox Maps SDK The Mapbox Maps SDK for macOS requires the macOS 10.10.0 SDK (or above) and Xcode 8.0 (or above). To use this SDK with Xcode 7.3.1, download and use a symbols build from the [releases](https://github.com/mapbox/mapbox-gl-native/releases) page. -### Building the SDK from source - -To build the SDK from source: - -1. [Install core dependencies](../../INSTALL.md). +Before building, follow these steps to install prerequisites: +1. Install [Xcode](https://developer.apple.com/xcode/) +1. Launch Xcode and install any updates +1. Install [Homebrew](http://brew.sh) +1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org): + ``` + brew install node cmake ccache + ``` +1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds): + ``` + [sudo] gem install xcpretty + ``` 1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation: - ``` [sudo] gem install jazzy ``` -1. Run `make xpackage`, which produces a `Mapbox.framework` in the `build/macos/pkg/` folder. +### Building the SDK from source + +To build the SDK from source: + +1. Clone the git repository: + ``` + git clone https://github.com/mapbox/mapbox-gl-native.git + cd mapbox-gl-native + ``` + Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command, + but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to + this repository. +1. Run: + ``` + make xpackage + ``` + This produces a `Mapbox.framework` in the `build/macos/pkg/` folder. ### Installation diff --git a/platform/node/DEVELOPING.md b/platform/node/DEVELOPING.md index b313d75c13..215b06c7bf 100644 --- a/platform/node/DEVELOPING.md +++ b/platform/node/DEVELOPING.md @@ -4,11 +4,13 @@ This document explains how to build the [Node.js](https://nodejs.org/) bindings ## Building -To develop these bindings, you’ll need to build them from source. Building requires [installing all of the basic dependencies needed for Mapbox GL Native](../../INSTALL.md), then running: +To develop these bindings, you’ll need to build them from source. Building requires the prerequisites listed in either +the [macOS](../macos/INSTALL.md#requirements) or [Linux](../linux/README.md#prerequisites) install documentation, depending +on the target platform. - npm install --build-from-source +To compile the Node.js bindings and install module dependencies, from the repository root directory, run: -From the root directory. This will compile the Node.js bindings and install module dependencies. + npm install --build-from-source To recompile just the C++ code while developing, run `make node`. diff --git a/platform/node/README.md b/platform/node/README.md index d19b2a9343..ac5bcd7e8d 100644 --- a/platform/node/README.md +++ b/platform/node/README.md @@ -17,7 +17,8 @@ Run: npm install @mapbox/mapbox-gl-native ``` -Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites. +Other platforms will fall back to a source compile with `make node`; see [DEVELOPING.md](DEVELOPING.md) for details on +building from source. ## Testing diff --git a/platform/qt/README.md b/platform/qt/README.md index 018f8823b6..f083d4b519 100644 --- a/platform/qt/README.md +++ b/platform/qt/README.md @@ -17,9 +17,7 @@ See the Mapbox Qt landing page for more details: https://www.mapbox.com/qt/ #### Linux -For Linux (tested on Ubuntu) desktop, together with these [build -instructions](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/linux#build), -you also need: +For Linux (tested on Ubuntu) desktop, together with these [build instructions](../linux/README.md), you also need: ``` $ sudo apt-get install qt5-default @@ -55,8 +53,7 @@ At runtime, you will also need installed: ### Build instructions -Public API headers -can be found in the [platform/qt/include](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/qt/include) directory. +Public API headers can be found in the [platform/qt/include](qt/include) directory. #### Linux and macOS -- cgit v1.2.1 From b820ca4686b923785f359d4e7054a2c47b5faff8 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Mon, 30 Apr 2018 15:27:28 -0700 Subject: [core] Remove unused 'Throttler' class. Throttler was previously used to control how frequently background placement ran. --- cmake/core-files.cmake | 2 -- src/mbgl/tile/geometry_tile.hpp | 1 - src/mbgl/util/throttler.cpp | 36 ------------------------------------ src/mbgl/util/throttler.hpp | 22 ---------------------- 4 files changed, 61 deletions(-) delete mode 100644 src/mbgl/util/throttler.cpp delete mode 100644 src/mbgl/util/throttler.hpp diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index d4fec34bf5..0474de4680 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -748,8 +748,6 @@ set(MBGL_CORE_FILES src/mbgl/util/stopwatch.hpp src/mbgl/util/string.cpp src/mbgl/util/thread_local.hpp - src/mbgl/util/throttler.cpp - src/mbgl/util/throttler.hpp src/mbgl/util/tile_coordinate.hpp src/mbgl/util/tile_cover.cpp src/mbgl/util/tile_cover.hpp diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index d0490f1009..af122474c2 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/util/throttler.cpp b/src/mbgl/util/throttler.cpp deleted file mode 100644 index 910810ce2f..0000000000 --- a/src/mbgl/util/throttler.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -namespace mbgl { -namespace util { - -Throttler::Throttler(Duration frequency_, std::function&& function_) - : frequency(frequency_) - , function(std::move(function_)) - , pendingInvocation(false) - , lastInvocation(TimePoint::min()) -{} - -void Throttler::invoke() { - if (pendingInvocation) { - return; - } - - Duration timeToNextInvocation = lastInvocation == TimePoint::min() - ? Duration::zero() - : (lastInvocation + frequency) - Clock::now(); - - if (timeToNextInvocation <= Duration::zero()) { - lastInvocation = Clock::now(); - function(); - } else { - pendingInvocation = true; - timer.start(timeToNextInvocation, Duration::zero(), [this]{ - pendingInvocation = false; - lastInvocation = Clock::now(); - function(); - }); - } -} - -} // namespace util -} // namespace mbgl diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp deleted file mode 100644 index 175de7ccaf..0000000000 --- a/src/mbgl/util/throttler.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include - -namespace mbgl { -namespace util { - -class Throttler { -public: - Throttler(Duration frequency, std::function&& function); - - void invoke(); -private: - Duration frequency; - std::function function; - - Timer timer; - bool pendingInvocation; - TimePoint lastInvocation; -}; - -} // namespace util -} // namespace mbgl -- cgit v1.2.1 From a6d70c2db73daf6b490be976b86a1c826a2edc3e Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 1 May 2018 13:05:51 -0700 Subject: [core] Don't copy TileLayerIndexes on every frame. Fixes issue #11811 (too much CPU time spent in CrossTileSymbolIndex). --- src/mbgl/text/cross_tile_symbol_index.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index f88bab9d6f..01a4a02b4e 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -62,7 +62,7 @@ CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() { } bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) { - auto thisZoomIndexes = indexes[tileID.overscaledZ]; + const auto& thisZoomIndexes = indexes[tileID.overscaledZ]; auto previousIndex = thisZoomIndexes.find(tileID); if (previousIndex != thisZoomIndexes.end()) { if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) { -- cgit v1.2.1 From cf4b4e728c26e444514f6ba792c207692350eb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 30 Apr 2018 18:32:37 +0200 Subject: [android] - checking is renderer is not destroyed before delivering the snapshot --- .../mapboxsdk/maps/renderer/MapRenderer.java | 8 ++------ platform/android/src/map_renderer.cpp | 23 +++++++--------------- platform/android/src/map_renderer.hpp | 6 +----- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index f1c70325a0..fcee5bd179 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -37,11 +37,11 @@ public abstract class MapRenderer implements MapRendererScheduler { } public void onPause() { - nativeOnPause(); + // Implement if needed } public void onResume() { - nativeOnResume(); + // Implement if needed } public void onStop() { @@ -124,10 +124,6 @@ public abstract class MapRenderer implements MapRendererScheduler { private native void nativeRender(); - private native void nativeOnResume(); - - private native void nativeOnPause(); - private long frames; private long timeElapsed; diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp index f7e16b7091..ba6fdc63b0 100644 --- a/platform/android/src/map_renderer.cpp +++ b/platform/android/src/map_renderer.cpp @@ -29,6 +29,7 @@ MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object obj, MapRenderer::~MapRenderer() = default; void MapRenderer::reset() { + destroyed = true; // Make sure to destroy the renderer on the GL Thread auto self = ActorRef(*this, mailbox); self.ask(&MapRenderer::resetRenderer).wait(); @@ -88,8 +89,10 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) { self.invoke( &MapRenderer::scheduleSnapshot, std::make_unique([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { - runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { - callback(std::move(image)); + runloop->invoke([callback=std::move(callback), image=std::move(image), renderer=std::move(this)]() mutable { + if (renderer && !renderer->destroyed) { + callback(std::move(image)); + } }); snapshotCallback.reset(); }) @@ -136,7 +139,7 @@ void MapRenderer::render(JNIEnv&) { renderer->render(*params); // Deliver the snapshot if requested - if (snapshotCallback && !paused) { + if (snapshotCallback) { snapshotCallback->operator()(backend->readFramebuffer()); snapshotCallback.reset(); } @@ -174,14 +177,6 @@ void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { requestRender(); } -void MapRenderer::onResume(JNIEnv&) { - paused = false; -} - -void MapRenderer::onPause(JNIEnv&) { - paused = true; -} - // Static methods // jni::Class MapRenderer::javaClass; @@ -200,11 +195,7 @@ void MapRenderer::registerNative(jni::JNIEnv& env) { METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"), METHOD(&MapRenderer::onSurfaceChanged, - "nativeOnSurfaceChanged"), - METHOD(&MapRenderer::onResume, - "nativeOnResume"), - METHOD(&MapRenderer::onPause, - "nativeOnPause")); + "nativeOnSurfaceChanged")); } MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object jObject) { diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp index 5fb5ef1a61..97d2db4a91 100644 --- a/platform/android/src/map_renderer.hpp +++ b/platform/android/src/map_renderer.hpp @@ -98,10 +98,6 @@ private: void onSurfaceChanged(JNIEnv&, jint width, jint height); - void onResume(JNIEnv&); - - void onPause(JNIEnv&); - private: GenericUniqueWeakObject javaPeer; @@ -124,7 +120,7 @@ private: std::mutex updateMutex; bool framebufferSizeChanged = false; - std::atomic paused {false}; + std::atomic destroyed {false}; std::unique_ptr snapshotCallback; }; -- cgit v1.2.1 From fa700cc10864a6ec9df1f41062b210072f4926d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 1 May 2018 15:15:30 +0200 Subject: Revert "[android] - unwrap LatLngBounds for the shortest path when passing to core (#11759)" This reverts commit eb39c80 --- .../mapbox/mapboxsdk/geometry/LatLngBounds.java | 45 ++++------------------ .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 13 ++----- .../mapboxsdk/geometry/LatLngBoundsTest.java | 22 +---------- 3 files changed, 14 insertions(+), 66 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index eec0d566bb..05187cf333 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -28,7 +28,7 @@ public class LatLngBounds implements Parcelable { /** * Construct a new LatLngBounds based on its corners, given in NESW * order. - *

+ * * If eastern longitude is smaller than the western one, bounds will include antimeridian. * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees * and cross the antimeridian. @@ -46,11 +46,6 @@ public class LatLngBounds implements Parcelable { this.longitudeWest = westLongitude; } - LatLngBounds(LatLngBounds latLngBounds) { - this(latLngBounds.latitudeNorth, latLngBounds.longitudeEast, - latLngBounds.latitudeSouth, latLngBounds.longitudeWest); - } - /** * Returns the world bounds. * @@ -80,6 +75,7 @@ public class LatLngBounds implements Parcelable { if (longCenter >= GeometryConstants.MAX_LONGITUDE) { longCenter = this.longitudeEast - halfSpan; } + return new LatLng(latCenter, longCenter); } return new LatLng(latCenter, longCenter); @@ -192,6 +188,7 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } + static double getLongitudeSpan(final double longEast, final double longWest) { double longSpan = Math.abs(longEast - longWest); if (longEast >= longWest) { @@ -202,25 +199,6 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } - /** - * If bounds cross the antimeridian, unwrap west longitude for the shortest path. - * - * @return unwrapped bounds - */ - public LatLngBounds unwrapBounds() { - double unwrapedLonWest = longitudeWest; - if (longitudeEast < longitudeWest) { - if (longitudeWest > 0 && longitudeEast < 0) { - unwrapedLonWest -= GeometryConstants.LONGITUDE_SPAN; - } else if (longitudeWest < 0 && longitudeEast > 0) { - unwrapedLonWest += GeometryConstants.LONGITUDE_SPAN; - } - return unwrapped(latitudeNorth, longitudeEast, latitudeSouth, unwrapedLonWest); - } else { - return new LatLngBounds(this); - } - } - /** * Validate if LatLngBounds is empty, determined if absolute distance is * @@ -301,11 +279,12 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from doubles representing a LatLng pair. - *

+ * * This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. + * *

* This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -339,20 +318,12 @@ public class LatLngBounds implements Parcelable { throw new IllegalArgumentException("LatSouth cannot be less than latNorth"); } - return wrapped(latNorth, lonEast, latSouth, lonWest); - } - - static LatLngBounds wrapped(double latNorth, double lonEast, double latSouth, double lonWest) { lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); } - static LatLngBounds unwrapped(double latNorth, double lonEast, double latSouth, double lonWest) { - return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); - } - private static double lat_(int z, int y) { double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z); return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); @@ -364,14 +335,14 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from a Tile identifier. - *

+ * * Returned bounds will have latitude in the range of Mercator projection. + * @see GeometryConstants#MIN_MERCATOR_LATITUDE + * @see GeometryConstants#MAX_MERCATOR_LATITUDE * * @param z Tile zoom level. * @param x Tile X coordinate. * @param y Tile Y coordinate. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE */ public static LatLngBounds from(int z, int x, int y) { return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 745485e2d2..5e36dd0f78 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1540,12 +1540,7 @@ public final class MapboxMap { * @param latLngBounds the bounds to constrain the map with */ public void setLatLngBoundsForCameraTarget(@Nullable LatLngBounds latLngBounds) { - if (latLngBounds == null) { - nativeMapView.setLatLngBounds(latLngBounds); - } else { - //unwrapping the bounds to generate the right convex hull in core - nativeMapView.setLatLngBounds(latLngBounds.unwrapBounds()); - } + nativeMapView.setLatLngBounds(latLngBounds); } /** @@ -1555,9 +1550,9 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { - // get padded camera position from LatLngBounds, unwrapping the bounds to generate the right convex hull in core - return nativeMapView.getCameraForLatLngBounds(latLngBounds.unwrapBounds(), padding); + public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { + // get padded camera position from LatLngBounds + return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index c1e497af32..e072f07fb9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -524,6 +524,7 @@ public class LatLngBoundsTest { LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); } + @Test public void testConstructorChecksSouthLatitudeNaN() { exception.expect(IllegalArgumentException.class); @@ -542,7 +543,7 @@ public class LatLngBoundsTest { public void testConstructorChecksSouthLatitudeGreaterThan90() { exception.expect(IllegalArgumentException.class); exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20, 95, 0); + LatLngBounds.from(20, 20,95, 0); } @Test @@ -565,23 +566,4 @@ public class LatLngBoundsTest { exception.expectMessage("LatSouth cannot be less than latNorth"); LatLngBounds.from(0, 20, 20, 0); } - - @Test - public void testCopyConstructor() { - LatLngBounds bounds = LatLngBounds.from(50, 10, -20, -30); - LatLngBounds copyBounds = new LatLngBounds(bounds); - assertEquals(bounds, copyBounds); - } - - @Test - public void testUnwrapBounds() { - LatLngBounds bounds = LatLngBounds.from(16.5, -172.8, -35.127709, 172.6); - LatLngBounds unwrappedBounds = bounds.unwrapBounds(); - assertEquals(bounds.getCenter().wrap(), unwrappedBounds.getCenter().wrap()); - assertEquals(bounds.getSpan(), unwrappedBounds.getSpan()); - assertTrue(unwrappedBounds.getLonEast() < 0 && unwrappedBounds.getLonWest() < 0); - - LatLngBounds bounds2 = LatLngBounds.from(16.5, -162.8, -35.127709, -177.4); - assertEquals(bounds2, bounds2.unwrapBounds()); - } } -- cgit v1.2.1 From 8bf1ff1b36b8575823c8f5612fb39070b4ab8e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 1 May 2018 15:33:33 +0200 Subject: [android] - unwrap LatLngBounds before creating core object --- .../com/mapbox/mapboxsdk/geometry/LatLngBounds.java | 13 +++++-------- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 2 +- .../mapbox/mapboxsdk/geometry/LatLngBoundsTest.java | 3 +-- platform/android/src/geometry/lat_lng_bounds.cpp | 19 +++++++++++-------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 05187cf333..90cb56f605 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -28,7 +28,7 @@ public class LatLngBounds implements Parcelable { /** * Construct a new LatLngBounds based on its corners, given in NESW * order. - * + *

* If eastern longitude is smaller than the western one, bounds will include antimeridian. * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees * and cross the antimeridian. @@ -75,7 +75,6 @@ public class LatLngBounds implements Parcelable { if (longCenter >= GeometryConstants.MAX_LONGITUDE) { longCenter = this.longitudeEast - halfSpan; } - return new LatLng(latCenter, longCenter); } return new LatLng(latCenter, longCenter); @@ -188,7 +187,6 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } - static double getLongitudeSpan(final double longEast, final double longWest) { double longSpan = Math.abs(longEast - longWest); if (longEast >= longWest) { @@ -279,12 +277,11 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from doubles representing a LatLng pair. - * + *

* This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. - * *

* This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -335,14 +332,14 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from a Tile identifier. - * + *

* Returned bounds will have latitude in the range of Mercator projection. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE * * @param z Tile zoom level. * @param x Tile X coordinate. * @param y Tile Y coordinate. + * @see GeometryConstants#MIN_MERCATOR_LATITUDE + * @see GeometryConstants#MAX_MERCATOR_LATITUDE */ public static LatLngBounds from(int z, int x, int y) { return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 5e36dd0f78..cfa7143671 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1550,7 +1550,7 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { + public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { // get padded camera position from LatLngBounds return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e072f07fb9..c66e4b6fda 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -524,7 +524,6 @@ public class LatLngBoundsTest { LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); } - @Test public void testConstructorChecksSouthLatitudeNaN() { exception.expect(IllegalArgumentException.class); @@ -543,7 +542,7 @@ public class LatLngBoundsTest { public void testConstructorChecksSouthLatitudeGreaterThan90() { exception.expect(IllegalArgumentException.class); exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20,95, 0); + LatLngBounds.from(20, 20, 95, 0); } @Test diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp index ec1a32fed5..827ee52e95 100644 --- a/platform/android/src/geometry/lat_lng_bounds.cpp +++ b/platform/android/src/geometry/lat_lng_bounds.cpp @@ -9,14 +9,17 @@ jni::Object LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds } mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object bounds) { - static auto swLat = LatLngBounds::javaClass.GetField(env, "latitudeSouth"); - static auto swLon = LatLngBounds::javaClass.GetField(env, "longitudeWest"); - static auto neLat = LatLngBounds::javaClass.GetField(env, "latitudeNorth"); - static auto neLon = LatLngBounds::javaClass.GetField(env, "longitudeEast"); - return mbgl::LatLngBounds::hull( - { bounds.Get(env, swLat), bounds.Get(env, swLon) }, - { bounds.Get(env, neLat), bounds.Get(env, neLon) } - ); + static auto swLatField = LatLngBounds::javaClass.GetField(env, "latitudeSouth"); + static auto swLonField = LatLngBounds::javaClass.GetField(env, "longitudeWest"); + static auto neLatField = LatLngBounds::javaClass.GetField(env, "latitudeNorth"); + static auto neLonField = LatLngBounds::javaClass.GetField(env, "longitudeEast"); + + mbgl::LatLng sw = { bounds.Get(env, swLatField), bounds.Get(env, swLonField) }; + mbgl::LatLng ne = { bounds.Get(env, neLatField), bounds.Get(env, neLonField) }; + + sw.unwrapForShortestPath(ne); + + return mbgl::LatLngBounds::hull(sw, ne); } void LatLngBounds::registerNative(jni::JNIEnv& env) { -- cgit v1.2.1 From 9522674c42eb5fd41c3c09649bab33f5a043ad54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Fri, 27 Apr 2018 12:24:32 +0200 Subject: [android] - null check source before removing --- .../main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 6 +++++- .../mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 976277dcac..0e77910c3d 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -752,9 +752,13 @@ final class NativeMapView { return null; } Source source = getSource(sourceId); - return removeSource(source); + if (source != null) { + return removeSource(source); + } + return null; } + @Nullable public Source removeSource(@NonNull Source source) { if (isDestroyedOn("removeSource")) { return null; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java index fc526176d4..23a75d1642 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java @@ -280,6 +280,20 @@ public class RuntimeStyleTests extends BaseActivityTest { }); } + @Test + public void testRemoveNonExistingSource() { + invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.removeSource("source")); + } + + @Test + public void testRemoveNonExistingLayer() { + invoke(mapboxMap, (uiController, mapboxMap) -> { + mapboxMap.removeLayer("layer"); + mapboxMap.removeLayerAt(mapboxMap.getLayers().size() + 1); + mapboxMap.removeLayerAt(-1); + }); + } + /** * https://github.com/mapbox/mapbox-gl-native/issues/7973 */ -- cgit v1.2.1 From ba9b49cb997ba6cd119242be9209c7a16d53bd40 Mon Sep 17 00:00:00 2001 From: tobrun Date: Wed, 2 May 2018 13:29:17 +0200 Subject: [android] - fix expression example that changes icon images dynamically --- .../activity/style/ZoomFunctionSymbolLayerActivity.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java index df06c9c42d..81fd2c6ff8 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; - import com.google.gson.JsonObject; import com.mapbox.geojson.Feature; import com.mapbox.geojson.FeatureCollection; @@ -16,16 +15,13 @@ import com.mapbox.mapboxsdk.style.layers.Property; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; - +import timber.log.Timber; import java.util.List; -import timber.log.Timber; - import static com.mapbox.mapboxsdk.style.expressions.Expression.get; -import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; -import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.step; import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; @@ -44,7 +40,6 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { private static final String BUS_MAKI_ICON_ID = "bus-11"; private static final String CAFE_MAKI_ICON_ID = "cafe-11"; private static final String KEY_PROPERTY_SELECTED = "selected"; - private static final float ZOOM_STOP_MIN_VALUE = 7.0f; private static final float ZOOM_STOP_MAX_VALUE = 12.0f; private MapView mapView; @@ -103,11 +98,9 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { layer = new SymbolLayer(LAYER_ID, SOURCE_ID); layer.setProperties( iconImage( - interpolate( - linear(), zoom(), - stop(ZOOM_STOP_MIN_VALUE, BUS_MAKI_ICON_ID), - stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID) - ) + step(zoom(), literal(BUS_MAKI_ICON_ID), + stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID) + ) ), iconSize( switchCase( -- cgit v1.2.1 From a96fe6ad87d386f25a57a2a7a6632382951733f0 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 1 May 2018 13:05:51 -0700 Subject: [core] Don't copy TileLayerIndexes on every frame. Fixes issue #11811 (too much CPU time spent in CrossTileSymbolIndex). --- platform/android/CHANGELOG.md | 2 ++ platform/ios/CHANGELOG.md | 1 + src/mbgl/text/cross_tile_symbol_index.cpp | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 246b5828c4..4df5e79ee7 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,8 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. + - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811) + ## 6.0.1 - April 17, 2018 - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index c6b744395b..3ec701c8ed 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -12,6 +12,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) ## 4.0.0 - April 19, 2018 diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index f88bab9d6f..01a4a02b4e 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -62,7 +62,7 @@ CrossTileSymbolLayerIndex::CrossTileSymbolLayerIndex() { } bool CrossTileSymbolLayerIndex::addBucket(const OverscaledTileID& tileID, SymbolBucket& bucket, uint32_t& maxCrossTileID) { - auto thisZoomIndexes = indexes[tileID.overscaledZ]; + const auto& thisZoomIndexes = indexes[tileID.overscaledZ]; auto previousIndex = thisZoomIndexes.find(tileID); if (previousIndex != thisZoomIndexes.end()) { if (previousIndex->second.bucketInstanceId == bucket.bucketInstanceId) { -- cgit v1.2.1 From 29214902b5734361c534a985ac8fe559d8bf5b00 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 30 Apr 2018 12:46:17 -0700 Subject: Fix CXX11ABI builds with clang++ The intention of the `WITH_CXX11ABI` option is to allow the user to control toggle whether to compile against the CXX11ABI in libstdc++ (and whether to download a few specific CXX11ABI compatible mason packages as well). This option is important if you are building binaries (like the node binaries) and you want to support a platform that is older and does not have a recent enough libstdc++ to support the CXX11ABI (like ubuntu:precise, centos6, etc). But this was broken for clang++ builds due to the use of `MAKE_COMPILER_IS_GNUCXX`. That was preventing the correct flags from being set when using clang++ effectively making the option useless and resulting in the build defaulting to whatever the libstdc++-dev headers default is on the system (which varies per linux distribution based on how libstdc++-dev is packaged). This fixes the problem by ensuring that clang++ builds still support toggling control over this option. Note: clang++ > 3.9 supports targeting both the new CXX11ABI in libstdc++ (with `-D_GLIBCXX_USE_CXX11_ABI=1`) and targeting the old one (with `-D_GLIBCXX_USE_CXX11_ABI=0`). --- CMakeLists.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8307191a3f..46aea73356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,13 +15,9 @@ include(cmake/xcode.cmake) if(WITH_CXX11ABI) set(MASON_CXXABI_SUFFIX -cxx11abi) - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) - endif() + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1) else() - if(CMAKE_COMPILER_IS_GNUCXX) - add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) - endif() + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) endif() if(WITH_OSMESA AND WITH_EGL) -- cgit v1.2.1 From 7125ad995564641698ada76141b70aef6eea9520 Mon Sep 17 00:00:00 2001 From: Nick Italiano Date: Wed, 2 May 2018 12:51:37 -0700 Subject: [docs] Updated link for react native npm package (#11819) The link was pointing to the old version of our sdk before we moved it under the Mapbox namespace. This updates the link to point to the correct package under the Mapbox namespace. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5d11bb27e..0d6e731103 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Additional Mapbox GL Native–based libraries for **hybrid applications** are de | Toolkit | Android | iOS | Developer | | ---------------------------------------- | --------|-----|------------ | -| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | +| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/@mapbox/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox | | [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik | | [NativeScript](https://market.nativescript.org/plugins/nativescript-mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik | | [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin | -- cgit v1.2.1 From aabb7be3d62c1cee7d0d5c620f194f50375d14d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 27 Apr 2018 12:06:33 -0700 Subject: [android] Map European Portuguese to system-compatible locale code --- .tx/config | 1 + 1 file changed, 1 insertion(+) diff --git a/.tx/config b/.tx/config index 1de8f78303..272fe45b16 100644 --- a/.tx/config +++ b/.tx/config @@ -46,6 +46,7 @@ type = STRINGS [mapbox-gl-native.stringsxml-android] file_filter = platform/android/MapboxGLAndroidSDK/src/main/res/values-/strings.xml +lang_map = pt_PT: pt-rPT source_file = platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml source_lang = en type = ANDROID -- cgit v1.2.1 From ea59ba8209604c91b76abb31e6be15932bcb3430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Fri, 27 Apr 2018 12:07:28 -0700 Subject: [android, ios, macos] Added Korean localization --- platform/android/CHANGELOG.md | 2 + .../src/main/res/values-ko/strings.xml | 16 ++ .../darwin/resources/ko.lproj/Foundation.strings | 297 +++++++++++++++++++++ .../resources/ko.lproj/Foundation.stringsdict | 48 ++++ platform/ios/CHANGELOG.md | 3 +- platform/ios/app/ko.lproj/Localizable.strings | 0 .../Settings.bundle/ko.lproj/Root.strings | 3 + platform/ios/ios.xcodeproj/project.pbxproj | 13 + .../ios/resources/ko.lproj/Localizable.strings | 117 ++++++++ .../ios/resources/ko.lproj/Localizable.stringsdict | 48 ++++ platform/macos/CHANGELOG.md | 2 +- platform/macos/app/ko.lproj/Localizable.strings | 0 platform/macos/macos.xcodeproj/project.pbxproj | 7 + 13 files changed, 554 insertions(+), 2 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml create mode 100644 platform/darwin/resources/ko.lproj/Foundation.strings create mode 100644 platform/darwin/resources/ko.lproj/Foundation.stringsdict create mode 100644 platform/ios/app/ko.lproj/Localizable.strings create mode 100644 platform/ios/framework/Settings.bundle/ko.lproj/Root.strings create mode 100644 platform/ios/resources/ko.lproj/Localizable.strings create mode 100644 platform/ios/resources/ko.lproj/Localizable.stringsdict create mode 100644 platform/macos/app/ko.lproj/Localizable.strings diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 4df5e79ee7..8a98e78a71 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,7 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.0.2 - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811) + - Add Korean localization [#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792) ## 6.0.1 - April 17, 2018 - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..a292e52517 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml @@ -0,0 +1,16 @@ + + + 지도 나침반. 지도회전를 북쪽으로 재설정합니다. + 속성 정보. 속성 대화를 표시합니다. + 로케이션 뷰. 지도에서 현재 위치를 보여줍니다. + 맵박스로 생성된 지도 표시. 두 손가락으로 드래그하여 화면을 위 아래로 움직이세요. 두 손가락을 이용해 화면을 확대 축소 하세요. + 안드로이드를 위한 맵박스 맵 SDK + 더 나은 맵박스 지도 만들기 + 당신은 익명의 사용 데이터를 제공함으로써, 오픈스트리트맵과 맵박스 향상에 기여하고 있습니다. + 동의 + 비동의 + 추가정보 + 웹 브라우저가 설치 되어 있지 않아, 웹 페이지를 열 수 없습니다. + 제공된 오프라인지역정의가 월드바운즈에 적합하지 않습니다: %s + 원격 측정 설정 + diff --git a/platform/darwin/resources/ko.lproj/Foundation.strings b/platform/darwin/resources/ko.lproj/Foundation.strings new file mode 100644 index 0000000000..68e6cf86fc --- /dev/null +++ b/platform/darwin/resources/ko.lproj/Foundation.strings @@ -0,0 +1,297 @@ +/* Clock position format, long: {hours} o’clock */ +"CLOCK_FMT_LONG" = "%@ 시"; + +/* Clock position format, medium: {hours} o’clock */ +"CLOCK_FMT_MEDIUM" = "%@ 시"; + +/* Clock position format, short: {hours}:00 */ +"CLOCK_FMT_SHORT" = "%@:00"; + +/* East, long */ +"COMPASS_E_LONG" = "동쪽"; + +/* East, short */ +"COMPASS_E_SHORT" = "동"; + +/* East by north, long */ +"COMPASS_EbN_LONG" = "동미북쪽"; + +/* East by north, short */ +"COMPASS_EbN_SHORT" = "동미북"; + +/* East by south, long */ +"COMPASS_EbS_LONG" = "동미남쪽"; + +/* East by south, short */ +"COMPASS_EbS_SHORT" = "동미남"; + +/* East-northeast, long */ +"COMPASS_ENE_LONG" = "동북동쪽"; + +/* East-northeast, short */ +"COMPASS_ENE_SHORT" = "동북동"; + +/* East-southeast, long */ +"COMPASS_ESE_LONG" = "동남동쪽"; + +/* East-southeast, short */ +"COMPASS_ESE_SHORT" = "동남동"; + +/* North, long */ +"COMPASS_N_LONG" = "북쪽"; + +/* North, short */ +"COMPASS_N_SHORT" = "북"; + +/* North by east, long */ +"COMPASS_NbE_LONG" = "북미동쪽"; + +/* North by east, short */ +"COMPASS_NbE_SHORT" = "북미동"; + +/* North by west, long */ +"COMPASS_NbW_LONG" = "북미서쪽"; + +/* North by west, short */ +"COMPASS_NbW_SHORT" = "북미서"; + +/* Northeast, long */ +"COMPASS_NE_LONG" = "북동쪽"; + +/* Northeast, short */ +"COMPASS_NE_SHORT" = "북동"; + +/* Northeast by east, long */ +"COMPASS_NEbE_LONG" = "북동미동쪽"; + +/* Northeast by east, short */ +"COMPASS_NEbE_SHORT" = "북동미동"; + +/* Northeast by north, long */ +"COMPASS_NEbN_LONG" = "북동미북쪽"; + +/* Northeast by north, short */ +"COMPASS_NEbN_SHORT" = "북동미북"; + +/* North-northeast, long */ +"COMPASS_NNE_LONG" = "북북동쪽"; + +/* North-northeast, short */ +"COMPASS_NNE_SHORT" = "북북동"; + +/* North-northwest, long */ +"COMPASS_NNW_LONG" = "북북서쪽"; + +/* North-northwest, short */ +"COMPASS_NNW_SHORT" = "북북서"; + +/* Northwest, long */ +"COMPASS_NW_LONG" = "북서쪽"; + +/* Northwest, short */ +"COMPASS_NW_SHORT" = "북서"; + +/* Northwest by north, long */ +"COMPASS_NWbN_LONG" = "북서미북쪽"; + +/* Northwest by north, short */ +"COMPASS_NWbN_SHORT" = "북서미북"; + +/* Northwest by west, long */ +"COMPASS_NWbW_LONG" = "북서미서쪽"; + +/* Northwest by west, short */ +"COMPASS_NWbW_SHORT" = "북서미서"; + +/* South, long */ +"COMPASS_S_LONG" = "남쪽"; + +/* South, short */ +"COMPASS_S_SHORT" = "남"; + +/* South by east, long */ +"COMPASS_SbE_LONG" = "남미동쪽"; + +/* South by east, short */ +"COMPASS_SbE_SHORT" = "남미동"; + +/* South by west, long */ +"COMPASS_SbW_LONG" = "남미서"; + +/* South by west, short */ +"COMPASS_SbW_SHORT" = "남미서"; + +/* Southeast, long */ +"COMPASS_SE_LONG" = "남동쪽"; + +/* Southeast, short */ +"COMPASS_SE_SHORT" = "남동"; + +/* Southeast by east, long */ +"COMPASS_SEbE_LONG" = "남동미동쪽"; + +/* Southeast by east, short */ +"COMPASS_SEbE_SHORT" = "남동미동"; + +/* Southeast by south, long */ +"COMPASS_SEbS_LONG" = "남동미남쪽"; + +/* Southeast by south, short */ +"COMPASS_SEbS_SHORT" = "남동미남"; + +/* South-southeast, long */ +"COMPASS_SSE_LONG" = "남남동쪽"; + +/* South-southeast, short */ +"COMPASS_SSE_SHORT" = "남남동"; + +/* South-southwest, long */ +"COMPASS_SSW_LONG" = "남남서쪽"; + +/* South-southwest, short */ +"COMPASS_SSW_SHORT" = "남남서"; + +/* Southwest, long */ +"COMPASS_SW_LONG" = "남서쪽"; + +/* Southwest, short */ +"COMPASS_SW_SHORT" = "남서"; + +/* Southwest by south, long */ +"COMPASS_SWbS_LONG" = "남서미남쪽"; + +/* Southwest by south, short */ +"COMPASS_SWbS_SHORT" = "남서미남"; + +/* Southwest by west, long */ +"COMPASS_SWbW_LONG" = "남서미서쪽"; + +/* Southwest by west, short */ +"COMPASS_SWbW_SHORT" = "남서미서"; + +/* West, long */ +"COMPASS_W_LONG" = "서쪽"; + +/* West, short */ +"COMPASS_W_SHORT" = "서"; + +/* West by north, long */ +"COMPASS_WbN_LONG" = "서미북쪽"; + +/* West by north, short */ +"COMPASS_WbN_SHORT" = "서미북"; + +/* West by south, long */ +"COMPASS_WbS_LONG" = "서미남쪽"; + +/* West by south, short */ +"COMPASS_WbS_SHORT" = "서미남"; + +/* West-northwest, long */ +"COMPASS_WNW_LONG" = "서북서쪽"; + +/* West-northwest, short */ +"COMPASS_WNW_SHORT" = "서북서"; + +/* West-southwest, long */ +"COMPASS_WSW_LONG" = "서남서쪽"; + +/* West-southwest, short */ +"COMPASS_WSW_SHORT" = "서남서"; + +/* Degrees format, long */ +"COORD_DEG_LONG" = "%d 도"; + +/* Degrees format, medium: {degrees} */ +"COORD_DEG_MEDIUM" = "%d°"; + +/* Degrees format, short: {degrees} */ +"COORD_DEG_SHORT" = "%d°"; + +/* Coordinate format, long: {degrees}{minutes} */ +"COORD_DM_LONG" = "%1$@ 와 %2$@"; + +/* Coordinate format, medium: {degrees}{minutes} */ +"COORD_DM_MEDIUM" = "%1$@%2$@"; + +/* Coordinate format, short: {degrees}{minutes} */ +"COORD_DM_SHORT" = "%1$@%2$@"; + +/* Coordinate format, long: {degrees}{minutes}{seconds} */ +"COORD_DMS_LONG" = "%1$@, %2$@, 와 %3$@"; + +/* Coordinate format, medium: {degrees}{minutes}{seconds} */ +"COORD_DMS_MEDIUM" = "%1$@%2$@%3$@"; + +/* Coordinate format, short: {degrees}{minutes}{seconds} */ +"COORD_DMS_SHORT" = "%1$@%2$@%3$@"; + +/* East longitude format, long: {longitude} */ +"COORD_E_LONG" = "%@ 동쪽"; + +/* East longitude format, medium: {longitude} */ +"COORD_E_MEDIUM" = "%@ 동쪽"; + +/* East longitude format, short: {longitude} */ +"COORD_E_SHORT" = "%@동"; + +/* Coordinate pair format, long: {latitude}, {longitude} */ +"COORD_FMT_LONG" = "%1$@ by %2$@"; + +/* Coordinate pair format, medium: {latitude}, {longitude} */ +"COORD_FMT_MEDIUM" = "%1$@, %2$@"; + +/* Coordinate pair format, short: {latitude}, {longitude} */ +"COORD_FMT_SHORT" = "%1$@, %2$@"; + +/* Minutes format, long */ +"COORD_MIN_LONG" = "%d 분"; + +/* Minutes format, medium: {minutes} */ +"COORD_MIN_MEDIUM" = "%d′"; + +/* Minutes format, short: {minutes} */ +"COORD_MIN_SHORT" = "%d′"; + +/* North latitude format, long: {latitude} */ +"COORD_N_LONG" = "%@ 북쪽"; + +/* North latitude format, medium: {latitude} */ +"COORD_N_MEDIUM" = "%@ 북쪽"; + +/* North latitude format, short: {latitude} */ +"COORD_N_SHORT" = "%@북"; + +/* South latitude format, long: {latitude} */ +"COORD_S_LONG" = "%@ 남쪽"; + +/* South latitude format, medium: {latitude} */ +"COORD_S_MEDIUM" = "%@ 남쪽"; + +/* South latitude format, short: {latitude} */ +"COORD_S_SHORT" = "%@남"; + +/* Seconds format, long */ +"COORD_SEC_LONG" = "%d 초"; + +/* Seconds format, medium: {seconds} */ +"COORD_SEC_MEDIUM" = "%d″"; + +/* Seconds format, short: {seconds} */ +"COORD_SEC_SHORT" = "%d″"; + +/* West longitude format, long: {longitude} */ +"COORD_W_LONG" = "%@ 서쪽"; + +/* West longitude format, medium: {longitude} */ +"COORD_W_MEDIUM" = "%@ 서쪽"; + +/* West longitude format, short: {longitude} */ +"COORD_W_SHORT" = "%@서"; + +/* OpenStreetMap full name attribution */ +"OSM_FULL_NAME" = "오픈스트리트맵"; + +/* OpenStreetMap short name attribution */ +"OSM_SHORT_NAME" = "오픈스트리트맵"; + diff --git a/platform/darwin/resources/ko.lproj/Foundation.stringsdict b/platform/darwin/resources/ko.lproj/Foundation.stringsdict new file mode 100644 index 0000000000..56d26aa949 --- /dev/null +++ b/platform/darwin/resources/ko.lproj/Foundation.stringsdict @@ -0,0 +1,48 @@ + + + + + COORD_DEG_LONG + + NSStringLocalizedFormatKey + %#@degrees@ + degrees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + other + %d 도 + + + COORD_MIN_LONG + + NSStringLocalizedFormatKey + %#@minutes@ + minutes + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + other + %d 분 + + + COORD_SEC_LONG + + NSStringLocalizedFormatKey + %#@seconds@ + seconds + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + other + %d 초 + + + + diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 3ec701c8ed..ea7f819089 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,8 +8,9 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) -### Other +### Other changes +* Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) diff --git a/platform/ios/app/ko.lproj/Localizable.strings b/platform/ios/app/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..e69de29bb2 diff --git a/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings new file mode 100644 index 0000000000..1fc9f9ce51 --- /dev/null +++ b/platform/ios/framework/Settings.bundle/ko.lproj/Root.strings @@ -0,0 +1,3 @@ +"TELEMETRY_GROUP_TITLE" = "개인 정보 설정"; +"TELEMETRY_SWITCH_TITLE" = "맵박스 텔레메트리"; +"TELEMETRY_GROUP_FOOTER" = "이 설정은 어플리케이션이 익명화된 장소와 사용데이터를 맵박스와 공유하는것을 허용합니다."; diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 80335b8bf0..f5d50522ef 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -1256,6 +1256,12 @@ DAFBD0D21E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = ""; }; DAFBD0D31E3FA7A1000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; DAFBD0D41E3FA7A2000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Root.strings"; sourceTree = ""; }; + DAFEB3742093AE3700A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + DAFEB3752093AE4800A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Foundation.strings; sourceTree = ""; }; + DAFEB3762093AE6800A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Foundation.stringsdict; sourceTree = ""; }; + DAFEB3772093AE7900A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + DAFEB3782093AE9200A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Localizable.stringsdict; sourceTree = ""; }; + DAFEB3792093AEA100A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Root.strings; sourceTree = ""; }; DD0902A21DB18DE700C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = ""; }; DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = ""; }; DD4823721D94AE6C00EB71B7 /* fill_filter_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = fill_filter_style.json; sourceTree = ""; }; @@ -2670,6 +2676,7 @@ he, da, "pt-PT", + ko, ); mainGroup = DA1DC9411CB6C1C2006E619F; productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */; @@ -3214,6 +3221,7 @@ DACBC60B20118ABE00C4D7E2 /* he */, DAD88E07202ACFE800AAA536 /* da */, DA93409B208562EB0059919A /* pt-PT */, + DAFEB3742093AE3700A86A83 /* ko */, ); name = Localizable.strings; sourceTree = ""; @@ -3242,6 +3250,7 @@ DACBC60E20118AFE00C4D7E2 /* he */, DAD88E0C202AD06500AAA536 /* da */, DA93409F208563440059919A /* pt-PT */, + DAFEB3792093AEA100A86A83 /* ko */, ); name = Root.strings; sourceTree = ""; @@ -3268,6 +3277,7 @@ DACBC60D20118ADE00C4D7E2 /* he */, DAD88E0A202AD03C00AAA536 /* da */, DA93409D208563220059919A /* pt-PT */, + DAFEB3772093AE7900A86A83 /* ko */, ); name = Localizable.strings; sourceTree = ""; @@ -3291,6 +3301,7 @@ DA3389661FA3EE28001EA329 /* bg */, DACBC60C20118AD000C4D7E2 /* he */, DAD88E08202AD01300AAA536 /* da */, + DAFEB3752093AE4800A86A83 /* ko */, ); name = Foundation.strings; sourceTree = ""; @@ -3315,6 +3326,7 @@ DA80E9611FE84AEF0065FC9B /* ar */, DAD88E09202AD01F00AAA536 /* da */, DA93409C2085630C0059919A /* pt-PT */, + DAFEB3762093AE6800A86A83 /* ko */, ); name = Foundation.stringsdict; sourceTree = ""; @@ -3345,6 +3357,7 @@ DA3389691FA3EE50001EA329 /* bg */, DAD88E0B202AD04D00AAA536 /* da */, DA93409E208563360059919A /* pt-PT */, + DAFEB3782093AE9200A86A83 /* ko */, ); name = Localizable.stringsdict; sourceTree = ""; diff --git a/platform/ios/resources/ko.lproj/Localizable.strings b/platform/ios/resources/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..40136803d1 --- /dev/null +++ b/platform/ios/resources/ko.lproj/Localizable.strings @@ -0,0 +1,117 @@ +/* Accessibility hint */ +"ANNOTATION_A11Y_HINT" = "추가 정보 보기"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_DESC" = "세션 데이타 작업 실패. 초기 요청: %@"; + +/* No comment provided by engineer. */ +"API_CLIENT_400_REASON" = "상태 코드 %ld"; + +/* No comment provided by engineer. */ +"CANCEL" = "취소"; + +/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */ +"CLOSE_CALLOUT_A11Y_HINT" = "지도로 복귀"; + +/* Accessibility hint */ +"COMPASS_A11Y_HINT" = "정북쪽으로 지도 회전"; + +/* Accessibility label */ +"COMPASS_A11Y_LABEL" = "나침반"; + +/* Compass abbreviation for north */ +"COMPASS_NORTH" = "북"; + +/* Instructions in Interface Builder designable; {key}, {plist file name} */ +"DESIGNABLE" = "맵박스에의한 지도를 출력하기 위해, %1$@ 를 %2$@에 당신의 접근 토큰으로 으로 설정하십시오. \n\n자세한 정보를 찾으시는 경우, 확인:"; + +/* Setup documentation URL display string; keep as short as possible */ +"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk"; + +/* Accessibility hint */ +"INFO_A11Y_HINT" = "크레딧, 피드백 폼, 기타 보기"; + +/* Accessibility label */ +"INFO_A11Y_LABEL" = "이 맵에 관하여"; + +/* List separator */ +"LIST_SEPARATOR" = ", "; + +/* User-friendly error description */ +"LOAD_MAP_FAILED_DESC" = "알수 없는 에러로 인해 맵 로드에 실패하였습니다."; + +/* User-friendly error description */ +"LOAD_STYLE_FAILED_DESC" = "형식이 로드되지 않아, 맵 로드에 실패하였습니다."; + +/* Accessibility label */ +"LOGO_A11Y_LABEL" = "맵박스"; + +/* Accessibility label */ +"MAP_A11Y_LABEL" = "지도"; + +/* Map accessibility value; {number of visible annotations} */ +"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld 주석 볼 수 있는."; + +/* Map accessibility value; {list of visible places} */ +"MAP_A11Y_VALUE_PLACES" = "가시거리 범위: %@."; + +/* Map accessibility value; {number of visible roads} */ +"MAP_A11Y_VALUE_ROADS" = "%ld 도로 볼 수 있는."; + +/* Map accessibility value; {zoom level} */ +"MAP_A11Y_VALUE_ZOOM" = "확대 %dx."; + +/* User-friendly error description */ +"PARSE_STYLE_FAILED_DESC" = "형식에 오류가 발생하여 맵 로드에 실패하였습니다."; + +/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */ +"ROAD_DIRECTION_A11Y_FMT" = "%1$@ to %2$@"; + +/* Accessibility value indicating that a road is a divided road (dual carriageway) */ +"ROAD_DIVIDED_A11Y_VALUE" = "분리 도로"; + +/* Accessibility value indicating that a road is a one-way road */ +"ROAD_ONEWAY_A11Y_VALUE" = "일방 통행"; + +/* String format for accessibility value for road feature; {route number} */ +"ROAD_REF_A11Y_FMT" = "경로 %@"; + +/* Action sheet title */ +"SDK_NAME" = "iOS를 위한 맵박스 지도 SDK"; + +/* Developer-only SDK update notification; {latest version, in format x.x.x} */ +"SDK_UPDATE_AVAILABLE" = "iOS버전 맵박스 지도 %@가 사용 가능합니다:"; + +/* User-friendly error description */ +"STYLE_NOT_FOUND_DESC" = "형식을 찾을 수 없거나 호환이 되지 않아, 맵 로드에 실패하였습니다."; + +/* Telemetry prompt message */ +"TELEMETRY_DISABLED_MSG" = "당신은 익명의 사용 데이타를 제공함으로써, 오픈스트리트맵과 맵박스의 향상에 기여하고 있습니다."; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_OFF" = "참여하지 마십시오"; + +/* Telemetry prompt button */ +"TELEMETRY_DISABLED_ON" = "참여"; + +/* Telemetry prompt message */ +"TELEMETRY_ENABLED_MSG" = "당신은 익명의 사용 데이타를 제공함으로써, 오픈스트리트맵과 맵박스의 향상에 기여하고 있습니다."; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_OFF" = "참여 중지"; + +/* Telemetry prompt button */ +"TELEMETRY_ENABLED_ON" = "참여 계속"; + +/* Telemetry prompt button */ +"TELEMETRY_MORE" = "좀 더 말해보세요"; + +/* Action in attribution sheet */ +"TELEMETRY_NAME" = "맵박스 텔레메트리"; + +/* Telemetry prompt title */ +"TELEMETRY_TITLE" = "더 나은 맵박스 지도 만들기"; + +/* Default user location annotation title */ +"USER_DOT_TITLE" = "당신의 위치"; + diff --git a/platform/ios/resources/ko.lproj/Localizable.stringsdict b/platform/ios/resources/ko.lproj/Localizable.stringsdict new file mode 100644 index 0000000000..fdde2639c6 --- /dev/null +++ b/platform/ios/resources/ko.lproj/Localizable.stringsdict @@ -0,0 +1,48 @@ + + + + + MAP_A11Y_VALUE_ANNOTATIONS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + other + %d 주석 보이기 + + + MAP_A11Y_VALUE_ROADS + + NSStringLocalizedFormatKey + %#@count@ + count + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + ld + other + %d 도로 보이기 + + + MAP_A11Y_VALUE_ZOOM + + NSStringLocalizedFormatKey + %#@level@ + level + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + other + 확대 %dx + + + + diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 1285de7c3b..cbe0206629 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -6,7 +6,7 @@ * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) -### Other +### Other changes * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) diff --git a/platform/macos/app/ko.lproj/Localizable.strings b/platform/macos/app/ko.lproj/Localizable.strings new file mode 100644 index 0000000000..e69de29bb2 diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index c36b71403d..845aaf8e06 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -637,6 +637,9 @@ DAF2571D201902A500367EF5 /* MGLHillshadeStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLHillshadeStyleLayerTests.mm; sourceTree = ""; }; DAFBD0D51E3FA969000CD6BF /* zh-Hant */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; DAFBD0D61E3FA983000CD6BF /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Foundation.strings"; sourceTree = ""; }; + DAFEB3702093ACBF00A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; + DAFEB3722093ACDA00A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Foundation.strings; sourceTree = ""; }; + DAFEB3732093ACE400A86A83 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Foundation.stringsdict; sourceTree = ""; }; DD0902AF1DB1AC6400C5BDCE /* MGLNetworkConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNetworkConfiguration.m; sourceTree = ""; }; DD0902B01DB1AC6400C5BDCE /* MGLNetworkConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLNetworkConfiguration.h; sourceTree = ""; }; DD58A4C71D822C6200E1F038 /* MGLExpressionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLExpressionTests.mm; path = ../../darwin/test/MGLExpressionTests.mm; sourceTree = ""; }; @@ -1421,6 +1424,7 @@ he, da, "pt-PT", + ko, ); mainGroup = DA839E891CC2E3400062CAFB; productRefGroup = DA839E931CC2E3400062CAFB /* Products */; @@ -1661,6 +1665,7 @@ DACBC6082011885800C4D7E2 /* he */, DAD88E03202ACF5900AAA536 /* da */, DA934097208562590059919A /* pt-PT */, + DAFEB3702093ACBF00A86A83 /* ko */, ); name = Localizable.strings; sourceTree = ""; @@ -1739,6 +1744,7 @@ DA3389621FA3EDEF001EA329 /* bg */, DACBC6092011888C00C4D7E2 /* he */, DAD88E04202ACF7C00AAA536 /* da */, + DAFEB3722093ACDA00A86A83 /* ko */, ); name = Foundation.strings; sourceTree = ""; @@ -1763,6 +1769,7 @@ DA80E95F1FE84A540065FC9B /* ar */, DAD88E05202ACF8200AAA536 /* da */, DA934098208562870059919A /* pt-PT */, + DAFEB3732093ACE400A86A83 /* ko */, ); name = Foundation.stringsdict; sourceTree = ""; -- cgit v1.2.1 From 1c22374eac69fb2f95b3f6a60e6bfed449508af0 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 2 May 2018 12:34:50 -0700 Subject: [ios, macos] Avoid implicit capture of MBGLOfflineRegionObserver this pointer MBGLOfflineRegionObserver is owned by the offline database thread, and might be destroyed by the time the dispatch_async completes. Instead of implicitly capturing this, capture a copy of the MBGLOfflinePack weak pointer. --- platform/darwin/src/MGLOfflinePack.mm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 60a7b55531..5e555d425e 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -227,19 +227,22 @@ NSError *MGLErrorFromResponseError(mbgl::Response::Error error) { @end void MBGLOfflineRegionObserver::statusChanged(mbgl::OfflineRegionStatus status) { + __weak MBGLOfflinePack *_pack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack offlineRegionStatusDidChange:status]; + [_pack offlineRegionStatusDidChange:status]; }); } void MBGLOfflineRegionObserver::responseError(mbgl::Response::Error error) { + __weak MBGLOfflinePack *_pack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveError:MGLErrorFromResponseError(error)]; + [_pack didReceiveError:MGLErrorFromResponseError(error)]; }); } void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) { + __weak MBGLOfflinePack *_pack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveMaximumAllowedMapboxTiles:limit]; + [_pack didReceiveMaximumAllowedMapboxTiles:limit]; }); } -- cgit v1.2.1 From 60ac08afae3d3ca0e94ce4f1ce7dfb98c25e2345 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 2 May 2018 13:00:17 -0700 Subject: Fixup --- platform/darwin/src/MGLOfflinePack.mm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 5e555d425e..4653021a58 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -227,22 +227,22 @@ NSError *MGLErrorFromResponseError(mbgl::Response::Error error) { @end void MBGLOfflineRegionObserver::statusChanged(mbgl::OfflineRegionStatus status) { - __weak MBGLOfflinePack *_pack = pack; + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [_pack offlineRegionStatusDidChange:status]; + [weakPack offlineRegionStatusDidChange:status]; }); } void MBGLOfflineRegionObserver::responseError(mbgl::Response::Error error) { - __weak MBGLOfflinePack *_pack = pack; + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [_pack didReceiveError:MGLErrorFromResponseError(error)]; + [weakPack didReceiveError:MGLErrorFromResponseError(error)]; }); } void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) { - __weak MBGLOfflinePack *_pack = pack; + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [_pack didReceiveMaximumAllowedMapboxTiles:limit]; + [weakPack didReceiveMaximumAllowedMapboxTiles:limit]; }); } -- cgit v1.2.1 From 8b713d937343c8c4aafb1387279c1eec11e5ab95 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 2 May 2018 12:34:50 -0700 Subject: [ios, macos] Avoid implicit capture of MBGLOfflineRegionObserver this pointer MBGLOfflineRegionObserver is owned by the offline database thread, and might be destroyed by the time the dispatch_async completes. Instead of implicitly capturing this, capture a copy of the MBGLOfflinePack weak pointer. --- platform/darwin/src/MGLOfflinePack.mm | 9 ++++++--- platform/ios/CHANGELOG.md | 1 + platform/macos/CHANGELOG.md | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 60a7b55531..4653021a58 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -227,19 +227,22 @@ NSError *MGLErrorFromResponseError(mbgl::Response::Error error) { @end void MBGLOfflineRegionObserver::statusChanged(mbgl::OfflineRegionStatus status) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack offlineRegionStatusDidChange:status]; + [weakPack offlineRegionStatusDidChange:status]; }); } void MBGLOfflineRegionObserver::responseError(mbgl::Response::Error error) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveError:MGLErrorFromResponseError(error)]; + [weakPack didReceiveError:MGLErrorFromResponseError(error)]; }); } void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveMaximumAllowedMapboxTiles:limit]; + [weakPack didReceiveMaximumAllowedMapboxTiles:limit]; }); } diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ea7f819089..8f62d28ae3 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -14,6 +14,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) +* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) ## 4.0.0 - April 19, 2018 diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index cbe0206629..62c2931d43 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -9,6 +9,7 @@ ### Other changes * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) ## 0.7.0 - April 19, 2018 -- cgit v1.2.1 From 0bd9eac1744124e135df03753baa7395917ccf23 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Wed, 2 May 2018 12:34:50 -0700 Subject: [ios, macos] Avoid implicit capture of MBGLOfflineRegionObserver this pointer MBGLOfflineRegionObserver is owned by the offline database thread, and might be destroyed by the time the dispatch_async completes. Instead of implicitly capturing this, capture a copy of the MBGLOfflinePack weak pointer. --- platform/darwin/src/MGLOfflinePack.mm | 9 ++++++--- platform/ios/CHANGELOG.md | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/platform/darwin/src/MGLOfflinePack.mm b/platform/darwin/src/MGLOfflinePack.mm index 60a7b55531..4653021a58 100644 --- a/platform/darwin/src/MGLOfflinePack.mm +++ b/platform/darwin/src/MGLOfflinePack.mm @@ -227,19 +227,22 @@ NSError *MGLErrorFromResponseError(mbgl::Response::Error error) { @end void MBGLOfflineRegionObserver::statusChanged(mbgl::OfflineRegionStatus status) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack offlineRegionStatusDidChange:status]; + [weakPack offlineRegionStatusDidChange:status]; }); } void MBGLOfflineRegionObserver::responseError(mbgl::Response::Error error) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveError:MGLErrorFromResponseError(error)]; + [weakPack didReceiveError:MGLErrorFromResponseError(error)]; }); } void MBGLOfflineRegionObserver::mapboxTileCountLimitExceeded(uint64_t limit) { + __weak MGLOfflinePack *weakPack = pack; dispatch_async(dispatch_get_main_queue(), ^{ - [pack didReceiveMaximumAllowedMapboxTiles:limit]; + [weakPack didReceiveMaximumAllowedMapboxTiles:limit]; }); } diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index e7277a2e10..438186c2b9 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## 3.7.7 + +* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) + ## 3.7.6 - March 12, 2018 * Fixed an issue where full-resolution tiles could fail to replace lower-resolution placeholders. ([#11227](https://github.com/mapbox/mapbox-gl-native/pull/11227)) -- cgit v1.2.1 From 844a8244097c18ae12e9c14baaaaad8a2f7ef436 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Thu, 3 May 2018 09:11:42 -0400 Subject: [ios, macos] Fix broken link to predicates and expressions guide. (#11818) --- platform/darwin/docs/guides/For Style Authors.md.ejs | 4 ++-- platform/ios/docs/guides/For Style Authors.md | 4 ++-- platform/macos/docs/guides/For Style Authors.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index 2ae6602224..b0ded7bc03 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -272,7 +272,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -427,5 +427,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index b3beea8540..9a6e4428eb 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -280,7 +280,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -412,5 +412,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 22182c7810..73c4f1105a 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -267,7 +267,7 @@ Each property representing a layout or paint attribute is set to an but you create the former using a very different syntax. `NSExpression`’s format string syntax is reminiscent of a spreadsheet formula or an expression in a database query. See the -“[Predicates and Expressions](Predicates and Expressions.md)” guide for an +“[Predicates and Expressions](predicates-and-expressions.html)” guide for an overview of the expression support in this SDK. This SDK no longer supports style functions; use expressions instead. @@ -405,5 +405,5 @@ In style JSON | In the format string `["any", f0, …, fn]` | `p0 OR … OR pn` `["none", f0, …, fn]` | `NOT (p0 OR … OR pn)` -See the “[Predicates and Expressions](Predicates and Expressions.md)” guide for +See the “[Predicates and Expressions](predicates-and-expressions.html)” guide for a full description of the supported operators and operand types. -- cgit v1.2.1 From f6da3ba9be27ff9b279730603c517c4cb5e57007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 3 May 2018 21:48:58 +0200 Subject: iOS: Add custom layer example to debugging app (#7734) --- platform/darwin/app/LimeGreenStyleLayer.h | 5 ++ platform/darwin/app/LimeGreenStyleLayer.m | 58 +++++++++++++++++++++ platform/ios/app/MBXViewController.m | 12 +++++ platform/ios/ios.xcodeproj/project.pbxproj | 18 ++++--- .../ios/uitest/ios-tests.xcodeproj/project.pbxproj | 4 ++ platform/macos/app/LimeGreenStyleLayer.h | 5 -- platform/macos/app/LimeGreenStyleLayer.m | 60 ---------------------- platform/macos/macos.xcodeproj/project.pbxproj | 12 ++--- 8 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 platform/darwin/app/LimeGreenStyleLayer.h create mode 100644 platform/darwin/app/LimeGreenStyleLayer.m delete mode 100644 platform/macos/app/LimeGreenStyleLayer.h delete mode 100644 platform/macos/app/LimeGreenStyleLayer.m diff --git a/platform/darwin/app/LimeGreenStyleLayer.h b/platform/darwin/app/LimeGreenStyleLayer.h new file mode 100644 index 0000000000..35480963a4 --- /dev/null +++ b/platform/darwin/app/LimeGreenStyleLayer.h @@ -0,0 +1,5 @@ +#import + +@interface LimeGreenStyleLayer : MGLOpenGLStyleLayer + +@end diff --git a/platform/darwin/app/LimeGreenStyleLayer.m b/platform/darwin/app/LimeGreenStyleLayer.m new file mode 100644 index 0000000000..98e96381b6 --- /dev/null +++ b/platform/darwin/app/LimeGreenStyleLayer.m @@ -0,0 +1,58 @@ +#import "LimeGreenStyleLayer.h" +@import GLKit; + +@implementation LimeGreenStyleLayer { + GLuint _program; + GLuint _vertexShader; + GLuint _fragmentShader; + GLuint _buffer; + GLuint _aPos; +} + +- (void)didMoveToMapView:(MGLMapView *)mapView { + static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 1, 1); }"; + static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 0.5, 0, 0.5); }"; + + _program = glCreateProgram(); + _vertexShader = glCreateShader(GL_VERTEX_SHADER); + _fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(_vertexShader, 1, &vertexShaderSource, NULL); + glCompileShader(_vertexShader); + glAttachShader(_program, _vertexShader); + glShaderSource(_fragmentShader, 1, &fragmentShaderSource, NULL); + glCompileShader(_fragmentShader); + glAttachShader(_program, _fragmentShader); + glLinkProgram(_program); + _aPos = glGetAttribLocation(_program, "a_pos"); + + GLfloat triangle[] = { 0, 0.5, 0.5, -0.5, -0.5, -0.5 }; + glGenBuffers(1, &_buffer); + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), triangle, GL_STATIC_DRAW); +} + +- (void)drawInMapView:(MGLMapView *)mapView withContext:(MGLStyleLayerDrawingContext)context { + glUseProgram(_program); + glBindBuffer(GL_ARRAY_BUFFER, _buffer); + glEnableVertexAttribArray(_aPos); + glVertexAttribPointer(_aPos, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); +} + +- (void)willMoveFromMapView:(MGLMapView *)mapView { + if (!_program) { + return; + } + + glDeleteBuffers(1, &_buffer); + glDetachShader(_program, _vertexShader); + glDetachShader(_program, _fragmentShader); + glDeleteShader(_vertexShader); + glDeleteShader(_fragmentShader); + glDeleteProgram(_program); +} + +@end diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 391af5ea05..c3585fbeae 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -5,6 +5,7 @@ #import "MBXOfflinePacksTableViewController.h" #import "MBXAnnotationView.h" #import "MBXUserLocationAnnotationView.h" +#import "LimeGreenStyleLayer.h" #import "MBXEmbeddedMapViewController.h" #import @@ -82,6 +83,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) { MBXSettingsRuntimeStylingRasterTileSource, MBXSettingsRuntimeStylingImageSource, MBXSettingsRuntimeStylingRouteLine, + MBXSettingsRuntimeStylingAddLimeGreenTriangleLayer, MBXSettingsRuntimeStylingDDSPolygon, MBXSettingsRuntimeStylingCustomLatLonGrid, }; @@ -371,6 +373,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { @"Style Raster Tile Source", @"Style Image Source", @"Add Route Line", + @"Add Lime Green Triangle Layer", @"Dynamically Style Polygon", @"Add Custom Lat/Lon Grid", ]]; @@ -559,6 +562,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { case MBXSettingsRuntimeStylingRouteLine: [self styleRouteLine]; break; + case MBXSettingsRuntimeStylingAddLimeGreenTriangleLayer: + [self styleAddLimeGreenTriangleLayer]; + break; case MBXSettingsRuntimeStylingDDSPolygon: [self stylePolygonWithDDS]; break; @@ -1443,6 +1449,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { [self.mapView.style addLayer:routeLayer]; } +- (void)styleAddLimeGreenTriangleLayer +{ + LimeGreenStyleLayer *layer = [[LimeGreenStyleLayer alloc] initWithIdentifier:@"mbx-custom"]; + [self.mapView.style addLayer:layer]; +} + - (void)stylePolygonWithDDS { CLLocationCoordinate2D leftCoords[] = { {37.73081027834234, -122.49412536621094}, diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index f5d50522ef..c7708b2d37 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -158,6 +158,9 @@ 35E79F201D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; }; 35E79F211D41266300957B9E /* MGLStyleLayer_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */; }; 36F1153D1D46080700878E1A /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 36F1153B1D46080700878E1A /* libmbgl-core.a */; }; + 3E6465D62065767A00685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */; }; + 3E8770612074297100B7E842 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; + 3E8E82F120744B4100E7BE97 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */; }; 3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */; }; 3EA934623AD0000B7D99C3FB /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; 3EA9363147E77DD29FA06063 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */; }; @@ -266,9 +269,6 @@ 40F887701D7A1E58008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */; }; 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40FDA76A1CCAAA6800442548 /* MBXAnnotationView.m */; }; - 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5549A0371EF1D86B00073113 /* libmbgl-core.a */; }; - 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; - 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 554180411D2E97DE00012372 /* OpenGLES.framework */; }; 556660CA1E1BF3A900E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; 556660D81E1D085500E2C41B /* MGLVersionNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 556660D71E1D085500E2C41B /* MGLVersionNumber.m */; }; 556660DB1E1D8E8D00E2C41B /* MGLFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 556660C91E1BF3A900E2C41B /* MGLFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -829,6 +829,9 @@ 35E79F1F1D41266300957B9E /* MGLStyleLayer_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleLayer_Private.h; sourceTree = ""; }; 36F1153B1D46080700878E1A /* libmbgl-core.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-core.a"; path = "build/Debug-iphoneos/libmbgl-core.a"; sourceTree = ""; }; 36F1153C1D46080700878E1A /* libmbgl-platform-ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmbgl-platform-ios.a"; path = "build/Debug-iphoneos/libmbgl-platform-ios.a"; sourceTree = ""; }; + 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = ""; }; + 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = ""; }; + 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-core.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3EA931BC4F087E166D538F21 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = ""; }; 3EA9337830C7738BF7F5493C /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = ""; }; 400532FF1DB0862B0069F638 /* NSArray+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+MGLAdditions.h"; sourceTree = ""; }; @@ -1297,7 +1300,6 @@ buildActionMask = 2147483647; files = ( DA8847D91CBAF91600AB86E3 /* Mapbox.framework in Frameworks */, - 5549A0391EF2877100073113 /* OpenGLES.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1305,7 +1307,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5549A0381EF1D86B00073113 /* libmbgl-core.a in Frameworks */, + 3E8E82F120744B4100E7BE97 /* libmbgl-core.a in Frameworks */, DA2E88561CC036F400F24E7B /* Mapbox.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1336,7 +1338,7 @@ buildActionMask = 2147483647; files = ( DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */, - 5549A03A1EF2877500073113 /* OpenGLES.framework in Frameworks */, + 3E8770612074297100B7E842 /* OpenGLES.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1730,6 +1732,8 @@ DA1DC94C1CB6C1C2006E619F /* Demo App */ = { isa = PBXGroup; children = ( + 3E6465D52065767A00685536 /* LimeGreenStyleLayer.h */, + 3E6465D42065767A00685536 /* LimeGreenStyleLayer.m */, DA1DC9501CB6C1C2006E619F /* MBXAppDelegate.h */, DA1DC9981CB6E054006E619F /* MBXAppDelegate.m */, 40FDA7691CCAAA6800442548 /* MBXAnnotationView.h */, @@ -1769,6 +1773,7 @@ DA1DC9921CB6DF24006E619F /* Frameworks */ = { isa = PBXGroup; children = ( + 3E8E82F020744B4100E7BE97 /* libmbgl-core.a */, 55D120AD1F791018004B6D81 /* libmbgl-loop-darwin.a */, 55D120AB1F791015004B6D81 /* libmbgl-filesource.a */, 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */, @@ -2824,6 +2829,7 @@ 927FBCFC1F4DAA8300F8BF1F /* MBXSnapshotsViewController.m in Sources */, DA1DC99B1CB6E064006E619F /* MBXViewController.m in Sources */, 40FDA76B1CCAAA6800442548 /* MBXAnnotationView.m in Sources */, + 3E6465D62065767A00685536 /* LimeGreenStyleLayer.m in Sources */, 632281DF1E6F855900D75A5D /* MBXEmbeddedMapViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj index c9744d41bf..108139e6e3 100644 --- a/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj +++ b/platform/ios/uitest/ios-tests.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3EAB52CE206ACE7E008BFE2D /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3EAB52CF206ACE7E008BFE2D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 96567A221B0E84CD00D78776 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = SOURCE_ROOT; }; 96567A301B0E8BB900D78776 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = SOURCE_ROOT; }; DA180EF11DD1A4DF000A211D /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = "OHHTTPStubs/OHHTTPStubs/build/Debug-iphoneos/OHHTTPStubs.framework"; sourceTree = ""; }; @@ -122,6 +124,8 @@ DD043325196DB9BC00E6F39D /* Frameworks */ = { isa = PBXGroup; children = ( + 3EAB52CF206ACE7E008BFE2D /* OpenGLES.framework */, + 3EAB52CE206ACE7E008BFE2D /* libmbgl-loop-darwin.a */, DA180EF11DD1A4DF000A211D /* OHHTTPStubs.framework */, DA9C551B1CD9DFA7000A15C6 /* libKIF.a */, DA482C7F1C12582600772FE3 /* Mapbox.framework */, diff --git a/platform/macos/app/LimeGreenStyleLayer.h b/platform/macos/app/LimeGreenStyleLayer.h deleted file mode 100644 index 35480963a4..0000000000 --- a/platform/macos/app/LimeGreenStyleLayer.h +++ /dev/null @@ -1,5 +0,0 @@ -#import - -@interface LimeGreenStyleLayer : MGLOpenGLStyleLayer - -@end diff --git a/platform/macos/app/LimeGreenStyleLayer.m b/platform/macos/app/LimeGreenStyleLayer.m deleted file mode 100644 index 40c336cd98..0000000000 --- a/platform/macos/app/LimeGreenStyleLayer.m +++ /dev/null @@ -1,60 +0,0 @@ -#import "LimeGreenStyleLayer.h" - -#include -#include - -@implementation LimeGreenStyleLayer { - GLuint _program; - GLuint _vertexShader; - GLuint _fragmentShader; - GLuint _buffer; - GLuint _aPos; -} - -- (void)didMoveToMapView:(MGLMapView *)mapView { - static const GLchar *vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }"; - static const GLchar *fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }"; - - _program = glCreateProgram(); - _vertexShader = glCreateShader(GL_VERTEX_SHADER); - _fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - - glShaderSource(_vertexShader, 1, &vertexShaderSource, NULL); - glCompileShader(_vertexShader); - glAttachShader(_program, _vertexShader); - glShaderSource(_fragmentShader, 1, &fragmentShaderSource, NULL); - glCompileShader(_fragmentShader); - glAttachShader(_program, _fragmentShader); - glLinkProgram(_program); - _aPos = glGetAttribLocation(_program, "a_pos"); - - GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 }; - glGenBuffers(1, &_buffer); - glBindBuffer(GL_ARRAY_BUFFER, _buffer); - glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW); -} - -- (void)drawInMapView:(MGLMapView *)mapView withContext:(MGLStyleLayerDrawingContext)context { - glUseProgram(_program); - glBindBuffer(GL_ARRAY_BUFFER, _buffer); - glEnableVertexAttribArray(_aPos); - glVertexAttribPointer(_aPos, 2, GL_FLOAT, GL_FALSE, 0, NULL); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DEPTH_TEST); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -- (void)willMoveFromMapView:(MGLMapView *)mapView { - if (!_program) { - return; - } - - glDeleteBuffers(1, &_buffer); - glDetachShader(_program, _vertexShader); - glDetachShader(_program, _fragmentShader); - glDeleteShader(_vertexShader); - glDeleteShader(_fragmentShader); - glDeleteProgram(_program); -} - -@end diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 845aaf8e06..4bfcd29905 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -62,6 +62,7 @@ 35C6DF871E214C1800ACA483 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */; }; 35D65C5A1D65AD5500722C23 /* NSDate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */; }; 35D65C5B1D65AD5500722C23 /* NSDate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */; }; + 3E6465D9206576A900685536 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */; }; 3EA9317388DC9A0BF46B7674 /* MGLRendererConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */; }; 3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */; }; 4031ACFC1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */; }; @@ -163,7 +164,6 @@ DAA998FB1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; DAA998FC1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */; }; DAA999011E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */; }; - DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */; }; DAC25FCA200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */; }; DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */; }; DACA8622201920BE00E9693A /* MGLRasterDEMSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -348,6 +348,8 @@ 35C6DF861E214C1800ACA483 /* MGLDistanceFormatterTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLDistanceFormatterTests.m; path = ../../darwin/test/MGLDistanceFormatterTests.m; sourceTree = ""; }; 35D65C581D65AD5500722C23 /* NSDate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+MGLAdditions.h"; sourceTree = ""; }; 35D65C591D65AD5500722C23 /* NSDate+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+MGLAdditions.mm"; sourceTree = ""; }; + 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LimeGreenStyleLayer.m; path = ../../darwin/app/LimeGreenStyleLayer.m; sourceTree = ""; }; + 3E6465D8206576A900685536 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LimeGreenStyleLayer.h; path = ../../darwin/app/LimeGreenStyleLayer.h; sourceTree = ""; }; 3EA9369A4C46957566058822 /* MGLRendererConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRendererConfiguration.h; sourceTree = ""; }; 3EA93B1B0864609938506E12 /* MGLRendererConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRendererConfiguration.mm; sourceTree = ""; }; 4031ACFB1E9EB3C100A3EA26 /* MGLMapViewDelegateIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MGLMapViewDelegateIntegrationTests.swift; sourceTree = ""; }; @@ -520,8 +522,6 @@ DAA998F91E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFillExtrusionStyleLayer.h; sourceTree = ""; }; DAA998FA1E9F545C002E6EA6 /* MGLFillExtrusionStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayer.mm; sourceTree = ""; }; DAA999001E9F5EC5002E6EA6 /* MGLFillExtrusionStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFillExtrusionStyleLayerTests.mm; sourceTree = ""; }; - DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LimeGreenStyleLayer.h; sourceTree = ""; }; - DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LimeGreenStyleLayer.m; sourceTree = ""; }; DAC25FC9200FD5E2009BE98E /* NSExpression+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLPrivateAdditions.h"; sourceTree = ""; }; DAC2ABC41CC6D343006D18C4 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = ""; }; DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLRasterDEMSource.h; sourceTree = ""; }; @@ -821,8 +821,8 @@ DA839E961CC2E3400062CAFB /* AppDelegate.m */, DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */, DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */, - DAB2CCE31DF632ED001B2FE1 /* LimeGreenStyleLayer.h */, - DAB2CCE41DF632ED001B2FE1 /* LimeGreenStyleLayer.m */, + 3E6465D8206576A900685536 /* LimeGreenStyleLayer.h */, + 3E6465D7206576A800685536 /* LimeGreenStyleLayer.m */, DAE6C2E51CC3050F00DB3429 /* LocationCoordinate2DTransformer.h */, DAE6C2E61CC3050F00DB3429 /* LocationCoordinate2DTransformer.m */, DA839E9B1CC2E3400062CAFB /* MapDocument.h */, @@ -1490,11 +1490,11 @@ DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */, DAE6C2ED1CC3050F00DB3429 /* DroppedPinAnnotation.m in Sources */, DAE6C2EE1CC3050F00DB3429 /* LocationCoordinate2DTransformer.m in Sources */, - DAB2CCE51DF632ED001B2FE1 /* LimeGreenStyleLayer.m in Sources */, DAE6C2F11CC3050F00DB3429 /* TimeIntervalTransformer.m in Sources */, DACB0C391E18DFFD005DDBEA /* MGLStyle+MBXAdditions.m in Sources */, DA839E9A1CC2E3400062CAFB /* main.m in Sources */, DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */, + 3E6465D9206576A900685536 /* LimeGreenStyleLayer.m in Sources */, DAE6C2F01CC3050F00DB3429 /* OfflinePackNameValueTransformer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; -- cgit v1.2.1 From 27b21363e62c105db0b040b4c5a5ef31170ebd30 Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Fri, 27 Apr 2018 17:18:29 -0700 Subject: [core] Only run placement for first layer per SymbolBucket Native version of mapbox/mapbox-gl-js#6548. Port of mapbox/mapbox-gl-js#6550. Prevents symbols that share the same layout properties from colliding against each other. Bump GL JS pin to get regression test. Rename "bucketName" -> "bucketLeaderID" to make it clearer what it represents. --- mapbox-gl-js | 2 +- src/mbgl/geometry/feature_index.cpp | 10 +++++----- src/mbgl/geometry/feature_index.hpp | 10 +++++----- src/mbgl/layout/symbol_layout.cpp | 6 +++--- src/mbgl/layout/symbol_layout.hpp | 2 +- src/mbgl/renderer/buckets/symbol_bucket.cpp | 2 ++ src/mbgl/renderer/buckets/symbol_bucket.hpp | 3 +++ src/mbgl/text/cross_tile_symbol_index.cpp | 4 ++++ src/mbgl/text/placement.cpp | 9 +++++++++ test/gl/bucket.test.cpp | 3 ++- test/text/cross_tile_symbol_index.test.cpp | 24 ++++++++++++++---------- 11 files changed, 49 insertions(+), 26 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index b0ba8bded5..5f3bdb9b00 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit b0ba8bded514e791ba8bd7eda4a513a762c5e5ae +Subproject commit 5f3bdb9b00915d702ae9b967c5ebc15ab33ba653 diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 520fd313a2..fdd9558d0b 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -25,14 +25,14 @@ FeatureIndex::FeatureIndex(std::unique_ptr tileData_) void FeatureIndex::insert(const GeometryCollection& geometries, std::size_t index, const std::string& sourceLayerName, - const std::string& bucketName) { + const std::string& bucketLeaderID) { for (const auto& ring : geometries) { auto envelope = mapbox::geometry::envelope(ring); if (envelope.min.x < util::EXTENT && envelope.min.y < util::EXTENT && envelope.max.x >= 0 && envelope.max.y >= 0) { - grid.insert(IndexedSubfeature(index, sourceLayerName, bucketName, sortIndex++), + grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++), {convertPoint(envelope.min), convertPoint(envelope.max)}); } } @@ -141,7 +141,7 @@ void FeatureIndex::addFeature( std::unique_ptr sourceLayer; std::unique_ptr geometryTileFeature; - for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) { const RenderLayer* renderLayer = getRenderLayer(layerID); if (!renderLayer) { continue; @@ -190,8 +190,8 @@ optional FeatureIndex::translateQueryGeometry( return translated; } -void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs) { - bucketLayerIDs[bucketName] = layerIDs; +void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector& layerIDs) { + bucketLayerIDs[bucketLeaderID] = layerIDs; } } // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index cc91791d36..739c1f282f 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -25,7 +25,7 @@ public: IndexedSubfeature(std::size_t index_, std::string sourceLayerName_, std::string bucketName_, size_t sortIndex_) : index(index_) , sourceLayerName(std::move(sourceLayerName_)) - , bucketName(std::move(bucketName_)) + , bucketLeaderID(std::move(bucketName_)) , sortIndex(sortIndex_) , bucketInstanceId(0) {} @@ -34,13 +34,13 @@ public: IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_) : index(other.index) , sourceLayerName(other.sourceLayerName) - , bucketName(other.bucketName) + , bucketLeaderID(other.bucketLeaderID) , sortIndex(other.sortIndex) , bucketInstanceId(bucketInstanceId_) {} size_t index; std::string sourceLayerName; - std::string bucketName; + std::string bucketLeaderID; size_t sortIndex; // Only used for symbol features @@ -53,7 +53,7 @@ public: const GeometryTileData* getData() { return tileData.get(); } - void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName); + void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketLeaderID); void query( std::unordered_map>& result, @@ -74,7 +74,7 @@ public: const float bearing, const float pixelsToTileUnits); - void setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs); + void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector& layerIDs); std::unordered_map> lookupSymbolFeatures( const std::vector& symbolFeatures, diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index d3126bba45..b2f6fd450f 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -42,7 +42,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, std::unique_ptr sourceLayer_, ImageDependencies& imageDependencies, GlyphDependencies& glyphDependencies) - : bucketName(layers.at(0)->getID()), + : bucketLeaderID(layers.at(0)->getID()), sourceLayer(std::move(sourceLayer_)), overscaling(parameters.tileID.overscaleFactor()), zoom(parameters.tileID.overscaledZ), @@ -294,7 +294,7 @@ void SymbolLayout::addFeature(const std::size_t index, : layout.get(); const float textRepeatDistance = symbolSpacing / 2; - IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketName, symbolInstances.size()); + IndexedSubfeature indexedFeature(feature.index, sourceLayer->getName(), bucketLeaderID, symbolInstances.size()); auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) { // https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers @@ -420,7 +420,7 @@ std::unique_ptr SymbolLayout::place(const bool showCollisionBoxes) const bool mayOverlap = layout.get() || layout.get() || layout.get() || layout.get(); - auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, std::move(symbolInstances)); + auto bucket = std::make_unique(layout, layerPaintProperties, textSize, iconSize, zoom, sdfIcons, iconsNeedLinear, mayOverlap, bucketLeaderID, std::move(symbolInstances)); for (SymbolInstance &symbolInstance : bucket->symbolInstances) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index c93d8f4106..43b577859f 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -43,7 +43,7 @@ public: std::map> layerPaintProperties; - const std::string bucketName; + const std::string bucketLeaderID; std::vector symbolInstances; private: diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp index 3d40a1714d..4fe03eb453 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.cpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -18,11 +18,13 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo bool sdfIcons_, bool iconsNeedLinear_, bool sortFeaturesByY_, + const std::string bucketName_, const std::vector&& symbolInstances_) : layout(std::move(layout_)), sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), sortFeaturesByY(sortFeaturesByY_), + bucketLeaderID(std::move(bucketName_)), symbolInstances(std::move(symbolInstances_)), textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp index e52d18372d..e4aaf5ba30 100644 --- a/src/mbgl/renderer/buckets/symbol_bucket.hpp +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -47,6 +47,7 @@ public: bool sdfIcons, bool iconsNeedLinear, bool sortFeaturesByY, + const std::string bucketLeaderID, const std::vector&&); void upload(gl::Context&) override; @@ -64,6 +65,8 @@ public: const bool iconsNeedLinear; const bool sortFeaturesByY; + const std::string bucketLeaderID; + optional sortedAngle; bool staticUploaded = false; diff --git a/src/mbgl/text/cross_tile_symbol_index.cpp b/src/mbgl/text/cross_tile_symbol_index.cpp index 01a4a02b4e..b0c3511ce3 100644 --- a/src/mbgl/text/cross_tile_symbol_index.cpp +++ b/src/mbgl/text/cross_tile_symbol_index.cpp @@ -153,6 +153,10 @@ bool CrossTileSymbolIndex::addLayer(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only add this layer if it's the "group leader" for the bucket + continue; + } if (!symbolBucket.bucketInstanceId) { symbolBucket.bucketInstanceId = ++maxBucketInstanceId; diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index 43e8ff4f93..9883a1f456 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -56,6 +56,11 @@ void Placement::placeLayer(RenderSymbolLayer& symbolLayer, const mat4& projMatri auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only place this layer if it's the "group leader" for the bucket + continue; + } auto& layout = symbolBucket.layout; @@ -230,6 +235,10 @@ void Placement::updateLayerOpacities(RenderSymbolLayer& symbolLayer) { auto bucket = renderTile.tile.getBucket(*symbolLayer.baseImpl); assert(dynamic_cast(bucket)); SymbolBucket& symbolBucket = *reinterpret_cast(bucket); + if (symbolBucket.bucketLeaderID != symbolLayer.getID()) { + // Only update opacities this layer if it's the "group leader" for the bucket + continue; + } updateBucketOpacities(symbolBucket, seenCrossTileIDs); } } diff --git a/test/gl/bucket.test.cpp b/test/gl/bucket.test.cpp index 8393fd130d..9b28b2e01a 100644 --- a/test/gl/bucket.test.cpp +++ b/test/gl/bucket.test.cpp @@ -110,10 +110,11 @@ TEST(Buckets, SymbolBucket) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; std::vector symbolInstances; gl::Context context; - SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(symbolInstances) }; + SymbolBucket bucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(symbolInstances) }; ASSERT_FALSE(bucket.hasIconData()); ASSERT_FALSE(bucket.hasTextData()); ASSERT_FALSE(bucket.hasCollisionBoxData()); diff --git a/test/text/cross_tile_symbol_index.test.cpp b/test/text/cross_tile_symbol_index.test.cpp index 794c2b59a1..5d255a6105 100644 --- a/test/text/cross_tile_symbol_index.test.cpp +++ b/test/text/cross_tile_symbol_index.test.cpp @@ -25,12 +25,13 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); mainInstances.push_back(makeSymbolInstance(2000, 2000, u"Toronto")); - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(mainID, mainBucket, maxCrossTileID); @@ -45,7 +46,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { childInstances.push_back(makeSymbolInstance(2000, 2000, u"Windsor")); childInstances.push_back(makeSymbolInstance(3000, 3000, u"Toronto")); childInstances.push_back(makeSymbolInstance(4001, 4001, u"Toronto")); - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(childID, childBucket, maxCrossTileID); @@ -61,7 +62,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { OverscaledTileID parentID(5, 0, 5, 4, 4); std::vector parentInstances; parentInstances.push_back(makeSymbolInstance(500, 500, u"Detroit")); - SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(parentInstances) }; + SymbolBucket parentBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(parentInstances) }; parentBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(parentID, parentBucket, maxCrossTileID); @@ -77,7 +78,7 @@ TEST(CrossTileSymbolLayerIndex, addBucket) { std::vector grandchildInstances; grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Detroit")); grandchildInstances.push_back(makeSymbolInstance(4000, 4000, u"Windsor")); - SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(grandchildInstances) }; + SymbolBucket grandchildBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(grandchildInstances) }; grandchildBucket.bucketInstanceId = ++maxBucketInstanceId; index.addBucket(grandchildID, grandchildBucket, maxCrossTileID); @@ -98,17 +99,18 @@ TEST(CrossTileSymbolLayerIndex, resetIDs) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"Detroit")); - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; OverscaledTileID childID(7, 0, 7, 16, 16); std::vector childInstances; childInstances.push_back(makeSymbolInstance(2000, 2000, u"Detroit")); - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns a new id @@ -137,12 +139,13 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID mainID(6, 0, 6, 8, 8); std::vector mainInstances; mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A mainInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B - SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(mainInstances) }; + SymbolBucket mainBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(mainInstances) }; mainBucket.bucketInstanceId = ++maxBucketInstanceId; OverscaledTileID childID(7, 0, 7, 16, 16); @@ -150,7 +153,7 @@ TEST(CrossTileSymbolLayerIndex, noDuplicatesWithinZoomLevel) { childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // A' childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // B' childInstances.push_back(makeSymbolInstance(2000, 2000, u"")); // C' - SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(childInstances) }; + SymbolBucket childBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(childInstances) }; childBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns new ids @@ -174,19 +177,20 @@ TEST(CrossTileSymbolLayerIndex, bucketReplacement) { bool sdfIcons = false; bool iconsNeedLinear = false; bool sortFeaturesByY = false; + std::string bucketLeaderID = "test"; OverscaledTileID tileID(6, 0, 6, 8, 8); std::vector firstInstances; firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A firstInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B - SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(firstInstances) }; + SymbolBucket firstBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(firstInstances) }; firstBucket.bucketInstanceId = ++maxBucketInstanceId; std::vector secondInstances; secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // A' secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // B' secondInstances.push_back(makeSymbolInstance(1000, 1000, u"")); // C' - SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, std::move(secondInstances) }; + SymbolBucket secondBucket { layout, {}, 16.0f, 1.0f, 0, sdfIcons, iconsNeedLinear, sortFeaturesByY, bucketLeaderID, std::move(secondInstances) }; secondBucket.bucketInstanceId = ++maxBucketInstanceId; // assigns new ids -- cgit v1.2.1 From aadaa1c95b3f52483d1513efdff0b6d06c6c5420 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Thu, 3 May 2018 17:36:02 -0400 Subject: [ios] Prepare ios-v3.7.7 release (#11828) Bring ios-v4.0.0 changelog backward --- platform/ios/CHANGELOG.md | 72 +++++++++++++++++++++- .../ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 438186c2b9..392df1d1ea 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,9 +2,77 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 3.7.7 +## 3.7.7 - May 3, 2018 -* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) +* Fixed a crash when removing an `MGLOfflinePack`. ([#11821](https://github.com/mapbox/mapbox-gl-native/issues/11821)) + +## 4.0.0 - April 19, 2018 + +The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iOS deployment version will increase to iOS 9.0 in a future release. + +### Packaging + +* Removed support for 32-bit simulators. ([#10962](https://github.com/mapbox/mapbox-gl-native/pull/10962)) +* Added Danish, Hebrew, and European Portuguese localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134), [#11695](https://github.com/mapbox/mapbox-gl-native/pull/11695)) +* Removed methods, properties, and constants that had been deprecated as of v3.7.6. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205), [#11681](https://github.com/mapbox/mapbox-gl-native/pull/11681)) +* Refined certain Swift interfaces by converting them from class methods to class properties. ([#11674](https://github.com/mapbox/mapbox-gl-native/pull/11674)) +* Revamped the “Adding Points to a Map” guide. ([#11496](https://github.com/mapbox/mapbox-gl-native/pull/11496)) + +### Style layers + +* The layout and paint properties on subclasses of `MGLStyleLayer` are now of type `NSExpression` instead of `MGLStyleValue`. A new “Predicates and Expressions” guide provides an overview of the supported operators, which include arithmetic and conditional operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726)) +* A style can now display a heatmap layer that visualizes a point data distribution. You can customize the appearance at runtime using the `MGLHeatmapStyleLayer` class. ([#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046)) +* A style can now display a smooth hillshading layer and customize its appearance at runtime using the `MGLHillshadeStyleLayer` class. Hillshading is based on a rasterized digital elevation model supplied by the `MGLRasterDEMSource` class. ([#10642](https://github.com/mapbox/mapbox-gl-native/pull/10642)) +* You can now set the `MGLVectorStyleLayer.predicate` property to a predicate that contains arithmetic and calls to built-in `NSExpression` functions. You may need to cast a feature attribute key to `NSString` or `NSNumber` before comparing it to a string or number. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587)) +* Replaced the `MGLStyle.localizesLabels` property with an `-[MGLStyle localizeLabelsIntoLocale:]` method that allows you to specify the language to localize into. Also added an `-[NSExpression(MGLAdditions) mgl_expressionLocalizedIntoLocale:]` method for localizing an individual value used with `MGLSymbolStyleLayer.text`. ([#11651](https://github.com/mapbox/mapbox-gl-native/pull/11651)) +* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850)) +* Changes to the `MGLStyleLayer.minimumZoomLevel` and `MGLStyleLayer.maximumZoomLevel` properties take effect immediately. ([#11399](https://github.com/mapbox/mapbox-gl-native/pull/11399)) + +### Content sources + +* Renamed `MGLRasterSource` to `MGLRasterTileSource` and `MGLVectorSource` to `MGLVectorTileSource`. ([#11568](https://github.com/mapbox/mapbox-gl-native/pull/11568)) +* Added an `MGLComputedShapeSource` class that allows applications to supply vector data to a style layer on a per-tile basis. ([#9983](https://github.com/mapbox/mapbox-gl-native/pull/9983)) +* Properties such as `MGLSymbolStyleLayer.iconAllowsOverlap` and `MGLSymbolStyleLayer.iconIgnoresPlacement` now account for symbols in other sources. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) + +### Map rendering + +* Improved the reliability of collision detection between symbols near the edges of tiles, as well as between symbols when the map is tilted. It is no longer necessary to enable `MGLSymbolStyleLayer.symbolAvoidsEdges` to prevent symbols in adjacent tiles from overlapping with each other. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Symbols can fade in and out as the map pans, rotates, or tilts. ([#10436](https://github.com/mapbox/mapbox-gl-native/pull/10436)) +* Fixed an issue preventing a dynamically-added `MGLRasterStyleLayer` from drawing until the map pans. ([#10270](https://github.com/mapbox/mapbox-gl-native/pull/10270)) +* Fixed an issue preventing `MGLImageSource`s from drawing on the map when the map is zoomed in and tilted. ([#10677](https://github.com/mapbox/mapbox-gl-native/pull/10677)) +* Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984)) +* Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001)) +* Reduced memory usage by clearing in-memory tile cache before entering background. ([#11197](https://github.com/mapbox/mapbox-gl-native/pull/11197)) +* Fixed an issue where symbols with empty labels would always be hidden. ([#11206](https://github.com/mapbox/mapbox-gl-native/pull/11206)) +* Fixed an issue where a tilted map could flicker while displaying rotating symbols. ([#11488](https://github.com/mapbox/mapbox-gl-native/pull/11488)) +* Increased the maximum width of labels by a factor of two. ([#11508](https://github.com/mapbox/mapbox-gl-native/pull/11508)) + +### Annotations + +* Changed the default value of `MGLAnnotationView.scalesWithViewingDistance` to `NO`, to improve performance. If your use case involves many annotation views, consider keeping this property disabled. ([#11636](https://github.com/mapbox/mapbox-gl-native/pull/11636)) +* Fixed an issue preventing `MGLAnnotationImage.image` from being updated. ([#10372](https://github.com/mapbox/mapbox-gl-native/pull/10372)) +* Improved performance of `MGLAnnotationView`-backed annotations that have `scalesWithViewingDistance` enabled. ([#10951](https://github.com/mapbox/mapbox-gl-native/pull/10951)) +* Fixed an issue where tapping a group of annotations may not have selected the nearest annotation. ([#11438](https://github.com/mapbox/mapbox-gl-native/pull/11438)) +* The `MGLMapView.selectedAnnotations` property (backed by `-[MGLMapView setSelectedAnnotations:]`) now selects annotations that are off-screen. ([#9790](https://github.com/mapbox/mapbox-gl-native/issues/9790)) +* The `animated` parameter to `-[MGLMapView selectAnnotation:animated:]` now controls whether the annotation and its callout are brought on-screen. If `animated` is `NO` then the annotation is selected if offscreen, but the map is not panned. Currently only point annotations are supported. Setting the `MGLMapView.selectedAnnotations` property now animates. ([#3249](https://github.com/mapbox/mapbox-gl-native/issues/3249)) +* Fixed a crash when rapidly adding and removing annotations. ([#11551](https://github.com/mapbox/mapbox-gl-native/issues/11551), [#11575](https://github.com/mapbox/mapbox-gl-native/issues/11575)) +* Marked protocol method `-[MGLCalloutView presentCalloutFromRect:inView:constrainedToView:animated:]` as unavailable. Use `-[MGLCalloutView presentCalloutFromRect:inView:constrainedToRect:animated:]` instead. ([#11738](https://github.com/mapbox/mapbox-gl-native/pull/11738)) + +### Map snapshots + +* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585)) + +### Other changes + +* The `-[MGLMapView convertRect:toCoordinateBoundsFromView:]` method and the `MGLMapView.visibleCoordinateBounds` property’s getter now indicate that the coordinate bounds straddles the antimeridian by extending one side beyond ±180 degrees longitude. ([#11265](https://github.com/mapbox/mapbox-gl-native/pull/11265)) +* Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897)) +* Fixed an issue preventing labels from being transliterated when VoiceOver was enabled on iOS 10._x_ and below. ([#10881](https://github.com/mapbox/mapbox-gl-native/pull/10881)) +* 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)) +* Reduced offline download sizes for styles with symbol layers that render only icons, and no text. ([#11055](https://github.com/mapbox/mapbox-gl-native/pull/11055)) +* Added haptic feedback that occurs when the user rotates the map to due north, configurable via `MGLMapView.hapticFeedbackEnabled`. ([#10847](https://github.com/mapbox/mapbox-gl-native/pull/10847)) +* Added `MGLMapView.showsScale` as the recommended way to show the scale bar. This property can be set directly in Interface Builder. ([#11335](https://github.com/mapbox/mapbox-gl-native/pull/11335)) +* Fixed an issue where the scale bar would not appear until the map had moved. ([#11335](https://github.com/mapbox/mapbox-gl-native/pull/11335)) ## 3.7.6 - March 12, 2018 diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 268b7b9da6..9d714ccb6a 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.6' + version = '3.7.7' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index a1b2441d29..ebf758cbf6 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.6' + version = '3.7.7' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 7e696a23cf..d8ac87363b 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.6' + version = '3.7.7' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From c3346f473ac7e3d49219c8dbc381d92f8fe3f017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 4 May 2018 01:31:43 +0200 Subject: [core] Don't crash when attribute count is exceeded --- src/mbgl/gl/attribute.cpp | 14 +++- src/mbgl/gl/attribute.hpp | 18 +++-- src/mbgl/gl/context.cpp | 10 ++- src/mbgl/gl/context.hpp | 4 +- src/mbgl/gl/program.hpp | 2 +- src/mbgl/gl/vertex_array.cpp | 6 +- src/mbgl/gl/vertex_array.hpp | 13 +--- src/mbgl/programs/program.hpp | 34 +++++--- src/mbgl/programs/symbol_program.hpp | 50 +++++++----- .../renderer/layers/render_background_layer.cpp | 63 ++++++++------- src/mbgl/renderer/layers/render_circle_layer.cpp | 37 ++++++--- .../layers/render_fill_extrusion_layer.cpp | 91 ++++++++++++++++------ src/mbgl/renderer/layers/render_fill_layer.cpp | 66 +++++++++++----- src/mbgl/renderer/layers/render_heatmap_layer.cpp | 68 ++++++++++++---- .../renderer/layers/render_hillshade_layer.cpp | 65 +++++++++++----- src/mbgl/renderer/layers/render_line_layer.cpp | 27 +++++-- src/mbgl/renderer/layers/render_raster_layer.cpp | 33 +++++--- src/mbgl/renderer/layers/render_symbol_layer.cpp | 31 +++++--- src/mbgl/renderer/render_layer.cpp | 29 +++++++ src/mbgl/renderer/render_layer.hpp | 11 +++ src/mbgl/renderer/render_tile.cpp | 70 ++++++++++------- src/mbgl/renderer/renderer_impl.cpp | 24 ++++-- src/mbgl/renderer/sources/render_image_source.cpp | 28 ++++--- 23 files changed, 557 insertions(+), 237 deletions(-) diff --git a/src/mbgl/gl/attribute.cpp b/src/mbgl/gl/attribute.cpp index bb5b2ddc34..b2d05fe665 100644 --- a/src/mbgl/gl/attribute.cpp +++ b/src/mbgl/gl/attribute.cpp @@ -1,14 +1,20 @@ #include +#include #include namespace mbgl { namespace gl { -void bindAttributeLocation(ProgramID id, AttributeLocation location, const char* name) { - if (location >= MAX_ATTRIBUTES) { - throw gl::Error("too many vertex attributes"); +void bindAttributeLocation(Context& context, ProgramID id, AttributeLocation location, const char* name) { + // We're using sequentially numberered attribute locations starting with 0. Therefore we can use + // the location as a proxy for the number of attributes. + if (location >= context.maximumVertexBindingCount) { + // Don't bind the location on this hardware since it exceeds the limit (otherwise we'd get + // an OpenGL error). This means we'll see rendering errors, and possibly slow rendering due + // to unbound attributes. + } else { + MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); } - MBGL_CHECK_ERROR(glBindAttribLocation(id, location, name)); } std::set getActiveAttributes(ProgramID id) { diff --git a/src/mbgl/gl/attribute.hpp b/src/mbgl/gl/attribute.hpp index fa6c2ddeab..3763f0a583 100644 --- a/src/mbgl/gl/attribute.hpp +++ b/src/mbgl/gl/attribute.hpp @@ -17,8 +17,6 @@ namespace mbgl { namespace gl { -static constexpr std::size_t MAX_ATTRIBUTES = 8; - template struct DataTypeOf; template <> struct DataTypeOf< int8_t> : std::integral_constant {}; template <> struct DataTypeOf : std::integral_constant {}; @@ -45,7 +43,7 @@ public: } }; -using AttributeBindingArray = std::array, MAX_ATTRIBUTES>; +using AttributeBindingArray = std::vector>; /* gl::Attribute manages the binding of a vertex buffer to a GL program attribute. @@ -214,7 +212,8 @@ const std::size_t Vertex::attributeOffsets[5] = { } // namespace detail -void bindAttributeLocation(ProgramID, AttributeLocation, const char * name); +class Context; +void bindAttributeLocation(Context&, ProgramID, AttributeLocation, const char * name); std::set getActiveAttributes(ProgramID); template @@ -231,13 +230,13 @@ public: using Vertex = detail::Vertex; - static Locations bindLocations(const ProgramID& id) { + static Locations bindLocations(Context& context, const ProgramID& id) { std::set activeAttributes = getActiveAttributes(id); AttributeLocation location = 0; auto maybeBindLocation = [&](const char* name) -> optional { if (activeAttributes.count(name)) { - bindAttributeLocation(id, location, name); + bindAttributeLocation(context, id, location, name); return location++; } else { return {}; @@ -277,6 +276,7 @@ public: static AttributeBindingArray toBindingArray(const Locations& locations, const Bindings& bindings) { AttributeBindingArray result; + result.resize(sizeof...(As)); auto maybeAddBinding = [&] (const optional& location, const optional& binding) { @@ -289,6 +289,12 @@ public: return result; } + + static uint32_t activeBindingCount(const Bindings& bindings) { + uint32_t result = 0; + util::ignore({ ((result += bool(bindings.template get())), 0)... }); + return result; + } }; namespace detail { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index ba44adb42b..c6352d3e84 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -94,7 +94,13 @@ static_assert(underlying_type(BufferUsage::DynamicDraw) == GL_DYNAMIC_DRAW, "Ope static_assert(std::is_same::value, "OpenGL type mismatch"); -Context::Context() = default; +Context::Context() + : maximumVertexBindingCount([] { + GLint value; + MBGL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value)); + return value; + }()) { +} Context::~Context() { if (cleanupOnDestruction) { @@ -351,7 +357,7 @@ VertexArray Context::createVertexArray() { VertexArrayID id = 0; MBGL_CHECK_ERROR(vertexArray->genVertexArrays(1, &id)); UniqueVertexArray vao(std::move(id), { this }); - return { UniqueVertexArrayState(new VertexArrayState(std::move(vao), *this), VertexArrayStateDeleter { true })}; + return { UniqueVertexArrayState(new VertexArrayState(std::move(vao)), VertexArrayStateDeleter { true })}; } else { // On GL implementations which do not support vertex arrays, attribute bindings are global state. // So return a VertexArray which shares our global state tracking and whose deleter is a no-op. diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 67624288e2..d95311115e 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -226,7 +226,7 @@ public: State vertexBuffer; State bindVertexArray { *this }; - VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }), *this }; + VertexArrayState globalVertexArrayState { UniqueVertexArray(0, { this }) }; State pixelStorePack; State pixelStoreUnpack; @@ -239,6 +239,8 @@ public: #endif // MBGL_USE_GLES2 bool supportsHalfFloatTextures = false; + const uint32_t maximumVertexBindingCount; + static constexpr const uint32_t minimumRequiredVertexBindingCount = 8; private: State stencilFunc; diff --git a/src/mbgl/gl/program.hpp b/src/mbgl/gl/program.hpp index af02ad3d54..f33501cd11 100644 --- a/src/mbgl/gl/program.hpp +++ b/src/mbgl/gl/program.hpp @@ -35,7 +35,7 @@ public: context.createProgram(context.createShader(ShaderType::Vertex, vertexSource), context.createShader(ShaderType::Fragment, fragmentSource))), uniformsState((context.linkProgram(program), Uniforms::bindLocations(program))), - attributeLocations(Attributes::bindLocations(program)) { + attributeLocations(Attributes::bindLocations(context, program)) { // Re-link program after manually binding only active attributes in Attributes::bindLocations context.linkProgram(program); diff --git a/src/mbgl/gl/vertex_array.cpp b/src/mbgl/gl/vertex_array.cpp index 68a500ac45..d552a8292e 100644 --- a/src/mbgl/gl/vertex_array.cpp +++ b/src/mbgl/gl/vertex_array.cpp @@ -9,7 +9,11 @@ void VertexArray::bind(Context& context, BufferID indexBuffer, const AttributeBi context.bindVertexArray = state->vertexArray; state->indexBuffer = indexBuffer; - for (AttributeLocation location = 0; location < MAX_ATTRIBUTES; ++location) { + state->bindings.reserve(bindings.size()); + for (AttributeLocation location = 0; location < bindings.size(); ++location) { + if (state->bindings.size() <= location) { + state->bindings.emplace_back(context, AttributeLocation(location)); + } state->bindings[location] = bindings[location]; } } diff --git a/src/mbgl/gl/vertex_array.hpp b/src/mbgl/gl/vertex_array.hpp index 46c67017bb..604754f672 100644 --- a/src/mbgl/gl/vertex_array.hpp +++ b/src/mbgl/gl/vertex_array.hpp @@ -15,9 +15,8 @@ class Context; class VertexArrayState { public: - VertexArrayState(UniqueVertexArray vertexArray_, Context& context) - : vertexArray(std::move(vertexArray_)), - bindings(makeBindings(context, std::make_index_sequence())) { + VertexArrayState(UniqueVertexArray vertexArray_) + : vertexArray(std::move(vertexArray_)) { } void setDirty() { @@ -31,13 +30,7 @@ public: State indexBuffer; using AttributeState = State; - std::array bindings; - -private: - template - std::array makeBindings(Context& context, std::index_sequence) { - return {{ AttributeState { context, I }... }}; - } + std::vector bindings; }; class VertexArrayStateDeleter { diff --git a/src/mbgl/programs/program.hpp b/src/mbgl/programs/program.hpp index bcdb270b9c..4d5de05337 100644 --- a/src/mbgl/programs/program.hpp +++ b/src/mbgl/programs/program.hpp @@ -46,26 +46,38 @@ public: Shaders::fragmentSource)) { } + static typename AllUniforms::Values computeAllUniformValues( + const UniformValues& uniformValues, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties, + float currentZoom) { + return uniformValues + .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); + } + + static typename Attributes::Bindings computeAllAttributeBindings( + const gl::VertexBuffer& layoutVertexBuffer, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties) { + return LayoutAttributes::bindings(layoutVertexBuffer) + .concat(paintPropertyBinders.attributeBindings(currentProperties)); + } + + static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) { + return Attributes::activeBindingCount(allAttributeBindings); + } + template void draw(gl::Context& context, DrawMode drawMode, gl::DepthMode depthMode, gl::StencilMode stencilMode, gl::ColorMode colorMode, - const UniformValues& uniformValues, - const gl::VertexBuffer& layoutVertexBuffer, const gl::IndexBuffer& indexBuffer, const SegmentVector& segments, - const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::PossiblyEvaluated& currentProperties, - float currentZoom, + const typename AllUniforms::Values& allUniformValues, + const typename Attributes::Bindings& allAttributeBindings, const std::string& layerID) { - typename AllUniforms::Values allUniformValues = uniformValues - .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); - - typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer) - .concat(paintPropertyBinders.attributeBindings(currentProperties)); - for (auto& segment : segments) { auto vertexArrayIt = segment.vertexArrays.find(layerID); diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 9b5037ed9f..51f9a48f7f 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -278,35 +278,45 @@ public: Shaders::fragmentSource)) { } + static typename AllUniforms::Values computeAllUniformValues( + const UniformValues& uniformValues, + const SymbolSizeBinder& symbolSizeBinder, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties, + float currentZoom) { + return uniformValues.concat(symbolSizeBinder.uniformValues(currentZoom)) + .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); + } + + static typename Attributes::Bindings computeAllAttributeBindings( + const gl::VertexBuffer& layoutVertexBuffer, + const gl::VertexBuffer& dynamicLayoutVertexBuffer, + const gl::VertexBuffer& opacityVertexBuffer, + const PaintPropertyBinders& paintPropertyBinders, + const typename PaintProperties::PossiblyEvaluated& currentProperties) { + assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount && + layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount); + return LayoutAttributes::bindings(layoutVertexBuffer) + .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) + .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer)) + .concat(paintPropertyBinders.attributeBindings(currentProperties)); + } + + static uint32_t activeBindingCount(const typename Attributes::Bindings& allAttributeBindings) { + return Attributes::activeBindingCount(allAttributeBindings); + } + template void draw(gl::Context& context, DrawMode drawMode, gl::DepthMode depthMode, gl::StencilMode stencilMode, gl::ColorMode colorMode, - const UniformValues& uniformValues, - const gl::VertexBuffer& layoutVertexBuffer, - const gl::VertexBuffer& dynamicLayoutVertexBuffer, - const gl::VertexBuffer& opacityVertexBuffer, - const SymbolSizeBinder& symbolSizeBinder, const gl::IndexBuffer& indexBuffer, const SegmentVector& segments, - const PaintPropertyBinders& paintPropertyBinders, - const typename PaintProperties::PossiblyEvaluated& currentProperties, - float currentZoom, + const typename AllUniforms::Values& allUniformValues, + const typename Attributes::Bindings& allAttributeBindings, const std::string& layerID) { - typename AllUniforms::Values allUniformValues = uniformValues - .concat(symbolSizeBinder.uniformValues(currentZoom)) - .concat(paintPropertyBinders.uniformValues(currentZoom, currentProperties)); - - typename Attributes::Bindings allAttributeBindings = LayoutAttributes::bindings(layoutVertexBuffer) - .concat(SymbolDynamicLayoutAttributes::bindings(dynamicLayoutVertexBuffer)) - .concat(SymbolOpacityAttributes::bindings(opacityVertexBuffer)) - .concat(paintPropertyBinders.attributeBindings(currentProperties)); - - assert(layoutVertexBuffer.vertexCount == dynamicLayoutVertexBuffer.vertexCount && - layoutVertexBuffer.vertexCount == opacityVertexBuffer.vertexCount); - for (auto& segment : segments) { auto vertexArrayIt = segment.vertexArrays.find(layerID); diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index aebc4cc9aa..f22402ac98 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -49,6 +49,35 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { const Properties<>::PossiblyEvaluated properties; const BackgroundProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto draw = [&](auto& program, auto&& uniformValues) { + const auto allUniformValues = program.computeAllUniformValues( + std::move(uniformValues), + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, program.activeBindingCount(allAttributeBindings)); + + program.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + allUniformValues, + allAttributeBindings, + getID() + ); + }; + if (!evaluated.get().to.empty()) { optional imagePosA = parameters.imageManager.getPattern(evaluated.get().from); optional imagePosB = parameters.imageManager.getPattern(evaluated.get().to); @@ -59,12 +88,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { parameters.imageManager.bind(parameters.context, 0); for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.backgroundPattern.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + draw( + parameters.programs.backgroundPattern, BackgroundPatternUniforms::values( parameters.matrixForTile(tileID), evaluated.get(), @@ -74,36 +99,18 @@ void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { evaluated.get(), tileID, parameters.state - ), - parameters.staticData.tileVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - getID() + ) ); } } else { for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { - parameters.programs.background.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + draw( + parameters.programs.background, BackgroundProgram::UniformValues { uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, uniforms::u_color::Value{ evaluated.get() }, uniforms::u_opacity::Value{ evaluated.get() }, - }, - parameters.staticData.tileVertexBuffer, - parameters.staticData.quadTriangleIndexBuffer, - parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - getID() + } ); } } diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp index 6092ff5452..4c96137143 100644 --- a/src/mbgl/renderer/layers/render_circle_layer.cpp +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -59,14 +59,11 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { assert(dynamic_cast(tile.tile.getBucket(*baseImpl))); CircleBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); - parameters.programs.circle.get(evaluated).draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - parameters.mapMode != MapMode::Continuous - ? parameters.stencilModeForClipping(tile.clip) - : gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + auto& programInstance = parameters.programs.circle.get(evaluated); + + const auto allUniformValues = programInstance.computeAllUniformValues( CircleProgram::UniformValues { uniforms::u_matrix::Value{ tile.translatedMatrix(evaluated.get(), @@ -82,12 +79,30 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, uniforms::u_pitch_with_map::Value{ pitchWithMap } }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.mapMode != MapMode::Continuous + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); } diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp index fbd6160e8a..00ab05858f 100644 --- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -68,24 +68,53 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* parameters.context.setStencilMode(gl::StencilMode::disabled()); parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + auto draw = [&](auto& programInstance, const auto& tileBucket, auto&& uniformValues) { + const auto& paintPropertyBinders = tileBucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *tileBucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + *tileBucket.indexBuffer, + tileBucket.triangleSegments, + allUniformValues, + allAttributeBindings, + getID()); + }; + if (evaluated.get().from.empty()) { for (const RenderTile& tile : renderTiles) { assert(dynamic_cast(tile.tile.getBucket(*baseImpl))); FillExtrusionBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); - parameters.programs.fillExtrusion.get(evaluated).draw( - parameters.context, gl::Triangles(), - parameters.depthModeFor3D(gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + draw( + parameters.programs.fillExtrusion.get(evaluated), + bucket, FillExtrusionUniforms::values( tile.translatedClipMatrix(evaluated.get(), evaluated.get(), parameters.state), - parameters.state, parameters.evaluatedLight), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), - getID()); + parameters.state, + parameters.evaluatedLight + ) + ); } } else { optional imagePosA = @@ -104,10 +133,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* FillExtrusionBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); - parameters.programs.fillExtrusionPattern.get(evaluated).draw( - parameters.context, gl::Triangles(), - parameters.depthModeFor3D(gl::DepthMode::ReadWrite), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + draw( + parameters.programs.fillExtrusionPattern.get(evaluated), + bucket, FillExtrusionPatternUniforms::values( tile.translatedClipMatrix(evaluated.get(), evaluated.get(), @@ -115,10 +143,9 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, evaluated.get(), tile.id, parameters.state, -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, - parameters.evaluatedLight), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, - bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), - getID()); + parameters.evaluatedLight + ) + ); } } @@ -131,19 +158,39 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource* matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); const Properties<>::PossiblyEvaluated properties; + const ExtrusionTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; + + auto& programInstance = parameters.programs.extrusionTexture; - parameters.programs.extrusionTexture.draw( - parameters.context, gl::Triangles(), gl::DepthMode::disabled(), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + const auto allUniformValues = programInstance.computeAllUniformValues( ExtrusionTextureProgram::UniformValues{ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, uniforms::u_image::Value{ 0 }, - uniforms::u_opacity::Value{ evaluated.get() } }, + uniforms::u_opacity::Value{ evaluated.get() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.extrusionTextureVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.extrusionTextureSegments, - ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, - parameters.state.getZoom(), getID()); + allUniformValues, + allAttributeBindings, + getID()); } } diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp index 22cb9563c1..2607ebc170 100644 --- a/src/mbgl/renderer/layers/render_fill_layer.cpp +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -69,12 +69,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { const auto& depthMode, const auto& indexBuffer, const auto& segments) { - program.get(evaluated).draw( - parameters.context, - drawMode, - depthMode, - parameters.stencilModeForClipping(tile.clip), - parameters.colorModeForRenderPass(), + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( FillProgram::UniformValues { uniforms::u_matrix::Value{ tile.translatedMatrix(evaluated.get(), @@ -83,12 +82,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { }, uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), indexBuffer, segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -139,12 +154,11 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { const auto& depthMode, const auto& indexBuffer, const auto& segments) { - program.get(evaluated).draw( - parameters.context, - drawMode, - depthMode, - parameters.stencilModeForClipping(tile.clip), - parameters.colorModeForRenderPass(), + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( FillPatternUniforms::values( tile.translatedMatrix(evaluated.get(), evaluated.get(), @@ -157,12 +171,28 @@ void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { tile.id, parameters.state ), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), indexBuffer, segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp index 4f2e899220..86884b01a4 100644 --- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp +++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp @@ -92,23 +92,38 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { ? parameters.stencilModeForClipping(tile.clip) : gl::StencilMode::disabled(); - parameters.programs.heatmap.get(evaluated).draw( + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + auto& programInstance = parameters.programs.heatmap.get(evaluated); + + const auto allUniformValues = programInstance.computeAllUniformValues( + HeatmapProgram::UniformValues { + uniforms::u_intensity::Value{ evaluated.get() }, + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::heatmap::u_extrude_scale::Value{ extrudeScale } + }, + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), stencilMode, gl::ColorMode::additive(), - HeatmapProgram::UniformValues { - uniforms::u_intensity::Value{evaluated.get()}, - uniforms::u_matrix::Value{tile.matrix}, - uniforms::heatmap::u_extrude_scale::Value{extrudeScale} - }, - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); } @@ -123,20 +138,41 @@ void RenderHeatmapLayer::render(PaintParameters& parameters, RenderSource*) { matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); const Properties<>::PossiblyEvaluated properties; + const HeatmapTextureProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; + + auto& programInstance = parameters.programs.heatmapTexture; - parameters.programs.heatmapTexture.draw( - parameters.context, gl::Triangles(), gl::DepthMode::disabled(), - gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + const auto allUniformValues = programInstance.computeAllUniformValues( HeatmapTextureProgram::UniformValues{ uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, uniforms::u_image::Value{ 0 }, uniforms::u_color_ramp::Value{ 1 }, - uniforms::u_opacity::Value{ evaluated.get() } }, + uniforms::u_opacity::Value{ evaluated.get() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.extrusionTextureVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + gl::DepthMode::disabled(), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.extrusionTextureSegments, - HeatmapTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, - parameters.state.getZoom(), getID()); + allUniformValues, + allAttributeBindings, + getID() + ); } } diff --git a/src/mbgl/renderer/layers/render_hillshade_layer.cpp b/src/mbgl/renderer/layers/render_hillshade_layer.cpp index bcfd4ffe99..411305edf4 100644 --- a/src/mbgl/renderer/layers/render_hillshade_layer.cpp +++ b/src/mbgl/renderer/layers/render_hillshade_layer.cpp @@ -69,12 +69,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src const auto& indexBuffer, const auto& segments, const UnwrappedTileID& id) { - parameters.programs.hillshade.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.hillshade; + + const HillshadeProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 }; + + const auto allUniformValues = programInstance.computeAllUniformValues( HillshadeProgram::UniformValues { uniforms::u_matrix::Value{ matrix }, uniforms::u_image::Value{ 0 }, @@ -84,12 +83,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src uniforms::u_light::Value{ getLight(parameters) }, uniforms::u_latrange::Value{ getLatRange(id) }, }, + paintAttributeData, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( vertexBuffer, + paintAttributeData, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), indexBuffer, segments, - HillshadeProgram::PaintPropertyBinders { evaluated, 0 }, - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; @@ -112,13 +127,11 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src parameters.context.bindTexture(*bucket.dem, 0, gl::TextureFilter::Nearest, gl::TextureMipMap::No, gl::TextureWrap::Clamp, gl::TextureWrap::Clamp); const Properties<>::PossiblyEvaluated properties; + const HillshadePrepareProgram::PaintPropertyBinders paintAttributeData{ properties, 0 }; - parameters.programs.hillshadePrepare.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.hillshadePrepare; + + const auto allUniformValues = programInstance.computeAllUniformValues( HillshadePrepareProgram::UniformValues { uniforms::u_matrix::Value { mat }, uniforms::u_dimension::Value { {{uint16_t(tilesize * 2), uint16_t(tilesize * 2) }} }, @@ -126,12 +139,28 @@ void RenderHillshadeLayer::render(PaintParameters& parameters, RenderSource* src uniforms::u_maxzoom::Value{ float(maxzoom) }, uniforms::u_image::Value{ 0 } }, + paintAttributeData, + properties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( parameters.staticData.rasterVertexBuffer, + paintAttributeData, + properties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.rasterSegments, - HillshadePrepareProgram::PaintPropertyBinders { properties, 0 }, - properties, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); bucket.texture = std::move(view.getTexture()); diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 1b4a1c0ff7..e7fe4d34b2 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -62,19 +62,34 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { LineBucket& bucket = *reinterpret_cast(tile.tile.getBucket(*baseImpl)); auto draw = [&] (auto& program, auto&& uniformValues) { - program.get(evaluated).draw( + auto& programInstance = program.get(evaluated); + + const auto& paintPropertyBinders = bucket.paintPropertyBinders.at(getID()); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + paintPropertyBinders, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *bucket.vertexBuffer, + paintPropertyBinders, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), parameters.stencilModeForClipping(tile.clip), parameters.colorModeForRenderPass(), - std::move(uniformValues), - *bucket.vertexBuffer, *bucket.indexBuffer, bucket.segments, - bucket.paintPropertyBinders.at(getID()), - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp index b41b2ac560..f202ed4ebb 100644 --- a/src/mbgl/renderer/layers/render_raster_layer.cpp +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -73,16 +73,15 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source if (parameters.pass != RenderPass::Translucent) return; + RasterProgram::PaintPropertyBinders paintAttributeData{ evaluated, 0 }; + auto draw = [&] (const mat4& matrix, const auto& vertexBuffer, const auto& indexBuffer, const auto& segments) { - parameters.programs.raster.draw( - parameters.context, - gl::Triangles(), - parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - gl::StencilMode::disabled(), - parameters.colorModeForRenderPass(), + auto& programInstance = parameters.programs.raster; + + const auto allUniformValues = programInstance.computeAllUniformValues( RasterProgram::UniformValues { uniforms::u_matrix::Value{ matrix }, uniforms::u_image0::Value{ 0 }, @@ -98,12 +97,28 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source uniforms::u_scale_parent::Value{ 1.0f }, uniforms::u_tl_parent::Value{ std::array {{ 0.0f, 0.0f }} }, }, + paintAttributeData, + evaluated, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( vertexBuffer, + paintAttributeData, + evaluated + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), indexBuffer, segments, - RasterProgram::PaintPropertyBinders { evaluated, 0 }, - evaluated, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 9e493003c0..e48c0e2f92 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -88,7 +88,26 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { const auto& binders, const auto& paintProperties) { - program.get(paintProperties).draw( + auto& programInstance = program.get(paintProperties); + + const auto allUniformValues = programInstance.computeAllUniformValues( + std::move(uniformValues), + *symbolSizeBinder, + binders, + paintProperties, + parameters.state.getZoom() + ); + const auto allAttributeBindings = programInstance.computeAllAttributeBindings( + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *buffers.opacityVertexBuffer, + binders, + paintProperties + ); + + checkRenderability(parameters, programInstance.activeBindingCount(allAttributeBindings)); + + programInstance.draw( parameters.context, gl::Triangles(), values_.pitchAlignment == AlignmentType::Map @@ -96,16 +115,10 @@ void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { : gl::DepthMode::disabled(), gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), - std::move(uniformValues), - *buffers.vertexBuffer, - *buffers.dynamicVertexBuffer, - *buffers.opacityVertexBuffer, - *symbolSizeBinder, *buffers.indexBuffer, buffers.segments, - binders, - paintProperties, - parameters.state.getZoom(), + allUniformValues, + allAttributeBindings, getID() ); }; diff --git a/src/mbgl/renderer/render_layer.cpp b/src/mbgl/renderer/render_layer.cpp index bcdc175f14..a667d5837e 100644 --- a/src/mbgl/renderer/render_layer.cpp +++ b/src/mbgl/renderer/render_layer.cpp @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include namespace mbgl { @@ -73,5 +75,32 @@ void RenderLayer::setRenderTiles(std::vector> renderTiles = std::move(tiles); } +void RenderLayer::checkRenderability(const PaintParameters& parameters, + const uint32_t activeBindingCount) { + // Only warn once for every layer. + if (hasRenderFailures) { + return; + } + + if (activeBindingCount > parameters.context.maximumVertexBindingCount) { + Log::Error(Event::OpenGL, + "The layer '%s' uses more data-driven properties than the current device " + "supports, and will have rendering errors. To ensure compatibility with this " + "device, use %d fewer data driven properties in this layer.", + getID().c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } else if (activeBindingCount > parameters.context.minimumRequiredVertexBindingCount) { + Log::Error(Event::OpenGL, + "The layer '%s' uses more data-driven properties than some devices may support. " + "Though it will render correctly on this device, it may have rendering errors " + "on other devices. To ensure compatibility with all devices, use %d fewer " + "data-driven properties in this layer.", + getID().c_str(), + activeBindingCount - parameters.context.minimumRequiredVertexBindingCount); + hasRenderFailures = true; + } +} + } //namespace mbgl diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 55831cb72c..5b92ffa4db 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -81,6 +81,11 @@ public: friend std::string layoutKey(const RenderLayer&); +protected: + // Checks whether the current hardware can render this layer. If it can't, we'll show a warning + // in the console to inform the developer. + void checkRenderability(const PaintParameters&, uint32_t activeBindingCount); + protected: // renderTiles are exposed directly to CrossTileSymbolIndex and Placement so they // can update opacities in the symbol buckets immediately before rendering @@ -92,6 +97,12 @@ protected: // Stores what render passes this layer is currently enabled for. This depends on the // evaluated StyleProperties object and is updated accordingly. RenderPass passes = RenderPass::None; + +private: + // Some layers may not render correctly on some hardware when the vertex attribute limit of + // that GPU is exceeded. More attributes are used when adding many data driven paint properties + // to a layer. + bool hasRenderFailures = false; }; } // namespace mbgl diff --git a/src/mbgl/renderer/render_tile.cpp b/src/mbgl/renderer/render_tile.cpp index 35b34833e4..64790938ef 100644 --- a/src/mbgl/renderer/render_tile.cpp +++ b/src/mbgl/renderer/render_tile.cpp @@ -74,6 +74,8 @@ void RenderTile::finishRender(PaintParameters& parameters) { static const style::Properties<>::PossiblyEvaluated properties {}; static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto& program = parameters.programs.debug; + if (parameters.debugOptions & (MapDebugOptions::Timestamps | MapDebugOptions::ParseStatus)) { if (!tile.debugBucket || tile.debugBucket->renderable != tile.isRenderable() || tile.debugBucket->complete != tile.isComplete() || @@ -85,41 +87,51 @@ void RenderTile::finishRender(PaintParameters& parameters) { tile.expires, parameters.debugOptions, parameters.context); } - parameters.programs.debug.draw( + const auto allAttributeBindings = program.computeAllAttributeBindings( + *tile.debugBucket->vertexBuffer, + paintAttributeData, + properties + ); + + program.draw( parameters.context, gl::Lines { 4.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::white() } - }, - *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::white() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + allAttributeBindings, "debug" ); - parameters.programs.debug.draw( + program.draw( parameters.context, gl::Lines { 2.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::black() } - }, - *tile.debugBucket->vertexBuffer, *tile.debugBucket->indexBuffer, tile.debugBucket->segments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::black() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + allAttributeBindings, "debug" ); } @@ -131,16 +143,22 @@ void RenderTile::finishRender(PaintParameters& parameters) { gl::DepthMode::disabled(), parameters.stencilModeForClipping(clip), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::red() } - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), "debug" ); } diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 2ac714e122..7b25b37cf2 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -495,7 +495,9 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { static const ClippingMaskProgram::PaintPropertyBinders paintAttributeData(properties, 0); for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) { - parameters.staticData.programs.clippingMask.draw( + auto& program = parameters.staticData.programs.clippingMask; + + program.draw( parameters.context, gl::Triangles(), gl::DepthMode::disabled(), @@ -508,15 +510,21 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { gl::StencilMode::Replace }, gl::ColorMode::disabled(), - ClippingMaskProgram::UniformValues { - uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.quadTriangleIndexBuffer, parameters.staticData.tileTriangleSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), + program.computeAllUniformValues( + ClippingMaskProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(clipID.first) }, + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + program.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), "clipping" ); } diff --git a/src/mbgl/renderer/sources/render_image_source.cpp b/src/mbgl/renderer/sources/render_image_source.cpp index 31a5916a34..4946b0bc30 100644 --- a/src/mbgl/renderer/sources/render_image_source.cpp +++ b/src/mbgl/renderer/sources/render_image_source.cpp @@ -58,24 +58,32 @@ void RenderImageSource::finishRender(PaintParameters& parameters) { static const style::Properties<>::PossiblyEvaluated properties {}; static const DebugProgram::PaintPropertyBinders paintAttributeData(properties, 0); + auto& programInstance = parameters.programs.debug; + for (auto matrix : matrices) { - parameters.programs.debug.draw( + programInstance.draw( parameters.context, gl::LineStrip { 4.0f * parameters.pixelRatio }, gl::DepthMode::disabled(), gl::StencilMode::disabled(), gl::ColorMode::unblended(), - DebugProgram::UniformValues { - uniforms::u_matrix::Value{ matrix }, - uniforms::u_color::Value{ Color::red() } - }, - parameters.staticData.tileVertexBuffer, parameters.staticData.tileBorderIndexBuffer, parameters.staticData.tileBorderSegments, - paintAttributeData, - properties, - parameters.state.getZoom(), - "debug" + programInstance.computeAllUniformValues( + DebugProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_color::Value{ Color::red() } + }, + paintAttributeData, + properties, + parameters.state.getZoom() + ), + programInstance.computeAllAttributeBindings( + parameters.staticData.tileVertexBuffer, + paintAttributeData, + properties + ), + "image" ); } } -- cgit v1.2.1 From 1b9c8b5ca72c8863ba2fd674f327fcc145753c1f Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Thu, 3 May 2018 20:36:37 -0400 Subject: [ios] Switched to use center property (instead of frame) when repositioning annotation views (when adding back into queue). (#11817) --- platform/ios/CHANGELOG.md | 7 ++++++- platform/ios/src/MGLMapView.mm | 8 +++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 8f62d28ae3..e28510f80d 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,11 +8,16 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) + +### Annotations + +* Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` is `YES`. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) + ### Other changes * Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) -* Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 34269ce442..0e1f786a39 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -5725,10 +5725,12 @@ public: if (annotationView.layer.animationKeys.count > 0) { continue; } + // Move the annotation view far out of view to the left - CGRect adjustedFrame = annotationView.frame; - adjustedFrame.origin.x = -CGRectGetWidth(self.frame) * 10.0; - annotationView.frame = adjustedFrame; + CGPoint adjustedCenter = annotationView.center; + adjustedCenter.x = -CGRectGetWidth(self.frame) * 10.0; + annotationView.center = adjustedCenter; + [self enqueueAnnotationViewForAnnotationContext:annotationContext]; } } -- cgit v1.2.1 From e423ef5609cd738c07180d11744d4a45ffb3f82f Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 29 Apr 2018 16:08:16 -0700 Subject: [linux] Don't compile linux mbgl-loop-uv against node headers Previously, `target_include_directories(mbgl-loop-uv PUBLIC ${NODEJS_INCLUDE_DIRS})` always applied. Compiles were working by accident because node is always installed in CI. Instead, split and inline loop-uv.cmake contents: linux uses `target_add_mason_package(mbgl-loop-uv PUBLIC libuv)`, node uses `target_include_directories(mbgl-loop-node PUBLIC ${NODEJS_INCLUDE_DIRS})`. --- cmake/loop-uv.cmake | 18 ------------------ cmake/node.cmake | 22 +++++++++++++++++----- platform/linux/config.cmake | 27 ++++++++++++++++----------- platform/macos/config.cmake | 1 - 4 files changed, 33 insertions(+), 35 deletions(-) delete mode 100644 cmake/loop-uv.cmake diff --git a/cmake/loop-uv.cmake b/cmake/loop-uv.cmake deleted file mode 100644 index f4e7ced00e..0000000000 --- a/cmake/loop-uv.cmake +++ /dev/null @@ -1,18 +0,0 @@ -add_library(mbgl-loop-uv STATIC - platform/default/async_task.cpp - platform/default/run_loop.cpp - platform/default/timer.cpp -) - -target_include_directories(mbgl-loop-uv - PRIVATE include - PRIVATE src -) - -target_link_libraries(mbgl-loop-uv - PRIVATE mbgl-core -) - -create_source_groups(mbgl-loop-uv) - -xcode_create_scheme(TARGET mbgl-loop-uv) \ No newline at end of file diff --git a/cmake/node.cmake b/cmake/node.cmake index 0c2f556b7d..90bec6575c 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -2,6 +2,22 @@ include(cmake/NodeJS.cmake) nodejs_init() +add_library(mbgl-loop-node STATIC + platform/default/async_task.cpp + platform/default/run_loop.cpp + platform/default/timer.cpp +) + +target_include_directories(mbgl-loop-node + PRIVATE include + PRIVATE src +) + +target_include_directories(mbgl-loop-node PUBLIC ${NODEJS_INCLUDE_DIRS}) + +create_source_groups(mbgl-loop-node) +xcode_create_scheme(TARGET mbgl-loop-node) + add_nodejs_module(mbgl-node platform/node/src/node_mapbox_gl_native.cpp ) @@ -31,13 +47,9 @@ target_include_directories(mbgl-node PRIVATE platform/default ) -# Use node-provided uv.h. This is not part of loop-uv.cmake because loop-uv.cmake is also -# used by linux/config.cmake, where we need to use headers provided by mason's libuv. -target_include_directories(mbgl-loop-uv PUBLIC ${NODEJS_INCLUDE_DIRS}) - target_link_libraries(mbgl-node PRIVATE mbgl-core - PRIVATE mbgl-loop-uv + PRIVATE mbgl-loop-node ) target_add_mason_package(mbgl-node PRIVATE geojson) diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake index dd5f0af112..53683daef0 100644 --- a/platform/linux/config.cmake +++ b/platform/linux/config.cmake @@ -11,7 +11,22 @@ mason_use(benchmark VERSION 1.2.0) mason_use(icu VERSION 58.1-min-size) mason_use(args VERSION 6.2.0 HEADER_ONLY) -include(cmake/loop-uv.cmake) +add_library(mbgl-loop-uv STATIC + platform/default/async_task.cpp + platform/default/run_loop.cpp + platform/default/timer.cpp +) + +target_include_directories(mbgl-loop-uv + PRIVATE include + PRIVATE src +) + +target_link_libraries(mbgl-loop-uv + PRIVATE mbgl-core +) + +target_add_mason_package(mbgl-loop-uv PUBLIC libuv) macro(mbgl_platform_core) target_add_mason_package(mbgl-core PUBLIC mesa) @@ -110,8 +125,6 @@ macro(mbgl_platform_glfw) PRIVATE mbgl-loop-uv ) - target_add_mason_package(mbgl-glfw PUBLIC libuv) - add_custom_command( TARGET mbgl-glfw POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy @@ -126,8 +139,6 @@ macro(mbgl_platform_render) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-render PUBLIC libuv) endmacro() @@ -136,8 +147,6 @@ macro(mbgl_platform_offline) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-offline PUBLIC libuv) endmacro() @@ -160,8 +169,6 @@ macro(mbgl_platform_test) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-test PUBLIC libuv) endmacro() @@ -180,8 +187,6 @@ macro(mbgl_platform_benchmark) PRIVATE mbgl-filesource PRIVATE mbgl-loop-uv ) - - target_add_mason_package(mbgl-benchmark PUBLIC libuv) endmacro() diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index e929bb55c6..28573258d9 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -6,7 +6,6 @@ mason_use(benchmark VERSION 1.2.0) mason_use(icu VERSION 58.1-min-size) mason_use(args VERSION 6.2.0 HEADER_ONLY) -include(cmake/loop-uv.cmake) include(cmake/loop-darwin.cmake) macro(mbgl_platform_core) -- cgit v1.2.1 From 167a2321d98b0733527fd4f94c11a2f33fb243e5 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Fri, 4 May 2018 09:40:51 -0700 Subject: [ios, macos] Update migration guides in jazzy table (#11808) * [macos] update jazzy for macos * [ios] moved migration guide --- platform/ios/jazzy.yml | 2 +- platform/macos/jazzy.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml index 34dcc1f50b..d3aae51b77 100644 --- a/platform/ios/jazzy.yml +++ b/platform/ios/jazzy.yml @@ -19,10 +19,10 @@ custom_categories: children: - Adding Markers to a Map - Runtime Styling - - Migrating to Expressions - Working with Mapbox Studio - Working with GeoJSON Data - Predicates and Expressions + - Migrating to Expressions - For Style Authors - Tile URL Templates - Info.plist Keys diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml index e1053324fb..915c1f46a9 100644 --- a/platform/macos/jazzy.yml +++ b/platform/macos/jazzy.yml @@ -20,7 +20,7 @@ custom_categories: - Working with GeoJSON Data - Predicates and Expressions - For Style Authors - - Style Layers Using Expressions + - Migrating to Expressions - Tile URL Templates - Info.plist Keys - name: Maps -- cgit v1.2.1 From 7693a00581315c750b5a00cf9d87b15cb1970bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 30 Apr 2018 18:32:37 +0200 Subject: [android] - checking is renderer is not destroyed before delivering the snapshot --- .../mapboxsdk/maps/renderer/MapRenderer.java | 8 ++------ platform/android/src/map_renderer.cpp | 23 +++++++--------------- platform/android/src/map_renderer.hpp | 6 +----- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index f1c70325a0..fcee5bd179 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -37,11 +37,11 @@ public abstract class MapRenderer implements MapRendererScheduler { } public void onPause() { - nativeOnPause(); + // Implement if needed } public void onResume() { - nativeOnResume(); + // Implement if needed } public void onStop() { @@ -124,10 +124,6 @@ public abstract class MapRenderer implements MapRendererScheduler { private native void nativeRender(); - private native void nativeOnResume(); - - private native void nativeOnPause(); - private long frames; private long timeElapsed; diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp index f7e16b7091..ba6fdc63b0 100644 --- a/platform/android/src/map_renderer.cpp +++ b/platform/android/src/map_renderer.cpp @@ -29,6 +29,7 @@ MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object obj, MapRenderer::~MapRenderer() = default; void MapRenderer::reset() { + destroyed = true; // Make sure to destroy the renderer on the GL Thread auto self = ActorRef(*this, mailbox); self.ask(&MapRenderer::resetRenderer).wait(); @@ -88,8 +89,10 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) { self.invoke( &MapRenderer::scheduleSnapshot, std::make_unique([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { - runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { - callback(std::move(image)); + runloop->invoke([callback=std::move(callback), image=std::move(image), renderer=std::move(this)]() mutable { + if (renderer && !renderer->destroyed) { + callback(std::move(image)); + } }); snapshotCallback.reset(); }) @@ -136,7 +139,7 @@ void MapRenderer::render(JNIEnv&) { renderer->render(*params); // Deliver the snapshot if requested - if (snapshotCallback && !paused) { + if (snapshotCallback) { snapshotCallback->operator()(backend->readFramebuffer()); snapshotCallback.reset(); } @@ -174,14 +177,6 @@ void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { requestRender(); } -void MapRenderer::onResume(JNIEnv&) { - paused = false; -} - -void MapRenderer::onPause(JNIEnv&) { - paused = true; -} - // Static methods // jni::Class MapRenderer::javaClass; @@ -200,11 +195,7 @@ void MapRenderer::registerNative(jni::JNIEnv& env) { METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"), METHOD(&MapRenderer::onSurfaceChanged, - "nativeOnSurfaceChanged"), - METHOD(&MapRenderer::onResume, - "nativeOnResume"), - METHOD(&MapRenderer::onPause, - "nativeOnPause")); + "nativeOnSurfaceChanged")); } MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object jObject) { diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp index 5fb5ef1a61..97d2db4a91 100644 --- a/platform/android/src/map_renderer.hpp +++ b/platform/android/src/map_renderer.hpp @@ -98,10 +98,6 @@ private: void onSurfaceChanged(JNIEnv&, jint width, jint height); - void onResume(JNIEnv&); - - void onPause(JNIEnv&); - private: GenericUniqueWeakObject javaPeer; @@ -124,7 +120,7 @@ private: std::mutex updateMutex; bool framebufferSizeChanged = false; - std::atomic paused {false}; + std::atomic destroyed {false}; std::unique_ptr snapshotCallback; }; -- cgit v1.2.1 From 2d11d89547b79150c93c9c265fda71c769d457af Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 15:40:41 +0200 Subject: [android] - update changelog for v5.5.3 --- platform/android/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index f62ecca2e4..bee3c886a6 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.3 - May 4, 2018 + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + ## 5.5.2 - April 10, 2018 - Correct animation scale point [#11643](https://github.com/mapbox/mapbox-gl-native/pull/11643) - Re-bind uniform locations after re-linking program [#11583](https://github.com/mapbox/mapbox-gl-native/pull/11583) -- cgit v1.2.1 From 86a2e93294b472898cfd005a4c34818551de0728 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 4 May 2018 12:49:58 -0400 Subject: [ios, macos] Fix overlay bounds that span the antimeridian. (#11783) * [ios, macos] Fix overlay bounds that span the antimeridian. * [ios, macos] Update changelogs. * [ios, macos] Make MGLLocationCoordinate2DIsValid private. * [ios, macos] Update changelogs. --- platform/darwin/src/MGLGeometry_Private.h | 11 ++++++++++ platform/darwin/src/MGLMultiPoint.mm | 2 +- platform/darwin/src/MGLOverlay.h | 5 +++++ platform/darwin/src/MGLPointCollection.mm | 2 +- platform/darwin/test/MGLGeometryTests.mm | 36 +++++++++++++++++++++++++++++++ platform/ios/CHANGELOG.md | 1 + platform/macos/CHANGELOG.md | 1 + 7 files changed, 56 insertions(+), 2 deletions(-) diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index 0bff9b09f5..d0d9446a5f 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -71,6 +71,17 @@ NS_INLINE MGLCoordinateQuad MGLCoordinateQuadFromLatLngArray(std::array= -90.0 && + coordinate.longitude <= 360.0 && + coordinate.longitude >= -360.0); +} + #if TARGET_OS_IPHONE NS_INLINE mbgl::EdgeInsets MGLEdgeInsetsFromNSEdgeInsets(UIEdgeInsets insets) { return { insets.top, insets.left, insets.bottom, insets.right }; diff --git a/platform/darwin/src/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm index 240dad9614..75638fec97 100644 --- a/platform/darwin/src/MGLMultiPoint.mm +++ b/platform/darwin/src/MGLMultiPoint.mm @@ -163,7 +163,7 @@ if (!_bounds) { mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); for (auto coordinate : _coordinates) { - if (!CLLocationCoordinate2DIsValid(coordinate)) { + if (!MGLLocationCoordinate2DIsValid(coordinate)) { bounds = mbgl::LatLngBounds::empty(); break; } diff --git a/platform/darwin/src/MGLOverlay.h b/platform/darwin/src/MGLOverlay.h index 462a0c1031..7706b741e2 100644 --- a/platform/darwin/src/MGLOverlay.h +++ b/platform/darwin/src/MGLOverlay.h @@ -30,6 +30,11 @@ NS_ASSUME_NONNULL_BEGIN This property contains the smallest rectangle that completely encompasses the overlay. Implementers of this protocol must set this area when implementing their overlay class, and after setting it, you must not change it. + + If this overlay spans the antimeridian, its bounds may extend west of −180 degrees + longitude or east of 180 degrees longitude. For example, an overlay covering the + Pacific Ocean from Tokyo to San Francisco might have a bounds extending + from (35.68476, −220.24257) to (37.78428, −122.41310). */ @property (nonatomic, readonly) MGLCoordinateBounds overlayBounds; diff --git a/platform/darwin/src/MGLPointCollection.mm b/platform/darwin/src/MGLPointCollection.mm index 8f20d91a42..efb9497a1f 100644 --- a/platform/darwin/src/MGLPointCollection.mm +++ b/platform/darwin/src/MGLPointCollection.mm @@ -54,7 +54,7 @@ NS_ASSUME_NONNULL_BEGIN if (!_bounds) { mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty(); for (auto coordinate : _coordinates) { - if (!CLLocationCoordinate2DIsValid(coordinate)) { + if (!MGLLocationCoordinate2DIsValid(coordinate)) { bounds = mbgl::LatLngBounds::empty(); break; } diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm index a0ddecf77e..1489fefea9 100644 --- a/platform/darwin/test/MGLGeometryTests.mm +++ b/platform/darwin/test/MGLGeometryTests.mm @@ -172,4 +172,40 @@ XCTAssertEqual(point.y, roundTrippedPoint.y); XCTAssertEqual(point.zoomLevel, roundTrippedPoint.zoomLevel); } + +- (void)testMGLLocationCoordinate2DIsValid { + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(37.936, -71.516); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(46.816368, 5.844469); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-21.512680, 23.334703); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-44.947936, -73.081313); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(19.333630, 203.555405); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(23.254696, -240.795323); + XCTAssertTrue(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(91, 361); + XCTAssertFalse(MGLLocationCoordinate2DIsValid(coordinate)); + } + { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(-91, -361); + XCTAssertFalse(MGLLocationCoordinate2DIsValid(coordinate)); + } +} + @end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index e28510f80d..8fc1f7e1e0 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -20,6 +20,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) ## 4.0.0 - April 19, 2018 diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 62c2931d43..0e8d40f256 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -10,6 +10,7 @@ * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) ## 0.7.0 - April 19, 2018 -- cgit v1.2.1 From 4db617c7a9d78691d63d540adebea0fd62a641bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Fri, 27 Apr 2018 12:24:32 +0200 Subject: [android] - null check source before removing --- .../main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 6 +++++- .../mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index b2d7af7687..c84651da24 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -759,9 +759,13 @@ final class NativeMapView { return null; } Source source = getSource(sourceId); - return removeSource(source); + if (source != null) { + return removeSource(source); + } + return null; } + @Nullable public Source removeSource(@NonNull Source source) { if (isDestroyedOn("removeSource")) { return null; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java index fc526176d4..23a75d1642 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java @@ -280,6 +280,20 @@ public class RuntimeStyleTests extends BaseActivityTest { }); } + @Test + public void testRemoveNonExistingSource() { + invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.removeSource("source")); + } + + @Test + public void testRemoveNonExistingLayer() { + invoke(mapboxMap, (uiController, mapboxMap) -> { + mapboxMap.removeLayer("layer"); + mapboxMap.removeLayerAt(mapboxMap.getLayers().size() + 1); + mapboxMap.removeLayerAt(-1); + }); + } + /** * https://github.com/mapbox/mapbox-gl-native/issues/7973 */ -- cgit v1.2.1 From f0b5a56a4bfd9b2e2c057fe1501f432c36a65fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Tue, 1 May 2018 15:33:33 +0200 Subject: [android] - unwrap LatLngBounds before creating core object --- .../com/mapbox/mapboxsdk/geometry/LatLngBounds.java | 13 +++++-------- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 2 +- .../mapbox/mapboxsdk/geometry/LatLngBoundsTest.java | 3 +-- platform/android/src/geometry/lat_lng_bounds.cpp | 19 +++++++++++-------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 55494b72d8..2b56f5b326 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -28,7 +28,7 @@ public class LatLngBounds implements Parcelable { /** * Construct a new LatLngBounds based on its corners, given in NESW * order. - * + *

* If eastern longitude is smaller than the western one, bounds will include antimeridian. * For example, if the NE point is (10, -170) and the SW point is (-10, 170), then bounds will span over 20 degrees * and cross the antimeridian. @@ -75,7 +75,6 @@ public class LatLngBounds implements Parcelable { if (longCenter >= GeometryConstants.MAX_LONGITUDE) { longCenter = this.longitudeEast - halfSpan; } - return new LatLng(latCenter, longCenter); } return new LatLng(latCenter, longCenter); @@ -188,7 +187,6 @@ public class LatLngBounds implements Parcelable { return GeometryConstants.LONGITUDE_SPAN - longSpan; } - static double getLongitudeSpan(final double longEast, final double longWest) { double longSpan = Math.abs(longEast - longWest); if (longEast >= longWest) { @@ -278,12 +276,11 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from doubles representing a LatLng pair. - * + *

* This values of latNorth and latSouth should be in the range of [-90, 90], * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, * otherwise IllegalArgumentException will be thrown. * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. - * *

* This method doesn't recalculate most east or most west boundaries. * Note that lonEast and lonWest will be wrapped to be in the range of [-180, 180], @@ -334,14 +331,14 @@ public class LatLngBounds implements Parcelable { /** * Constructs a LatLngBounds from a Tile identifier. - * + *

* Returned bounds will have latitude in the range of Mercator projection. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE * * @param z Tile zoom level. * @param x Tile X coordinate. * @param y Tile Y coordinate. + * @see GeometryConstants#MIN_MERCATOR_LATITUDE + * @see GeometryConstants#MAX_MERCATOR_LATITUDE */ public static LatLngBounds from(int z, int x, int y) { return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 5e36dd0f78..cfa7143671 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -1550,7 +1550,7 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { + public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { // get padded camera position from LatLngBounds return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); } diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index e072f07fb9..c66e4b6fda 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -524,7 +524,6 @@ public class LatLngBoundsTest { LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); } - @Test public void testConstructorChecksSouthLatitudeNaN() { exception.expect(IllegalArgumentException.class); @@ -543,7 +542,7 @@ public class LatLngBoundsTest { public void testConstructorChecksSouthLatitudeGreaterThan90() { exception.expect(IllegalArgumentException.class); exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20,95, 0); + LatLngBounds.from(20, 20, 95, 0); } @Test diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp index ec1a32fed5..827ee52e95 100644 --- a/platform/android/src/geometry/lat_lng_bounds.cpp +++ b/platform/android/src/geometry/lat_lng_bounds.cpp @@ -9,14 +9,17 @@ jni::Object LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds } mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object bounds) { - static auto swLat = LatLngBounds::javaClass.GetField(env, "latitudeSouth"); - static auto swLon = LatLngBounds::javaClass.GetField(env, "longitudeWest"); - static auto neLat = LatLngBounds::javaClass.GetField(env, "latitudeNorth"); - static auto neLon = LatLngBounds::javaClass.GetField(env, "longitudeEast"); - return mbgl::LatLngBounds::hull( - { bounds.Get(env, swLat), bounds.Get(env, swLon) }, - { bounds.Get(env, neLat), bounds.Get(env, neLon) } - ); + static auto swLatField = LatLngBounds::javaClass.GetField(env, "latitudeSouth"); + static auto swLonField = LatLngBounds::javaClass.GetField(env, "longitudeWest"); + static auto neLatField = LatLngBounds::javaClass.GetField(env, "latitudeNorth"); + static auto neLonField = LatLngBounds::javaClass.GetField(env, "longitudeEast"); + + mbgl::LatLng sw = { bounds.Get(env, swLatField), bounds.Get(env, swLonField) }; + mbgl::LatLng ne = { bounds.Get(env, neLatField), bounds.Get(env, neLonField) }; + + sw.unwrapForShortestPath(ne); + + return mbgl::LatLngBounds::hull(sw, ne); } void LatLngBounds::registerNative(jni::JNIEnv& env) { -- cgit v1.2.1 From 5388f9ea2c9826a45582343bc0b555fddf7ff28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Paczos?= Date: Mon, 30 Apr 2018 18:32:37 +0200 Subject: [android] - checking is renderer is not destroyed before delivering the snapshot --- .../mapboxsdk/maps/renderer/MapRenderer.java | 8 ++------ platform/android/src/map_renderer.cpp | 23 +++++++--------------- platform/android/src/map_renderer.hpp | 6 +----- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java index f1c70325a0..fcee5bd179 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java @@ -37,11 +37,11 @@ public abstract class MapRenderer implements MapRendererScheduler { } public void onPause() { - nativeOnPause(); + // Implement if needed } public void onResume() { - nativeOnResume(); + // Implement if needed } public void onStop() { @@ -124,10 +124,6 @@ public abstract class MapRenderer implements MapRendererScheduler { private native void nativeRender(); - private native void nativeOnResume(); - - private native void nativeOnPause(); - private long frames; private long timeElapsed; diff --git a/platform/android/src/map_renderer.cpp b/platform/android/src/map_renderer.cpp index f7e16b7091..ba6fdc63b0 100644 --- a/platform/android/src/map_renderer.cpp +++ b/platform/android/src/map_renderer.cpp @@ -29,6 +29,7 @@ MapRenderer::MapRenderer(jni::JNIEnv& _env, jni::Object obj, MapRenderer::~MapRenderer() = default; void MapRenderer::reset() { + destroyed = true; // Make sure to destroy the renderer on the GL Thread auto self = ActorRef(*this, mailbox); self.ask(&MapRenderer::resetRenderer).wait(); @@ -88,8 +89,10 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) { self.invoke( &MapRenderer::scheduleSnapshot, std::make_unique([&, callback=std::move(callback), runloop=util::RunLoop::Get()](PremultipliedImage image) { - runloop->invoke([callback=std::move(callback), image=std::move(image)]() mutable { - callback(std::move(image)); + runloop->invoke([callback=std::move(callback), image=std::move(image), renderer=std::move(this)]() mutable { + if (renderer && !renderer->destroyed) { + callback(std::move(image)); + } }); snapshotCallback.reset(); }) @@ -136,7 +139,7 @@ void MapRenderer::render(JNIEnv&) { renderer->render(*params); // Deliver the snapshot if requested - if (snapshotCallback && !paused) { + if (snapshotCallback) { snapshotCallback->operator()(backend->readFramebuffer()); snapshotCallback.reset(); } @@ -174,14 +177,6 @@ void MapRenderer::onSurfaceChanged(JNIEnv&, jint width, jint height) { requestRender(); } -void MapRenderer::onResume(JNIEnv&) { - paused = false; -} - -void MapRenderer::onPause(JNIEnv&) { - paused = true; -} - // Static methods // jni::Class MapRenderer::javaClass; @@ -200,11 +195,7 @@ void MapRenderer::registerNative(jni::JNIEnv& env) { METHOD(&MapRenderer::onSurfaceCreated, "nativeOnSurfaceCreated"), METHOD(&MapRenderer::onSurfaceChanged, - "nativeOnSurfaceChanged"), - METHOD(&MapRenderer::onResume, - "nativeOnResume"), - METHOD(&MapRenderer::onPause, - "nativeOnPause")); + "nativeOnSurfaceChanged")); } MapRenderer& MapRenderer::getNativePeer(JNIEnv& env, jni::Object jObject) { diff --git a/platform/android/src/map_renderer.hpp b/platform/android/src/map_renderer.hpp index 5fb5ef1a61..97d2db4a91 100644 --- a/platform/android/src/map_renderer.hpp +++ b/platform/android/src/map_renderer.hpp @@ -98,10 +98,6 @@ private: void onSurfaceChanged(JNIEnv&, jint width, jint height); - void onResume(JNIEnv&); - - void onPause(JNIEnv&); - private: GenericUniqueWeakObject javaPeer; @@ -124,7 +120,7 @@ private: std::mutex updateMutex; bool framebufferSizeChanged = false; - std::atomic paused {false}; + std::atomic destroyed {false}; std::unique_ptr snapshotCallback; }; -- cgit v1.2.1 From 2f0475b061e5d7791481427d8b7250b39a6b8073 Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 14:31:58 +0200 Subject: [android] - update changelog for release v6.1.0 --- platform/android/CHANGELOG.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 8a98e78a71..9d9d22e1f9 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,10 +2,19 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## 6.0.2 +## 6.1.0 - May 4, 2018 + - Unwrap LatLngBounds during JNI conversion [#11807](https://github.com/mapbox/mapbox-gl-native/pull/11807) + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + - Null-check source before removing [#11789](https://github.com/mapbox/mapbox-gl-native/pull/11789) + - Flutter support: promote pixel-ratio to public API NativeMapView [#11772](https://github.com/mapbox/mapbox-gl-native/pull/11772) + - Unwrap LatLngBounds for the shortest path when requesting camera [#11759](https://github.com/mapbox/mapbox-gl-native/pull/11759) + - Flutter support: integrate view callback abstraction [#11706](https://github.com/mapbox/mapbox-gl-native/pull/11706) + - Match expression doc tweaks [#11691](https://github.com/mapbox/mapbox-gl-native/pull/11691) + - Improve stop javadoc to include interpolate [#11677](https://github.com/mapbox/mapbox-gl-native/pull/11677) - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811) - Add Korean localization [#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792) - + - Add Danish localization; update Hungarian, Russian, Swedish translations [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11136) + ## 6.0.1 - April 17, 2018 - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) -- cgit v1.2.1 From 4f0241c1afcf46370eb325e80c1d52a4b490df38 Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 14:35:10 +0200 Subject: [android] - bump snapshot version --- circle.yml | 2 +- platform/android/MapboxGLAndroidSDK/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 5f06791cd6..8c0505badc 100644 --- a/circle.yml +++ b/circle.yml @@ -358,7 +358,7 @@ jobs: gcloud firebase test android run --type instrumentation \ --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \ --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \ - --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m + --device-ids sailfish --os-version-ids 26 --locales en --orientations portrait --timeout 20m - store_artifacts: path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug destination: . diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties index bdfc444454..a49ef47674 100644 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ b/platform/android/MapboxGLAndroidSDK/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.mapbox.mapboxsdk -VERSION_NAME=6.1.0-SNAPSHOT +VERSION_NAME=6.2.0-SNAPSHOT POM_DESCRIPTION=Mapbox GL Android SDK POM_URL=https://github.com/mapbox/mapbox-gl-native -- cgit v1.2.1 From 1c847b321b401a473a45f839d7e53c27836c1393 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 4 May 2018 13:30:06 -0400 Subject: [ios, macos] Add support for subscripting in expressions. (#11770) * [ios, macos] Add support for subscripting in expressions. * [ios, macos] Update changelogs. * [ios, macos] Refactor LAST subscripting expression. --- platform/darwin/src/NSExpression+MGLAdditions.mm | 17 ++++++++++++++- platform/darwin/test/MGLExpressionTests.mm | 27 ++++++++++++++++++++++++ platform/ios/CHANGELOG.md | 1 + platform/macos/CHANGELOG.md | 1 + 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 633f433a8f..7881b79370 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -1097,7 +1097,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *arguments = self.arguments.mgl_jsonExpressionObject; return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments]; } else if ([function isEqualToString:@"objectFrom:withIndex:"]) { - return @[@"at", self.arguments[1].mgl_jsonExpressionObject, self.arguments[0].mgl_jsonExpressionObject]; + id index = self.arguments[1].mgl_jsonExpressionObject; + + if ([self.arguments[1] expressionType] == NSConstantValueExpressionType + && [[self.arguments[1] constantValue] isKindOfClass:[NSString class]]) { + id value = self.arguments[1].constantValue; + + if ([value isEqualToString:@"FIRST"]) { + index = [NSExpression expressionForConstantValue:@0].mgl_jsonExpressionObject; + } else if ([value isEqualToString:@"LAST"]) { + index = [NSExpression expressionWithFormat:@"count(%@) - 1", self.arguments[0]].mgl_jsonExpressionObject; + } else if ([value isEqualToString:@"SIZE"]) { + return [NSExpression expressionWithFormat:@"count(%@)", self.arguments[0]].mgl_jsonExpressionObject; + } + } + + return @[@"at", index, self.arguments[0].mgl_jsonExpressionObject]; } else if ([function isEqualToString:@"boolValue"]) { return @[@"to-boolean", self.operand.mgl_jsonExpressionObject]; } else if ([function isEqualToString:@"mgl_number"] || diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 9c75dedaa3..186cae609d 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -837,6 +837,33 @@ using namespace std::string_literals; } - (void)testLookupExpressionObject { + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"FIRST")]]; + NSArray *jsonExpression = @[@"at", @0, @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"LAST")]]; + NSArray *jsonExpression = @[@"at", @[@"-", @[@"length", @[ @"literal", @[@9, @8, @7]]], @1], @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), + MGLConstantExpression(@8), + MGLConstantExpression(@7)]]; + NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:" + arguments:@[array, MGLConstantExpression(@"SIZE")]]; + NSArray *jsonExpression = @[@"length", @[ @"literal", @[@9, @8, @7]]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } { NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9), MGLConstantExpression(@8), diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 8fc1f7e1e0..d979021760 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) ### Annotations diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 0e8d40f256..7cc8e945af 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,6 +5,7 @@ ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) ### Other changes -- cgit v1.2.1 From a365dd11da0f4c41ad793ea1e3db3cef8e3cbce8 Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 4 May 2018 15:09:07 -0400 Subject: [ios, macos] Make NSPredicate+MGLAdditions public. (#11810) --- platform/darwin/src/MGLBackgroundStyleLayer.mm | 2 +- platform/darwin/src/MGLCircleStyleLayer.mm | 2 +- platform/darwin/src/MGLFillExtrusionStyleLayer.mm | 2 +- platform/darwin/src/MGLFillStyleLayer.mm | 2 +- platform/darwin/src/MGLHeatmapStyleLayer.mm | 2 +- platform/darwin/src/MGLHillshadeStyleLayer.mm | 2 +- platform/darwin/src/MGLLineStyleLayer.mm | 2 +- platform/darwin/src/MGLRasterStyleLayer.mm | 2 +- platform/darwin/src/MGLShapeSource.mm | 2 +- platform/darwin/src/MGLStyleLayer.mm.ejs | 2 +- platform/darwin/src/MGLSymbolStyleLayer.mm | 2 +- platform/darwin/src/MGLVectorTileSource.mm | 2 +- .../darwin/src/NSCompoundPredicate+MGLAdditions.mm | 2 +- platform/darwin/src/NSExpression+MGLAdditions.mm | 2 +- platform/darwin/src/NSPredicate+MGLAdditions.h | 51 ++++++++---- platform/darwin/src/NSPredicate+MGLAdditions.mm | 16 ++-- .../darwin/src/NSPredicate+MGLPrivateAdditions.h | 25 ++++++ platform/darwin/test/MGLPredicateTests.mm | 92 +++++++++++----------- platform/ios/ios.xcodeproj/project.pbxproj | 10 ++- platform/ios/src/MGLMapView.mm | 2 +- platform/ios/src/Mapbox.h | 1 + platform/macos/CHANGELOG.md | 1 + platform/macos/macos.xcodeproj/project.pbxproj | 6 +- platform/macos/src/MGLMapView.mm | 2 +- platform/macos/src/Mapbox.h | 1 + 25 files changed, 149 insertions(+), 86 deletions(-) create mode 100644 platform/darwin/src/NSPredicate+MGLPrivateAdditions.h diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.mm b/platform/darwin/src/MGLBackgroundStyleLayer.mm index 993645d3a8..acea3441fa 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.mm +++ b/platform/darwin/src/MGLBackgroundStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm index 0be3920987..b85b3d9d4f 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.mm +++ b/platform/darwin/src/MGLCircleStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm index 688ce4c1ac..ea29dff62d 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm index c975e28d6b..5be0decd4a 100644 --- a/platform/darwin/src/MGLFillStyleLayer.mm +++ b/platform/darwin/src/MGLFillStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm index a394dbda3b..b4bf4c9566 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.mm +++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.mm b/platform/darwin/src/MGLHillshadeStyleLayer.mm index 2383c1ce26..70fab24e33 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.mm +++ b/platform/darwin/src/MGLHillshadeStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index 619cc70afe..b359c424a2 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLRasterStyleLayer.mm b/platform/darwin/src/MGLRasterStyleLayer.mm index 94a58409de..0e31512491 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.mm +++ b/platform/darwin/src/MGLRasterStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm index dcc3fd97f5..af26b7ea13 100644 --- a/platform/darwin/src/MGLShapeSource.mm +++ b/platform/darwin/src/MGLShapeSource.mm @@ -6,7 +6,7 @@ #import "MGLFeature_Private.h" #import "MGLShape_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSURL+MGLAdditions.h" #include diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index 42940083b5..ead246fb03 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -8,7 +8,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 0d9fac4808..4236e04238 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -2,7 +2,7 @@ // Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`. #import "MGLSource.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSDate+MGLAdditions.h" #import "MGLStyleLayer_Private.h" #import "MGLStyleValue_Private.h" diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm index c1f7267e4a..96629997d6 100644 --- a/platform/darwin/src/MGLVectorTileSource.mm +++ b/platform/darwin/src/MGLVectorTileSource.mm @@ -6,7 +6,7 @@ #import "MGLStyle_Private.h" #import "MGLMapView_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSURL+MGLAdditions.h" #include diff --git a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm index 5a98b763ea..a8ae19b172 100644 --- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm @@ -2,7 +2,7 @@ #import "MGLStyleValue_Private.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSExpression+MGLPrivateAdditions.h" #include diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index 7881b79370..b2bcf72caf 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -913,7 +913,7 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { for (NSUInteger index = 0; index < argumentObjects.count; index++) { if (index % 2 == 0 && index != argumentObjects.count - 1) { - NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:argumentObjects[index]]; + NSPredicate *predicate = [NSPredicate predicateWithMGLJSONObject:argumentObjects[index]]; NSExpression *argument = [NSExpression expressionForConstantValue:predicate]; [arguments addObject:argument]; } else { diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.h b/platform/darwin/src/NSPredicate+MGLAdditions.h index a73b1a61ba..6c4b878d37 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.h +++ b/platform/darwin/src/NSPredicate+MGLAdditions.h @@ -1,23 +1,44 @@ #import -#import "NSExpression+MGLPrivateAdditions.h" +NS_ASSUME_NONNULL_BEGIN @interface NSPredicate (MGLAdditions) -- (mbgl::style::Filter)mgl_filter; - -+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter; - -@end - -@interface NSPredicate (MGLExpressionAdditions) - -+ (instancetype)mgl_predicateWithJSONObject:(id)object; - +#pragma mark Converting JSON Expressions + +/** + Returns a predicate equivalent to the given Foundation object deserialized + from JSON data. + + The Foundation object is interpreted according to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the + “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSPredicate` representation used by this SDK. + + @param object A Foundation object deserialized from JSON data, for example + using `NSJSONSerialization`. + @return An initialized predicate equivalent to `object`, suitable for use + with the `MGLVectorStyleLayer.predicate` property. + */ ++ (instancetype)predicateWithMGLJSONObject:(id)object NS_SWIFT_NAME(init(mglJSONObject:)); + +/** + An equivalent Foundation object that can be serialized as JSON. + + The Foundation object conforms to the + [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions). + See the + “[Predicates and Expressions](../predicates-and-expressions.html)” + guide for a correspondence of operators and types between the style + specification and the `NSPredicate` representation used by this SDK. + + You can use `NSJSONSerialization` to serialize the Foundation object as data to + write to a file. + */ @property (nonatomic, readonly) id mgl_jsonExpressionObject; -- (id)mgl_if:(id)firstValue, ...; - -- (id)mgl_match:(NSExpression *)firstCase, ...; - @end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index bbd324bb63..07154cb246 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -1,4 +1,4 @@ -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" #import "MGLStyleValue_Private.h" @@ -200,11 +200,11 @@ public: NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression); - return [NSPredicate mgl_predicateWithJSONObject:jsonObject]; + return [NSPredicate predicateWithMGLJSONObject:jsonObject]; } }; -@implementation NSPredicate (MGLAdditions) +@implementation NSPredicate (MGLPrivateAdditions) - (mbgl::style::Filter)mgl_filter { @@ -230,18 +230,18 @@ public: @end -@implementation NSPredicate (MGLExpressionAdditions) +@implementation NSPredicate (MGLAdditions) NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { NSMutableArray *subpredicates = [NSMutableArray arrayWithCapacity:objects.count]; for (id object in objects) { - NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:object]; + NSPredicate *predicate = [NSPredicate predicateWithMGLJSONObject:object]; [subpredicates addObject:predicate]; } return subpredicates; } -+ (instancetype)mgl_predicateWithJSONObject:(id)object { ++ (instancetype)predicateWithMGLJSONObject:(id)object { if ([object isEqual:@YES]) { return [NSPredicate predicateWithValue:YES]; } @@ -360,6 +360,10 @@ NSArray *MGLSubpredicatesWithJSONObjects(NSArray *objects) { return nil; } +@end + +@implementation NSPredicate (MGLExpressionAdditions) + - (id)mgl_if:(id)firstValue, ... { if ([self evaluateWithObject:nil]) { diff --git a/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h b/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h new file mode 100644 index 0000000000..1828009678 --- /dev/null +++ b/platform/darwin/src/NSPredicate+MGLPrivateAdditions.h @@ -0,0 +1,25 @@ +#import + +#import "NSPredicate+MGLAdditions.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface NSPredicate (MGLPrivateAdditions) + +- (mbgl::style::Filter)mgl_filter; + ++ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter; + +@end + +@interface NSPredicate (MGLExpressionAdditions) + +- (id)mgl_if:(id)firstValue, ...; + +- (id)mgl_match:(NSExpression *)firstCase, ...; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index ab4a7e2d88..b725c63140 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -1,7 +1,7 @@ #import #import -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" namespace mbgl { @@ -242,48 +242,48 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"]; NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"]; NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"]; NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"]; NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"]; NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"]; NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { @@ -360,48 +360,48 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"]; NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"]; NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"]; NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"]; NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"]; NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { @@ -409,24 +409,24 @@ namespace mbgl { NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]]; NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]]; NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:expected], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:expected] mustRoundTrip:NO]; } { @@ -434,7 +434,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -443,7 +443,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -452,7 +452,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -461,7 +461,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -470,7 +470,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -479,7 +479,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -488,7 +488,7 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; - auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter; + auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } @@ -499,32 +499,32 @@ namespace mbgl { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]; NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]; NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"]; NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]; NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression); - XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate); - [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression] + XCTAssertEqualObjects([NSPredicate predicateWithMGLJSONObject:jsonExpression], predicate); + [self testSymmetryWithPredicate:[NSPredicate predicateWithMGLJSONObject:jsonExpression] mustRoundTrip:NO]; } { diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index c7708b2d37..46c10dab89 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ 1F7454971ECD450D00021D39 /* MGLLight_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F7454941ECD450D00021D39 /* MGLLight_Private.h */; }; 1F7454A91ED08AB400021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */; }; 1F95931D1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */; }; + 1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; + 1FC4817F2098CD80000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; 30E578171DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */; }; 30E578191DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; }; @@ -130,8 +132,8 @@ 357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 357FE2DC1E02D2B20068B753 /* NSCoder+MGLAdditions.mm */; }; 3598544D1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */; }; 359F57461D2FDDA6005217F1 /* MGLUserLocationAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 359F57451D2FDBD5005217F1 /* MGLUserLocationAnnotationView_Private.h */; }; - 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; - 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; }; + 35B82BF81D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 35B82BF91D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35B82BFA1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; 35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */; }; 35B8E08C1D6C8B5100E768D2 /* MGLPredicateTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35B8E08B1D6C8B5100E768D2 /* MGLPredicateTests.mm */; }; @@ -753,6 +755,7 @@ 1F7454941ECD450D00021D39 /* MGLLight_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLight_Private.h; sourceTree = ""; }; 1F7454A61ED08AB400021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLLightTest.mm; path = ../../darwin/test/MGLLightTest.mm; sourceTree = ""; }; 1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; + 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLPrivateAdditions.h"; sourceTree = ""; }; 20DABE861DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Foundation.strings"; sourceTree = ""; }; 20DABE881DF78148007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 20DABE8A1DF78149007AC5FF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Root.strings"; sourceTree = ""; }; @@ -2111,6 +2114,7 @@ 3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */, 35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */, 35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */, + 1FC4817B2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h */, DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */, DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */, DA8848171CBAFA6200AB86E3 /* NSString+MGLAdditions.h */, @@ -2199,6 +2203,7 @@ DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */, 35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */, 3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */, + 1FC4817D2098CBC0000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, 353AFA141D65AB17005A69F4 /* NSDate+MGLAdditions.h in Headers */, DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */, DA8847FB1CBAFA5100AB86E3 /* MGLShape.h in Headers */, @@ -2378,6 +2383,7 @@ 96E516F920005A3500A02306 /* MGLFaux3DUserLocationAnnotationView.h in Headers */, 96E516F22000596D00A02306 /* NSException+MGLAdditions.h in Headers */, 96E516EC2000560B00A02306 /* MGLUserLocationAnnotationView_Private.h in Headers */, + 1FC4817F2098CD80000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, DABFB8671CBE99E500D62B32 /* MGLPolygon.h in Headers */, 404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */, 1F7454931ECBB43F00021D39 /* MGLLight.h in Headers */, diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 0e1f786a39..71f308caf0 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -49,7 +49,7 @@ #import "NSBundle+MGLAdditions.h" #import "NSDate+MGLAdditions.h" #import "NSException+MGLAdditions.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import "NSProcessInfo+MGLAdditions.h" #import "NSString+MGLAdditions.h" #import "NSURL+MGLAdditions.h" diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h index 20417dbbd4..7beb8b766b 100644 --- a/platform/ios/src/Mapbox.h +++ b/platform/ios/src/Mapbox.h @@ -66,3 +66,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" #import "NSExpression+MGLAdditions.h" +#import "NSPredicate+MGLAdditions.h" diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 7cc8e945af..d62ba2ea87 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,6 +5,7 @@ ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) * Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) ### Other changes diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj index 4bfcd29905..5b17c61de2 100644 --- a/platform/macos/macos.xcodeproj/project.pbxproj +++ b/platform/macos/macos.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 1F7454AB1ED1DDBD00021D39 /* MGLLightTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */; }; 1F95931B1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */; }; 1F9EF4061FBA1B0E0063FBB0 /* mapbox_helmet.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */; }; + 1FC481852098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */; }; 30E5781B1DAA857E0050F07E /* NSImage+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */; }; 3508EC641D749D39009B0EE4 /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3508EC651D749D39009B0EE4 /* NSExpression+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */; }; @@ -37,7 +38,7 @@ 3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3527428E1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3527428C1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.mm */; }; 352742A11D4C25BD00A1ECE6 /* MGLStyleValue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3527429E1D4C25BD00A1ECE6 /* MGLStyleValue.mm */; }; - 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */; }; + 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3529039C1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */; }; 3537CA741D3F93A600380318 /* MGLStyle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3537CA731D3F93A600380318 /* MGLStyle_Private.h */; }; 3538AA231D542685008EC33D /* MGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3538AA211D542685008EC33D /* MGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -309,6 +310,7 @@ 1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLightTest.mm; sourceTree = ""; }; 1F95931A1E6DE2B600D5B294 /* MGLNSDateAdditionsTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLNSDateAdditionsTests.mm; path = ../../darwin/test/MGLNSDateAdditionsTests.mm; sourceTree = ""; }; 1F9EF4051FBA1B0D0063FBB0 /* mapbox_helmet.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = mapbox_helmet.pdf; sourceTree = ""; }; + 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSPredicate+MGLPrivateAdditions.h"; sourceTree = ""; }; 30E578141DAA7D920050F07E /* NSImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+MGLAdditions.h"; path = "src/NSImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; }; 3508EC621D749D39009B0EE4 /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = ""; }; 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSExpression+MGLAdditions.mm"; sourceTree = ""; }; @@ -1034,6 +1036,7 @@ 3508EC631D749D39009B0EE4 /* NSExpression+MGLAdditions.mm */, 352903991D6C63B80002C7DF /* NSPredicate+MGLAdditions.h */, 3529039A1D6C63B80002C7DF /* NSPredicate+MGLAdditions.mm */, + 1FC481842098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h */, DAE6C3801CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h */, DAE6C3811CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.m */, DAE6C3821CC31E2A00DB3429 /* NSString+MGLAdditions.h */, @@ -1218,6 +1221,7 @@ DAE6C3631CC31E0400DB3429 /* MGLPointAnnotation.h in Headers */, DAC2ABC51CC6D343006D18C4 /* MGLAnnotationImage_Private.h in Headers */, DAE6C35F1CC31E0400DB3429 /* MGLOfflinePack.h in Headers */, + 1FC481852098F323000D09B4 /* NSPredicate+MGLPrivateAdditions.h in Headers */, DAE6C39C1CC31E2A00DB3429 /* NSString+MGLAdditions.h in Headers */, 3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */, DA8F25971D51CAC70010E6B5 /* MGLVectorTileSource.h in Headers */, diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 9a9e19c363..5b78dcca14 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -58,7 +58,7 @@ #import "NSURL+MGLAdditions.h" #import "NSColor+MGLAdditions.h" #import "NSImage+MGLAdditions.h" -#import "NSPredicate+MGLAdditions.h" +#import "NSPredicate+MGLPrivateAdditions.h" #import #import diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h index fcf41203cf..198998a874 100644 --- a/platform/macos/src/Mapbox.h +++ b/platform/macos/src/Mapbox.h @@ -62,3 +62,4 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[]; #import "MGLAttributionInfo.h" #import "MGLMapSnapshotter.h" #import "NSExpression+MGLAdditions.h" +#import "NSPredicate+MGLAdditions.h" -- cgit v1.2.1 From 83b6c4405aba37f4125c3086aeec08aadb307837 Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 15:40:41 +0200 Subject: [android] - update changelog for v5.5.3 --- platform/android/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 246b5828c4..9943e3b316 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 5.5.3 - May 4, 2018 + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + ## 6.0.1 - April 17, 2018 - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710) -- cgit v1.2.1 From bc7bed642e6bb1c26feedb5c04ec64c2ff7fdd2d Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 14:31:58 +0200 Subject: [android] - update changelog for release v6.1.0 --- platform/android/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 9943e3b316..c35cde88e0 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,19 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.1.0 - May 4, 2018 + - Unwrap LatLngBounds during JNI conversion [#11807](https://github.com/mapbox/mapbox-gl-native/pull/11807) + - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) + - Null-check source before removing [#11789](https://github.com/mapbox/mapbox-gl-native/pull/11789) + - Flutter support: promote pixel-ratio to public API NativeMapView [#11772](https://github.com/mapbox/mapbox-gl-native/pull/11772) + - Unwrap LatLngBounds for the shortest path when requesting camera [#11759](https://github.com/mapbox/mapbox-gl-native/pull/11759) + - Flutter support: integrate view callback abstraction [#11706](https://github.com/mapbox/mapbox-gl-native/pull/11706) + - Match expression doc tweaks [#11691](https://github.com/mapbox/mapbox-gl-native/pull/11691) + - Improve stop javadoc to include interpolate [#11677](https://github.com/mapbox/mapbox-gl-native/pull/11677) + - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811) + - Add Korean localization [#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792) + - Add Danish localization; update Hungarian, Russian, Swedish translations [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11136) + ## 5.5.3 - May 4, 2018 - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) -- cgit v1.2.1 From a4892231544ccf6fcbe6dc233b5462f550cbbbcc Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 7 May 2018 16:11:00 +0200 Subject: [android] - update telemetry to 3.1.0 --- platform/android/gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index a5c6ad0da3..7b31cf9e4c 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices : '3.0.1', - mapboxTelemetry: '3.0.2', + mapboxTelemetry: '3.1.0', mapboxGestures : '0.2.0', supportLib : '25.4.0', espresso : '3.0.1', -- cgit v1.2.1 From 9c1e414981616e4a65ed0c3b632019178fd2e95a Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 7 May 2018 16:11:00 +0200 Subject: [android] - update telemetry to 3.1.0 --- platform/android/gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 69b0c4b97f..07a9938915 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices : '3.0.1', - mapboxTelemetry: '3.0.2', + mapboxTelemetry: '3.1.0', mapboxGestures : '0.2.0', supportLib : '25.4.0', espresso : '3.0.1', -- cgit v1.2.1 From e839bf52997249bdbe1373d4df6a257c7f779a5f Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 7 May 2018 09:09:51 +0300 Subject: [build] Bump macos-debug-qt5 xcode to 9.3.0 --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 1aacf1a2a6..d2232e9106 100644 --- a/circle.yml +++ b/circle.yml @@ -866,7 +866,7 @@ jobs: # ------------------------------------------------------------------------------ macos-debug-qt5: macos: - xcode: "9.2.0" + xcode: "9.3.0" environment: BUILDTYPE: Debug HOMEBREW_NO_AUTO_UPDATE: 1 -- cgit v1.2.1 From a425995189c3f4c9d9040b4acff477bfc96b6afd Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Mon, 7 May 2018 12:01:29 -0400 Subject: [ios, macos] Add to-rgba expression operator. (#11725) * [ios, macos] Add expression support to to-rgba operator. * [ios, macos] Update style docs. * [ios, macos] Refactored to-rgba to to-color. * [ios, macos] Add support for to-rgba expression operator. * [ios, macos] Add multiple parameters support to to-color operand. * [ios, macos] Enable to-rgba operator for MGLColor or key path expressions. * [ios, macos] Update predicates and expressions guide to reflect cast changes. * [ios, macos] Update changelogs. * [ios, macos] Clarify color casting usage. --- .../darwin/docs/guides/For Style Authors.md.ejs | 4 +-- .../docs/guides/Predicates and Expressions.md | 2 ++ platform/darwin/src/NSExpression+MGLAdditions.mm | 36 ++++++++++++++++++++++ platform/darwin/test/MGLExpressionTests.mm | 32 +++++++++++++++++++ platform/ios/CHANGELOG.md | 1 + platform/ios/docs/guides/For Style Authors.md | 4 +-- platform/ios/src/UIColor+MGLAdditions.mm | 2 +- platform/macos/CHANGELOG.md | 1 + platform/macos/docs/guides/For Style Authors.md | 4 +-- platform/macos/src/NSColor+MGLAdditions.mm | 10 ++++-- 10 files changed, 87 insertions(+), 9 deletions(-) diff --git a/platform/darwin/docs/guides/For Style Authors.md.ejs b/platform/darwin/docs/guides/For Style Authors.md.ejs index b0ded7bc03..60177e57c2 100644 --- a/platform/darwin/docs/guides/For Style Authors.md.ejs +++ b/platform/darwin/docs/guides/For Style Authors.md.ejs @@ -335,7 +335,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, '<%- cocoaPrefix %>Color')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -372,7 +372,7 @@ In style specification | Method, function, or predicate type | Format string syn `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | <% } -%> -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index c3b3d39a52..18eccda569 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -70,6 +70,8 @@ path or variable into a matching type: * To cast a value to a number, use `CAST(key, 'NSNumber')`. * To cast a value to a string, use `CAST(key, 'NSString')`. +* To cast a value to a color, use `CAST(key, 'UIColor')` on iOS and `CAST(key, 'NSColor')` on macOS. +* To cast an `NSColor` or `UIColor` object to an array, use `CAST(noindex(color), 'NSArray')`. For details about the predicate format string syntax, consult the “Predicate Format String Syntax” chapter of the diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index b2bcf72caf..6dde705d3c 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -802,6 +802,22 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } else if ([op isEqualToString:@"to-string"] || [op isEqualToString:@"string"]) { NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; return [NSExpression expressionWithFormat:@"CAST(%@, 'NSString')", operand]; + } else if ([op isEqualToString:@"to-color"]) { + NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; + + if (argumentObjects.count == 1) { +#if TARGET_OS_IPHONE + return [NSExpression expressionWithFormat:@"CAST(%@, 'UIColor')", operand]; +#else + return [NSExpression expressionWithFormat:@"CAST(%@, 'NSColor')", operand]; +#endif + } + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array); + return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions]; + + } else if ([op isEqualToString:@"to-rgba"]) { + NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject]; + return [NSExpression expressionWithFormat:@"CAST(noindex(%@), 'NSArray')", operand]; } else if ([op isEqualToString:@"get"]) { if (argumentObjects.count == 2) { NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.lastObject]; @@ -1165,6 +1181,26 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { } else if ([type isEqualToString:@"NSNumber"]) { return @[@"to-number", object]; } +#if TARGET_OS_IPHONE + else if ([type isEqualToString:@"UIColor"] || [type isEqualToString:@"MGLColor"]) { + return @[@"to-color", object]; + } +#else + else if ([type isEqualToString:@"NSColor"] || [type isEqualToString:@"MGLColor"]) { + return @[@"to-color", object]; + } +#endif + else if ([type isEqualToString:@"NSArray"]) { + NSExpression *operand = self.arguments.firstObject; + if ([operand expressionType] == NSFunctionExpressionType ) { + operand = self.arguments.firstObject.arguments.firstObject; + } + if (([operand expressionType] != NSConstantValueExpressionType) || + ([operand expressionType] == NSConstantValueExpressionType && + [[operand constantValue] isKindOfClass:[MGLColor class]])) { + return @[@"to-rgba", object]; + } + } [NSException raise:NSInvalidArgumentException format:@"Casting expression to %@ not yet implemented.", type]; } else if ([function isEqualToString:@"MGL_FUNCTION"]) { diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index 186cae609d..4ed1f61eb1 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -668,6 +668,38 @@ using namespace std::string_literals; XCTAssertEqualObjects([compatibilityExpression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5"); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { +#if TARGET_OS_IPHONE + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(x, 'UIColor')"]; +#else + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(x, 'NSColor')"]; +#endif + + NSArray *jsonExpression = @[@"to-color", @[@"get", @"x"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('to-color', x, y, z)"]; + NSArray *jsonExpression = @[@"to-color", @[@"get", @"x"], @[@"get", @"y"], @[@"get", @"z"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex(x), 'NSArray')"]; + NSArray *jsonExpression = @[@"to-rgba", @[@"get", @"x"]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex(%@), 'NSArray')", MGLConstantExpression(MGLColor.blueColor)]; + NSArray *jsonExpression = @[@"to-rgba", @[@"rgb", @0, @0, @255]]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(noindex('x'), 'NSArray')"]; + XCTAssertThrowsSpecificNamed(expression.mgl_jsonExpressionObject, NSException, NSInvalidArgumentException); + } } - (void)testInterpolationExpressionObject { diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index d979021760..057149190c 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -22,6 +22,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) ## 4.0.0 - April 19, 2018 diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md index 9a6e4428eb..d4fb17eb6a 100644 --- a/platform/ios/docs/guides/For Style Authors.md +++ b/platform/ios/docs/guides/For Style Authors.md @@ -325,7 +325,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, 'UIColor')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -357,7 +357,7 @@ In style specification | Method, function, or predicate type | Format string syn `upcase` | `uppercase:` | `uppercase('Elysian Fields')` `rgb` | `+[UIColor colorWithRed:green:blue:alpha:]` | `rgba` | `+[UIColor colorWithRed:green:blue:alpha:]` | -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` diff --git a/platform/ios/src/UIColor+MGLAdditions.mm b/platform/ios/src/UIColor+MGLAdditions.mm index 9ca39acda4..7c1fbddc20 100644 --- a/platform/ios/src/UIColor+MGLAdditions.mm +++ b/platform/ios/src/UIColor+MGLAdditions.mm @@ -66,7 +66,7 @@ return [UIColor colorWithRed:[components[0].constantValue doubleValue] / 255.0 green:[components[1].constantValue doubleValue] / 255.0 blue:[components[2].constantValue doubleValue] / 255.0 - alpha:components.count == 3 ? [components[3].constantValue doubleValue] : 1.0]; + alpha:components.count == 3 ? 1.0 : [components[3].constantValue doubleValue]]; } @end diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index d62ba2ea87..1ae0212b97 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -13,6 +13,7 @@ * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) ## 0.7.0 - April 19, 2018 diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md index 73c4f1105a..08385a66d9 100644 --- a/platform/macos/docs/guides/For Style Authors.md +++ b/platform/macos/docs/guides/For Style Authors.md @@ -318,7 +318,7 @@ In style specification | Method, function, or predicate type | Format string syn `number` | | `string` | | `to-boolean` | `boolValue` | -`to-color` | | +`to-color` | | `CAST(var, 'NSColor')` `to-number` | `mgl_numberWithFallbackValues:` | `CAST(zipCode, 'NSNumber')` `to-string` | `stringValue` | `CAST(ele, 'NSString')` `typeof` | | @@ -350,7 +350,7 @@ In style specification | Method, function, or predicate type | Format string syn `upcase` | `uppercase:` | `uppercase('Elysian Fields')` `rgb` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | `rgba` | `+[NSColor colorWithCalibratedRed:green:blue:alpha:]` | -`to-rgba` | | +`to-rgba` | | `CAST(noindex(var), 'NSArray')` `-` | `from:subtract:` | `2 - 1` `*` | `multiply:by:` | `1 * 2` `/` | `divide:by:` | `1 / 2` diff --git a/platform/macos/src/NSColor+MGLAdditions.mm b/platform/macos/src/NSColor+MGLAdditions.mm index 8c9086ccf7..c73c1a41b7 100644 --- a/platform/macos/src/NSColor+MGLAdditions.mm +++ b/platform/macos/src/NSColor+MGLAdditions.mm @@ -79,8 +79,14 @@ components.push_back(component.doubleValue / 255.0); } - // Alpha - components.back() *= 255.0; + + if (components.size() < 4) { + components.push_back(1.0); + } else { + // Alpha + components.back() *= 255.0; + } + // macOS 10.12 Sierra and below uses calibrated RGB by default. if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) { -- cgit v1.2.1 From a24b8fd206265b597c6b12ee8f5b335a7027851e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 7 May 2018 17:28:48 +0200 Subject: [android] - update changelog to v6.1.1 --- platform/android/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 9d9d22e1f9..384dcdf883 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.1.1 - May 7, 2018 + - Update telemetry to 3.1.0 [#11855](https://github.com/mapbox/mapbox-gl-native/pull/11855) + ## 6.1.0 - May 4, 2018 - Unwrap LatLngBounds during JNI conversion [#11807](https://github.com/mapbox/mapbox-gl-native/pull/11807) - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800) -- cgit v1.2.1 From 0141f65e7be21b067957a8e8cb3474ab46e12b48 Mon Sep 17 00:00:00 2001 From: Randall Lee Date: Mon, 7 May 2018 17:57:42 -0400 Subject: [iOS] - Update telemetry certificate pinning (#11845) * Update telemetry certificate pinning * Load both CN certificates * [ios] Use China events endpoint with China API endpoint * Update CHANGELOG.md --- platform/darwin/src/MGLNetworkConfiguration.h | 6 ++++++ platform/darwin/src/MGLNetworkConfiguration.m | 3 +++ platform/ios/CHANGELOG.md | 4 ++++ platform/ios/ios.xcodeproj/project.pbxproj | 12 ++++++++++++ .../ios/resources/api_mapbox_cn-digicert_2018.der | Bin 0 -> 1704 bytes .../ios/resources/api_mapbox_cn-geotrust_2018.der | Bin 0 -> 1578 bytes platform/ios/src/MGLAPIClient.m | 18 +++++++++++++++++- 7 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 platform/ios/resources/api_mapbox_cn-digicert_2018.der create mode 100644 platform/ios/resources/api_mapbox_cn-geotrust_2018.der diff --git a/platform/darwin/src/MGLNetworkConfiguration.h b/platform/darwin/src/MGLNetworkConfiguration.h index 644291ee13..f1fe7bab2c 100644 --- a/platform/darwin/src/MGLNetworkConfiguration.h +++ b/platform/darwin/src/MGLNetworkConfiguration.h @@ -2,6 +2,12 @@ NS_ASSUME_NONNULL_BEGIN +/// The default base URL for Mapbox APIs other than the telemetry API. +extern NSString * const MGLDefaultMapboxAPIBaseURL; + +/// The PRC base URL for Mapbox APIs other than the telemetry API. +extern NSString * const MGLChinaMapboxAPIBaseURL; + /** The MGLNetworkConfiguration object provides a global way to set a base API URL for retrieval of map data, styles, and other resources. diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m index 82d333dc99..d0ee01c5a2 100644 --- a/platform/darwin/src/MGLNetworkConfiguration.m +++ b/platform/darwin/src/MGLNetworkConfiguration.m @@ -1,5 +1,8 @@ #import "MGLNetworkConfiguration.h" +NSString * const MGLDefaultMapboxAPIBaseURL = @"https://api.mapbox.com"; +NSString * const MGLChinaMapboxAPIBaseURL = @"https://api.mapbox.cn"; + @implementation MGLNetworkConfiguration + (void)load { diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 392df1d1ea..1b0806b689 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,6 +2,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. +## 3.7.8 - May 7, 2018 + +* Improved compatibility with Mapbox China APIs. ([#11845](https://github.com/mapbox/mapbox-gl-native/pull/11845)) + ## 3.7.7 - May 3, 2018 * Fixed a crash when removing an `MGLOfflinePack`. ([#11821](https://github.com/mapbox/mapbox-gl-native/issues/11821)) diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index bbd4067534..2bc216ff1c 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -247,6 +247,10 @@ 968F36B51E4D128D003A5522 /* MGLDistanceFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3557F7AE1E1D27D300CCA5E6 /* MGLDistanceFormatter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 96E027231E57C76E004B8E66 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 96E027251E57C76E004B8E66 /* Localizable.strings */; }; 96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */ = {isa = PBXBuildFile; fileRef = 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */; }; + AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; }; + AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; }; + AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; }; + AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; }; AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; @@ -756,6 +760,8 @@ 96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = ""; }; 96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = ""; }; + AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-geotrust_2018.der"; sourceTree = ""; }; + AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-digicert_2018.der"; sourceTree = ""; }; AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = ""; }; AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = ""; }; CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = ""; }; @@ -1479,6 +1485,8 @@ DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */, DA8933EF1CCD387900E68420 /* strip-frameworks.sh */, 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */, + AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */, + AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */, 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */, 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */, 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */, @@ -2225,12 +2233,14 @@ files = ( DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */, DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */, + AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */, 960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */, DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */, DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */, DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */, 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */, 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */, + AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */, 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */, 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */, 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */, @@ -2241,6 +2251,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */, DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */, DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */, 960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */, @@ -2250,6 +2261,7 @@ 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */, 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */, 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */, + AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */, 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/platform/ios/resources/api_mapbox_cn-digicert_2018.der b/platform/ios/resources/api_mapbox_cn-digicert_2018.der new file mode 100644 index 0000000000..e458713337 Binary files /dev/null and b/platform/ios/resources/api_mapbox_cn-digicert_2018.der differ diff --git a/platform/ios/resources/api_mapbox_cn-geotrust_2018.der b/platform/ios/resources/api_mapbox_cn-geotrust_2018.der new file mode 100644 index 0000000000..e3d4b222ae Binary files /dev/null and b/platform/ios/resources/api_mapbox_cn-geotrust_2018.der differ diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m index 8a987d76d8..68e78835c3 100644 --- a/platform/ios/src/MGLAPIClient.m +++ b/platform/ios/src/MGLAPIClient.m @@ -2,9 +2,11 @@ #import "NSBundle+MGLAdditions.h" #import "NSData+MGLAdditions.h" #import "MGLAccountManager.h" +#import "MGLNetworkConfiguration.h" static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS"; static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com"; +static NSString * const MGLAPIClientChinaBaseURL = @"https://events.mapbox.cn"; static NSString * const MGLAPIClientEventsPath = @"events/v2"; static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent"; @@ -21,6 +23,8 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; @property (nonatomic, copy) NSData *geoTrustCert_2016; @property (nonatomic, copy) NSData *digicertCert_2017; @property (nonatomic, copy) NSData *geoTrustCert_2017; +@property (nonatomic, copy) NSData *digicertCert_cn_2018; +@property (nonatomic, copy) NSData *geoTrustCert_cn_2018; @property (nonatomic, copy) NSData *testServerCert; @property (nonatomic, copy) NSString *userAgent; @property (nonatomic) BOOL usesTestServer; @@ -102,6 +106,8 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) { self.baseURL = testServerURL; self.usesTestServer = YES; + } else if ([[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxAPIBaseURL"] isEqualToString:MGLChinaMapboxAPIBaseURL]) { + self.baseURL = [NSURL URLWithString:MGLAPIClientChinaBaseURL]; } else { self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL]; } @@ -117,6 +123,10 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; self.geoTrustCert_2017 = certificate; [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"]; self.digicertCert_2017 = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_cn-geotrust_2018"]; + self.geoTrustCert_cn_2018 = certificate; + [self loadCertificate:&certificate withResource:@"api_mapbox_cn-digicert_2018"]; + self.digicertCert_cn_2018 = certificate; [self loadCertificate:&certificate withResource:@"api_mapbox_staging"]; self.testServerCert = certificate; } @@ -174,17 +184,23 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST"; // Look for a pinned certificate in the server's certificate chain CFIndex numKeys = SecTrustGetCertificateCount(serverTrust); - // Check certs in the following order: digicert 2016, digicert 2017, geotrust 2016, geotrust 2017 + // Check certs in the following order: digicert 2016, digicert 2017, digicert CN 2018, geotrust 2016, geotrust 2017, geotrust CN 2018 found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; if (!found) { found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; } + if (!found) { + found = [self evaluateCertificateWithCertificateData:self.digicertCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; + } if (!found) { found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; } if (!found) { found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; } + if (!found) { + found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler]; + } // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server if (!found && _usesTestServer) { -- cgit v1.2.1 From 30e8798d768847d821128b3ce8ce348811f53a22 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 7 May 2018 17:07:13 -0400 Subject: [ios] Prepare ios-v3.7.8 release --- platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 9d714ccb6a..265331791a 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.7' + version = '3.7.8' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index ebf758cbf6..3e3311f912 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.7' + version = '3.7.8' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index d8ac87363b..6eaac7b73b 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '3.7.7' + version = '3.7.8' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From d1dff1d6e782bc6e9d7a7df27e566bf1f1cf862b Mon Sep 17 00:00:00 2001 From: tobrun Date: Fri, 4 May 2018 12:44:03 +0200 Subject: [android] - build release with debug signing --- platform/android/MapboxGLAndroidSDKTestApp/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index edf860a946..80be26d3ae 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -41,6 +41,7 @@ android { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.debug } } -- cgit v1.2.1 From ee1008870284d2956c4d246bd8751e032ee91898 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 4 May 2018 14:45:29 -0400 Subject: [ios] Fix MGLAnnotationView.rotatesToMatchCamera clobbering other transforms --- platform/ios/CHANGELOG.md | 1 + platform/ios/src/MGLAnnotationView.mm | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 057149190c..9520a5d24e 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -14,6 +14,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` is `YES`. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) +* Fixed `MGLAnnotationView.rotatesToMatchCamera` overriding other transforms that might be applied to annotation views that had this property enabled. ([#11842](https://github.com/mapbox/mapbox-gl-native/pull/11842)) ### Other changes diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm index 1c53ba507a..46b0f56a79 100644 --- a/platform/ios/src/MGLAnnotationView.mm +++ b/platform/ios/src/MGLAnnotationView.mm @@ -11,8 +11,9 @@ @property (nonatomic, readwrite, nullable) NSString *reuseIdentifier; @property (nonatomic, readwrite) CATransform3D lastAppliedScaleTransform; -@property (nonatomic, readwrite) CATransform3D lastAppliedRotateTransform; @property (nonatomic, readwrite) CGFloat lastPitch; +@property (nonatomic, readwrite) CATransform3D lastAppliedRotationTransform; +@property (nonatomic, readwrite) CGFloat lastDirection; @property (nonatomic, weak) UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, weak) UILongPressGestureRecognizer *longPressRecognizer; @property (nonatomic, weak) MGLMapView *mapView; @@ -43,9 +44,9 @@ - (void)commonInitWithAnnotation:(nullable id)annotation reuseIdentifier:(nullable NSString *)reuseIdentifier { _lastAppliedScaleTransform = CATransform3DIdentity; + _lastAppliedRotationTransform = CATransform3DIdentity; _annotation = annotation; _reuseIdentifier = [reuseIdentifier copy]; - _scalesWithViewingDistance = NO; _enabled = YES; } @@ -159,7 +160,7 @@ // We keep track of each viewing distance scale transform that we apply. Each iteration, // we can account for it so that we don't get cumulative scaling every time we move. - // We also avoid clobbering any existing transform passed in by the client, too. + // We also avoid clobbering any existing transform passed in by the client or this SDK. CATransform3D undoOfLastScaleTransform = CATransform3DInvert(_lastAppliedScaleTransform); CATransform3D newScaleTransform = CATransform3DMakeScale(pitchAdjustedScale, pitchAdjustedScale, 1); CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastScaleTransform, newScaleTransform); @@ -181,11 +182,20 @@ { if (self.rotatesToMatchCamera == NO) return; - CGFloat directionRad = self.mapView.direction * M_PI / 180.0; - CATransform3D newRotateTransform = CATransform3DMakeRotation(-directionRad, 0, 0, 1); - self.layer.transform = CATransform3DConcat(CATransform3DIdentity, newRotateTransform); - - _lastAppliedRotateTransform = newRotateTransform; + CGFloat direction = -MGLRadiansFromDegrees(self.mapView.direction); + + // Return early if the map view has the same rotation as the already-applied transform. + if (direction == _lastDirection) return; + _lastDirection = direction; + + // We keep track of each rotation transform that we apply. Each iteration, + // we can account for it so that we don't get cumulative rotation every time we move. + // We also avoid clobbering any existing transform passed in by the client or this SDK. + CATransform3D undoOfLastRotationTransform = CATransform3DInvert(_lastAppliedRotationTransform); + CATransform3D newRotationTransform = CATransform3DMakeRotation(direction, 0, 0, 1); + CATransform3D effectiveTransform = CATransform3DConcat(undoOfLastRotationTransform, newRotationTransform); + self.layer.transform = CATransform3DConcat(self.layer.transform, effectiveTransform); + _lastAppliedRotationTransform = newRotationTransform; } #pragma mark - Draggable -- cgit v1.2.1 From a1195f6a9dc57910f7c3e6c9217e3041929a01fb Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 8 May 2018 10:40:35 -0700 Subject: Avoid exceptions for flow control during database creation Unfortuntely, it's difficult to avoid all exceptions, because sqlite3_open_v2 does not reliably return SQLITE_NOTADB if the file is not a database. However, this should avoid cases where developers misinterpret the SQLITE_CANTOPEN exception as a crash, which is the common case. --- platform/default/mbgl/storage/offline_database.cpp | 81 ++++++++++++--------- platform/default/mbgl/storage/offline_database.hpp | 1 - platform/default/sqlite3.cpp | 37 ++++++---- platform/default/sqlite3.hpp | 6 +- platform/qt/src/sqlite3.cpp | 85 ++++++++++++---------- test/storage/offline_database.test.cpp | 12 +-- test/storage/sqlite.test.cpp | 17 ++--- 7 files changed, 137 insertions(+), 102 deletions(-) diff --git a/platform/default/mbgl/storage/offline_database.cpp b/platform/default/mbgl/storage/offline_database.cpp index 4611e69f43..2b872ab87b 100644 --- a/platform/default/mbgl/storage/offline_database.cpp +++ b/platform/default/mbgl/storage/offline_database.cpp @@ -27,44 +27,58 @@ OfflineDatabase::~OfflineDatabase() { } } -void OfflineDatabase::connect(int flags) { - db = std::make_unique(path.c_str(), flags); - db->setBusyTimeout(Milliseconds::max()); - db->exec("PRAGMA foreign_keys = ON"); -} - void OfflineDatabase::ensureSchema() { if (path != ":memory:") { - try { - connect(mapbox::sqlite::ReadWrite); - - switch (userVersion()) { - case 0: break; // cache-only database; ok to delete - case 1: break; // cache-only database; ok to delete - case 2: migrateToVersion3(); // fall through - case 3: // no-op and fall through - case 4: migrateToVersion5(); // fall through - case 5: migrateToVersion6(); // fall through - case 6: return; - default: break; // downgrade, delete the database - } - - removeExisting(); - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - } catch (mapbox::sqlite::Exception& ex) { - if (ex.code != mapbox::sqlite::ResultCode::CantOpen && ex.code != mapbox::sqlite::ResultCode::NotADB) { + auto result = mapbox::sqlite::Database::tryOpen(path, mapbox::sqlite::ReadWrite); + if (result.is()) { + const auto& ex = result.get(); + if (ex.code == mapbox::sqlite::ResultCode::NotADB) { + // Corrupted; blow it away. + removeExisting(); + } else if (ex.code == mapbox::sqlite::ResultCode::CantOpen) { + // Doesn't exist yet. + } else { Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what()); - throw; + throw ex; } - + } else { try { + db = std::make_unique(std::move(result.get())); + db->setBusyTimeout(Milliseconds::max()); + db->exec("PRAGMA foreign_keys = ON"); + + switch (userVersion()) { + case 0: + case 1: + // cache-only database; ok to delete + removeExisting(); + break; + case 2: + migrateToVersion3(); + // fall through + case 3: + case 4: + migrateToVersion5(); + // fall through + case 5: + migrateToVersion6(); + // fall through + case 6: + // happy path; we're done + return; + default: + // downgrade, delete the database + removeExisting(); + break; + } + } catch (const mapbox::sqlite::Exception& ex) { + // Unfortunately, SQLITE_NOTADB is not always reported upon opening the database. + // Apparently sometimes it is delayed until the first read operation. if (ex.code == mapbox::sqlite::ResultCode::NotADB) { removeExisting(); + } else { + throw; } - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - } catch (...) { - Log::Error(Event::Database, "Unexpected error creating database: %s", util::toString(std::current_exception()).c_str()); - throw; } } } @@ -72,9 +86,9 @@ void OfflineDatabase::ensureSchema() { try { #include "offline_schema.cpp.include" - connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create); - - // If you change the schema you must write a migration from the previous version. + db = std::make_unique(mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadWrite | mapbox::sqlite::Create)); + db->setBusyTimeout(Milliseconds::max()); + db->exec("PRAGMA foreign_keys = ON"); db->exec("PRAGMA auto_vacuum = INCREMENTAL"); db->exec("PRAGMA journal_mode = DELETE"); db->exec("PRAGMA synchronous = FULL"); @@ -93,6 +107,7 @@ int OfflineDatabase::userVersion() { void OfflineDatabase::removeExisting() { Log::Warning(Event::Database, "Removing existing incompatible offline database"); + statements.clear(); db.reset(); try { diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp index 9673ad8212..e0d90a9a15 100644 --- a/platform/default/mbgl/storage/offline_database.hpp +++ b/platform/default/mbgl/storage/offline_database.hpp @@ -59,7 +59,6 @@ public: uint64_t getOfflineMapboxTileCount(); private: - void connect(int flags); int userVersion(); void ensureSchema(); void removeExisting(); diff --git a/platform/default/sqlite3.cpp b/platform/default/sqlite3.cpp index 8f9c34191f..776adc8f3a 100644 --- a/platform/default/sqlite3.cpp +++ b/platform/default/sqlite3.cpp @@ -14,27 +14,20 @@ namespace sqlite { class DatabaseImpl { public: - DatabaseImpl(const char* filename, int flags) + DatabaseImpl(sqlite3* db_) + : db(db_) { - const int error = sqlite3_open_v2(filename, &db, flags, nullptr); - if (error != SQLITE_OK) { - const auto message = sqlite3_errmsg(db); - db = nullptr; - throw Exception { error, message }; - } } ~DatabaseImpl() { - if (!db) return; - const int error = sqlite3_close(db); if (error != SQLITE_OK) { mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", sqlite3_errmsg(db), error); } } - sqlite3* db = nullptr; + sqlite3* db; }; class StatementImpl { @@ -164,11 +157,29 @@ const static bool sqliteVersionCheck __attribute__((unused)) = []() { return true; }(); -Database::Database(const std::string &filename, int flags) - : impl(std::make_unique(filename.c_str(), flags)) -{ +mapbox::util::variant Database::tryOpen(const std::string &filename, int flags) { + sqlite3* db = nullptr; + const int error = sqlite3_open_v2(filename.c_str(), &db, flags, nullptr); + if (error != SQLITE_OK) { + const auto message = sqlite3_errmsg(db); + return Exception { error, message }; + } + return Database(std::make_unique(db)); } +Database Database::open(const std::string &filename, int flags) { + auto result = tryOpen(filename, flags); + if (result.is()) { + throw result.get(); + } else { + return std::move(result.get()); + } +} + +Database::Database(std::unique_ptr impl_) + : impl(std::move(impl_)) +{} + Database::Database(Database &&other) : impl(std::move(other.impl)) {} diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp index 20d09b550c..cdc94298fe 100644 --- a/platform/default/sqlite3.hpp +++ b/platform/default/sqlite3.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace mapbox { namespace sqlite { @@ -71,11 +72,14 @@ class Query; class Database { private: + Database(std::unique_ptr); Database(const Database &) = delete; Database &operator=(const Database &) = delete; public: - Database(const std::string &filename, int flags = 0); + static mapbox::util::variant tryOpen(const std::string &filename, int flags = 0); + static Database open(const std::string &filename, int flags = 0); + Database(Database &&); ~Database(); Database &operator=(Database &&); diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp index 4bcaea0e31..351991f881 100644 --- a/platform/qt/src/sqlite3.cpp +++ b/platform/qt/src/sqlite3.cpp @@ -52,15 +52,6 @@ void checkDatabaseError(const QSqlDatabase &db) { } } -void checkDatabaseOpenError(const QSqlDatabase &db) { - // Assume every error when opening the data as CANTOPEN. Qt - // always returns -1 for `nativeErrorCode()` on database errors. - QSqlError lastError = db.lastError(); - if (lastError.type() != QSqlError::NoError) { - throw Exception { ResultCode::CantOpen, "Error opening the database." }; - } -} - namespace { QString incrementCounter() { static QAtomicInt count = 0; @@ -70,32 +61,9 @@ namespace { class DatabaseImpl { public: - DatabaseImpl(const char* filename, int flags) - : connectionName(QString::number(uint64_t(QThread::currentThread())) + incrementCounter()) + DatabaseImpl(QString connectionName_) + : connectionName(std::move(connectionName_)) { - if (!QSqlDatabase::drivers().contains("QSQLITE")) { - throw Exception { ResultCode::CantOpen, "SQLite driver not found." }; - } - - assert(!QSqlDatabase::contains(connectionName)); - auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); - - QString connectOptions = db.connectOptions(); - if (flags & OpenFlag::ReadOnly) { - if (!connectOptions.isEmpty()) connectOptions.append(';'); - connectOptions.append("QSQLITE_OPEN_READONLY"); - } - if (flags & OpenFlag::SharedCache) { - if (!connectOptions.isEmpty()) connectOptions.append(';'); - connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE"); - } - - db.setConnectOptions(connectOptions); - db.setDatabaseName(QString(filename)); - - if (!db.open()) { - checkDatabaseOpenError(db); - } } ~DatabaseImpl() { @@ -127,12 +95,51 @@ public: template using optional = std::experimental::optional; +mapbox::util::variant Database::tryOpen(const std::string &filename, int flags) { + if (!QSqlDatabase::drivers().contains("QSQLITE")) { + return Exception { ResultCode::CantOpen, "SQLite driver not found." }; + } -Database::Database(const std::string& file, int flags) - : impl(std::make_unique(file.c_str(), flags)) { - assert(impl); + QString connectionName = QString::number(uint64_t(QThread::currentThread())) + incrementCounter(); + + assert(!QSqlDatabase::contains(connectionName)); + auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); + + QString connectOptions = db.connectOptions(); + if (flags & OpenFlag::ReadOnly) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_OPEN_READONLY"); + } + if (flags & OpenFlag::SharedCache) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE"); + } + + db.setConnectOptions(connectOptions); + db.setDatabaseName(QString(filename.c_str())); + + if (!db.open()) { + // Assume every error when opening the data as CANTOPEN. Qt + // always returns -1 for `nativeErrorCode()` on database errors. + return Exception { ResultCode::CantOpen, "Error opening the database." }; + } + + return Database(std::make_unique(connectionName)); +} + +Database Database::open(const std::string &filename, int flags) { + auto result = tryOpen(filename, flags); + if (result.is()) { + throw result.get(); + } else { + return std::move(result.get()); + } } +Database::Database(std::unique_ptr impl_) + : impl(std::move(impl_)) +{} + Database::Database(Database &&other) : impl(std::move(other.impl)) { assert(impl); @@ -165,7 +172,9 @@ void Database::setBusyTimeout(std::chrono::milliseconds timeout) { } db.setConnectOptions(connectOptions); if (!db.open()) { - checkDatabaseOpenError(db); + // Assume every error when opening the data as CANTOPEN. Qt + // always returns -1 for `nativeErrorCode()` on database errors. + throw Exception { ResultCode::CantOpen, "Error opening the database." }; } } diff --git a/test/storage/offline_database.test.cpp b/test/storage/offline_database.test.cpp index 620e6eaa6d..656231eebe 100644 --- a/test/storage/offline_database.test.cpp +++ b/test/storage/offline_database.test.cpp @@ -66,7 +66,7 @@ TEST(OfflineDatabase, TEST_REQUIRES_WRITE(SchemaVersion)) { std::string path("test/fixtures/offline_database/offline.db"); { - mapbox::sqlite::Database db{ path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("PRAGMA user_version = 1"); } @@ -599,7 +599,7 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) { } static int databasePageCount(const std::string& path) { - mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); mapbox::sqlite::Statement stmt{ db, "pragma page_count" }; mapbox::sqlite::Query query{ stmt }; query.run(); @@ -607,7 +607,7 @@ static int databasePageCount(const std::string& path) { } static int databaseUserVersion(const std::string& path) { - mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); mapbox::sqlite::Statement stmt{ db, "pragma user_version" }; mapbox::sqlite::Query query{ stmt }; query.run(); @@ -615,7 +615,7 @@ static int databaseUserVersion(const std::string& path) { } static std::string databaseJournalMode(const std::string& path) { - mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" }; mapbox::sqlite::Query query{ stmt }; query.run(); @@ -623,7 +623,7 @@ static std::string databaseJournalMode(const std::string& path) { } static int databaseSyncMode(const std::string& path) { - mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); mapbox::sqlite::Statement stmt{ db, "pragma synchronous" }; mapbox::sqlite::Query query{ stmt }; query.run(); @@ -631,7 +631,7 @@ static int databaseSyncMode(const std::string& path) { } static std::vector databaseTableColumns(const std::string& path, const std::string& name) { - mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly }; + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(path, mapbox::sqlite::ReadOnly); const auto sql = std::string("pragma table_info(") + name + ")"; mapbox::sqlite::Statement stmt{ db, sql.c_str() }; mapbox::sqlite::Query query{ stmt }; diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp index 918200181f..553736a0b1 100644 --- a/test/storage/sqlite.test.cpp +++ b/test/storage/sqlite.test.cpp @@ -6,7 +6,7 @@ TEST(SQLite, Statement) { using namespace mbgl; - mapbox::sqlite::Database db(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); + mapbox::sqlite::Database db = mapbox::sqlite::Database::open(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite); db.exec("CREATE TABLE test (id INTEGER);"); mapbox::sqlite::Statement stmt1{ db, "INSERT INTO test (id) VALUES (?1);" }; @@ -28,13 +28,10 @@ TEST(SQLite, Statement) { ASSERT_EQ(query2.changes(), 1u); } -TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) { - try { - // Should throw a CANTOPEN when the database doesn't exist, - // make sure all the backends behave the same way. - mapbox::sqlite::Database("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); - FAIL(); - } catch (mapbox::sqlite::Exception& ex) { - ASSERT_EQ(ex.code, mapbox::sqlite::ResultCode::CantOpen); - } +TEST(SQLite, TEST_REQUIRES_WRITE(TryOpen)) { + // Should return a CANTOPEN exception when the database doesn't exist, + // make sure all the backends behave the same way. + auto result = mapbox::sqlite::Database::tryOpen("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly); + ASSERT_TRUE(result.is()); + ASSERT_EQ(result.get().code, mapbox::sqlite::ResultCode::CantOpen); } -- cgit v1.2.1 From a4e2c1af1fd83b22ef4ee57ab19a15616224f8b8 Mon Sep 17 00:00:00 2001 From: Lucas Wojciechowski Date: Thu, 10 May 2018 12:37:14 -0700 Subject: [core] Convert "legacy" filters directly into expressions (#11610) Ports the specialized filter-* expressions from GL JS, adding them to src/mbgl/style/expression/compound_expression.cpp --- benchmark/parse/filter.benchmark.cpp | 1 - cmake/core-files.cmake | 2 - .../mbgl/style/expression/compound_expression.hpp | 15 +- include/mbgl/style/expression/literal.hpp | 3 + include/mbgl/style/filter.hpp | 276 ++--------------- include/mbgl/style/filter_evaluator.hpp | 55 ---- platform/android/src/style/layers/layer.cpp | 12 +- platform/darwin/src/MGLCircleStyleLayer.mm | 2 +- platform/darwin/src/MGLFillExtrusionStyleLayer.mm | 2 +- platform/darwin/src/MGLFillStyleLayer.mm | 2 +- platform/darwin/src/MGLHeatmapStyleLayer.mm | 2 +- platform/darwin/src/MGLLineStyleLayer.mm | 2 +- platform/darwin/src/MGLStyleLayer.mm.ejs | 2 +- platform/darwin/src/MGLSymbolStyleLayer.mm | 2 +- platform/darwin/src/NSPredicate+MGLAdditions.mm | 207 +------------ platform/darwin/test/MGLPredicateTests.mm | 214 ------------- platform/glfw/glfw_view.cpp | 9 +- src/mbgl/geometry/feature_index.cpp | 1 - src/mbgl/layout/symbol_layout.cpp | 1 - src/mbgl/style/conversion/filter.cpp | 343 ++++++++------------- src/mbgl/style/conversion/stringify.hpp | 159 +--------- src/mbgl/style/expression/compound_expression.cpp | 328 +++++++++++++++++--- src/mbgl/style/expression/literal.cpp | 8 + src/mbgl/style/filter.cpp | 12 +- src/mbgl/style/filter_evaluator.cpp | 225 -------------- src/mbgl/tile/custom_geometry_tile.cpp | 1 - src/mbgl/tile/geojson_tile.cpp | 1 - src/mbgl/tile/geometry_tile.cpp | 1 - src/mbgl/tile/geometry_tile_worker.cpp | 1 - test/api/query.test.cpp | 15 +- test/renderer/group_by_layout.test.cpp | 5 +- test/style/conversion/stringify.test.cpp | 9 +- test/style/filter.test.cpp | 65 +++- test/style/style_layer.test.cpp | 2 +- 34 files changed, 578 insertions(+), 1407 deletions(-) delete mode 100644 include/mbgl/style/filter_evaluator.hpp delete mode 100644 src/mbgl/style/filter_evaluator.cpp diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index e4cf635256..4e39237138 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 0853097630..208c598872 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -360,7 +360,6 @@ set(MBGL_CORE_FILES include/mbgl/style/conversion.hpp include/mbgl/style/data_driven_property_value.hpp include/mbgl/style/filter.hpp - include/mbgl/style/filter_evaluator.hpp include/mbgl/style/heatmap_color_property_value.hpp include/mbgl/style/image.hpp include/mbgl/style/layer.hpp @@ -377,7 +376,6 @@ set(MBGL_CORE_FILES src/mbgl/style/custom_tile_loader.cpp src/mbgl/style/custom_tile_loader.hpp src/mbgl/style/filter.cpp - src/mbgl/style/filter_evaluator.cpp src/mbgl/style/image.cpp src/mbgl/style/image_impl.cpp src/mbgl/style/image_impl.hpp diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp index 6baaae862f..431afb4d13 100644 --- a/include/mbgl/style/expression/compound_expression.hpp +++ b/include/mbgl/style/expression/compound_expression.hpp @@ -36,7 +36,7 @@ template struct Varargs : std::vector { using std::vector::vector; }; namespace detail { -// Base class for the Signature structs that are used to determine the +// Base class for the Signature structs that are used to determine // each CompoundExpression definition's type::Type data from the type of its // "evaluate" function. struct SignatureBase { @@ -137,10 +137,21 @@ ParseResult parseCompoundExpression(const std::string name, const mbgl::style::c ParseResult createCompoundExpression(const CompoundExpressionRegistry::Definition& definition, std::vector> args, ParsingContext& ctx); - + ParseResult createCompoundExpression(const std::string& name, std::vector> args, ParsingContext& ctx); +// Convenience method for use expressions that have 0, 1, or 2 args. +ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + ParsingContext& ctx); + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + std::unique_ptr arg2, + ParsingContext& ctx); } // namespace expression } // namespace style diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp index a00c468efc..d3b3a20cce 100644 --- a/include/mbgl/style/expression/literal.hpp +++ b/include/mbgl/style/expression/literal.hpp @@ -51,6 +51,9 @@ private: Value value; }; +std::unique_ptr createLiteral(const char* value); +std::unique_ptr createLiteral(Value value); + } // namespace expression } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp index ccf8dce188..ce4015bb69 100644 --- a/include/mbgl/style/filter.hpp +++ b/include/mbgl/style/filter.hpp @@ -11,271 +11,31 @@ namespace mbgl { namespace style { - -class Filter; - -class NullFilter { -public: - friend bool operator==(const NullFilter&, const NullFilter&) { - return true; - } -}; - -class EqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const EqualsFilter& lhs, const EqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class NotEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const NotEqualsFilter& lhs, const NotEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class LessThanFilter { -public: - std::string key; - Value value; - - friend bool operator==(const LessThanFilter& lhs, const LessThanFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class LessThanEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const LessThanEqualsFilter& lhs, const LessThanEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class GreaterThanFilter { -public: - std::string key; - Value value; - - friend bool operator==(const GreaterThanFilter& lhs, const GreaterThanFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class GreaterThanEqualsFilter { -public: - std::string key; - Value value; - - friend bool operator==(const GreaterThanEqualsFilter& lhs, const GreaterThanEqualsFilter& rhs) { - return std::tie(lhs.key, lhs.value) == std::tie(rhs.key, rhs.value); - } -}; - -class InFilter { -public: - std::string key; - std::vector values; - - friend bool operator==(const InFilter& lhs, const InFilter& rhs) { - return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); - } -}; - -class NotInFilter { -public: - std::string key; - std::vector values; - - friend bool operator==(const NotInFilter& lhs, const NotInFilter& rhs) { - return std::tie(lhs.key, lhs.values) == std::tie(rhs.key, rhs.values); - } -}; - -class AnyFilter { -public: - std::vector filters; - - friend bool operator==(const AnyFilter& lhs, const AnyFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class AllFilter { -public: - std::vector filters; - - friend bool operator==(const AllFilter& lhs, const AllFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class NoneFilter { -public: - std::vector filters; - - friend bool operator==(const NoneFilter& lhs, const NoneFilter& rhs) { - return lhs.filters == rhs.filters; - } -}; - -class HasFilter { -public: - std::string key; - - friend bool operator==(const HasFilter& lhs, const HasFilter& rhs) { - return lhs.key == rhs.key; - } -}; - -class NotHasFilter { -public: - std::string key; - - friend bool operator==(const NotHasFilter& lhs, const NotHasFilter& rhs) { - return lhs.key == rhs.key; - } -}; - - -class TypeEqualsFilter { -public: - FeatureType value; - - friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class TypeNotEqualsFilter { -public: - FeatureType value; - - friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class TypeInFilter { -public: - std::vector values; - - friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class TypeNotInFilter { -public: - std::vector values; - - friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - - -class IdentifierEqualsFilter { -public: - FeatureIdentifier value; - - friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class IdentifierNotEqualsFilter { -public: - FeatureIdentifier value; - - friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) { - return lhs.value == rhs.value; - } -}; - -class IdentifierInFilter { -public: - std::vector values; - - friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class IdentifierNotInFilter { -public: - std::vector values; - - friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) { - return lhs.values == rhs.values; - } -}; - -class HasIdentifierFilter { + +class Filter { public: - friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) { - return true; + optional> expression; + + Filter() : expression() {} + + Filter(expression::ParseResult _expression) : expression(std::move(*_expression)) { + assert(!expression || *expression != nullptr); } -}; + + bool operator()(const expression::EvaluationContext& context) const; -class NotHasIdentifierFilter { -public: - friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) { - return true; + friend bool operator==(const Filter& lhs, const Filter& rhs) { + if (!lhs.expression || !rhs.expression) { + return lhs.expression == rhs.expression; + } else { + return *(lhs.expression) == *(rhs.expression); + } } -}; -class ExpressionFilter { -public: - std::shared_ptr expression; - - friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) { - return *(lhs.expression) == *(rhs.expression); + friend bool operator!=(const Filter& lhs, const Filter& rhs) { + return !(lhs == rhs); } }; - -using FilterBase = variant< - class NullFilter, - class EqualsFilter, - class NotEqualsFilter, - class LessThanFilter, - class LessThanEqualsFilter, - class GreaterThanFilter, - class GreaterThanEqualsFilter, - class InFilter, - class NotInFilter, - class AnyFilter, - class AllFilter, - class NoneFilter, - class HasFilter, - class NotHasFilter, - class TypeEqualsFilter, - class TypeNotEqualsFilter, - class TypeInFilter, - class TypeNotInFilter, - class IdentifierEqualsFilter, - class IdentifierNotEqualsFilter, - class IdentifierInFilter, - class IdentifierNotInFilter, - class HasIdentifierFilter, - class NotHasIdentifierFilter, - class ExpressionFilter>; - -class Filter : public FilterBase { -public: - using FilterBase::FilterBase; - bool operator()(const expression::EvaluationContext& context) const; -}; - } // namespace style } // namespace mbgl diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp deleted file mode 100644 index a4a4098b3f..0000000000 --- a/include/mbgl/style/filter_evaluator.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace mbgl { -namespace style { - -/* - A visitor that evaluates a `Filter` for a given feature. - - Use via `Filter::operator()`. For example: - - if (filter(feature)) { - // matches the filter - } else { - // does not match - } -*/ -class FilterEvaluator { -public: - const expression::EvaluationContext context; - - bool operator()(const NullFilter&) const; - bool operator()(const EqualsFilter& filter) const; - bool operator()(const NotEqualsFilter& filter) const; - bool operator()(const LessThanFilter& filter) const; - bool operator()(const LessThanEqualsFilter& filter) const; - bool operator()(const GreaterThanFilter& filter) const; - bool operator()(const GreaterThanEqualsFilter& filter) const; - bool operator()(const InFilter& filter) const; - bool operator()(const NotInFilter& filter) const; - bool operator()(const AnyFilter& filter) const; - bool operator()(const AllFilter& filter) const; - bool operator()(const NoneFilter& filter) const; - bool operator()(const HasFilter& filter) const; - bool operator()(const NotHasFilter& filter) const; - bool operator()(const TypeEqualsFilter& filter) const; - bool operator()(const TypeNotEqualsFilter& filter) const; - bool operator()(const TypeInFilter& filter) const; - bool operator()(const TypeNotInFilter& filter) const; - bool operator()(const IdentifierEqualsFilter& filter) const; - bool operator()(const IdentifierNotEqualsFilter& filter) const; - bool operator()(const IdentifierInFilter& filter) const; - bool operator()(const IdentifierNotInFilter& filter) const; - bool operator()(const HasIdentifierFilter&) const; - bool operator()(const NotHasIdentifierFilter&) const; - bool operator()(const ExpressionFilter&) const; - -}; - -} // namespace style -} // namespace mbgl diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index 6fe6e3cb29..c7a6bcd3a3 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -157,14 +157,12 @@ namespace android { using namespace mbgl::style::conversion; Filter filter = layer.accept(GetFilterEvaluator()); - - jni::Object converted; - if (filter.is()) { - ExpressionFilter filterExpression = filter.get(); - mbgl::Value expressionValue = filterExpression.expression.get()->serialize(); - converted = gson::JsonElement::New(env, expressionValue); + if (filter.expression) { + mbgl::Value expressionValue = (*filter.expression)->serialize(); + return gson::JsonElement::New(env, expressionValue); + } else { + return jni::Object(); } - return converted; } struct SetSourceLayerEvaluator { diff --git a/platform/darwin/src/MGLCircleStyleLayer.mm b/platform/darwin/src/MGLCircleStyleLayer.mm index b85b3d9d4f..b03ab8a7a6 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.mm +++ b/platform/darwin/src/MGLCircleStyleLayer.mm @@ -75,7 +75,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm index ea29dff62d..4f3bfee18e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.mm +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.mm @@ -65,7 +65,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLFillStyleLayer.mm b/platform/darwin/src/MGLFillStyleLayer.mm index 5be0decd4a..12e3643ce6 100644 --- a/platform/darwin/src/MGLFillStyleLayer.mm +++ b/platform/darwin/src/MGLFillStyleLayer.mm @@ -65,7 +65,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.mm b/platform/darwin/src/MGLHeatmapStyleLayer.mm index b4bf4c9566..925b3ac750 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.mm +++ b/platform/darwin/src/MGLHeatmapStyleLayer.mm @@ -56,7 +56,7 @@ { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLLineStyleLayer.mm b/platform/darwin/src/MGLLineStyleLayer.mm index b359c424a2..84412073cd 100644 --- a/platform/darwin/src/MGLLineStyleLayer.mm +++ b/platform/darwin/src/MGLLineStyleLayer.mm @@ -77,7 +77,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index ead246fb03..e7589c1f62 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -103,7 +103,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 4236e04238..7ec7816c3b 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -142,7 +142,7 @@ namespace mbgl { { MGLAssertStyleLayerIsValid(); - self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::NullFilter()); + self.rawLayer->setFilter(predicate ? predicate.mgl_filter : mbgl::style::Filter()); } - (NSPredicate *)predicate diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm index 07154cb246..4b9a4177cb 100644 --- a/platform/darwin/src/NSPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm @@ -5,205 +5,6 @@ #include -class FilterEvaluator { -public: - - NSArray *getPredicates(std::vector filters) { - NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()]; - for (auto filter : filters) { - [predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())]; - } - return predicates; - } - - template - NSExpression *getValues(std::vector values) { - NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()]; - for (auto value : values) { - id constantValue = MBGLType::visit(value, ValueEvaluator()); - [array addObject:[NSExpression expressionForConstantValue:constantValue]]; - } - return [NSExpression expressionForAggregate:array]; - } - - NSString *getFeatureTypeString(mbgl::FeatureType type) { - switch (type) { - case mbgl::FeatureType::Point: - return @"Point"; - - case mbgl::FeatureType::LineString: - return @"LineString"; - - case mbgl::FeatureType::Polygon: - return @"Polygon"; - - default: - NSCAssert(NO, @"Unrecognized feature type %hhu", type); - return nil; - } - } - - NSExpression *getFeatureTypeStrings(std::vector values) { - NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()]; - for (auto value : values) { - id typeString = getFeatureTypeString(value); - [array addObject:[NSExpression expressionForConstantValue:typeString]]; - } - return [NSExpression expressionForAggregate:array]; - } - - NSPredicate *operator()(mbgl::style::NullFilter filter) { - return nil; - } - - NSPredicate *operator()(mbgl::style::EqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::NotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::GreaterThanFilter filter) { - return [NSPredicate predicateWithFormat:@"%K > %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::GreaterThanEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K >= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::LessThanFilter filter) { - return [NSPredicate predicateWithFormat:@"%K < %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::LessThanEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K <= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::InFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @(filter.key.c_str()), getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::NotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @(filter.key.c_str()), getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::TypeEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @"$type", getFeatureTypeString(filter.value)]; - } - - NSPredicate *operator()(mbgl::style::TypeNotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @"$type", getFeatureTypeString(filter.value)]; - } - - NSPredicate *operator()(mbgl::style::TypeInFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", getFeatureTypeStrings(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::TypeNotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$type", getFeatureTypeStrings(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::IdentifierEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::IdentifierNotEqualsFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())]; - } - - NSPredicate *operator()(mbgl::style::IdentifierInFilter filter) { - return [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::IdentifierNotInFilter filter) { - return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$id", getValues(filter.values)]; - } - - NSPredicate *operator()(mbgl::style::AnyFilter filter) { - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count) { - return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; - } - return [NSPredicate predicateWithValue:NO]; - } - - NSPredicate *operator()(mbgl::style::AllFilter filter) { - // Convert [all, [>=, key, lower], [<=, key, upper]] to key BETWEEN {lower, upper} - if (filter.filters.size() == 2) { - auto leftFilter = filter.filters[0]; - auto rightFilter = filter.filters[1]; - - std::string lowerKey; - std::string upperKey; - mbgl::Value lowerBound; - mbgl::Value upperBound; - if (leftFilter.is()) { - lowerKey = leftFilter.get().key; - lowerBound = leftFilter.get().value; - } else if (rightFilter.is()) { - lowerKey = rightFilter.get().key; - lowerBound = rightFilter.get().value; - } - - if (leftFilter.is()) { - upperKey = leftFilter.get().key; - upperBound = leftFilter.get().value; - } else if (rightFilter.is()) { - upperKey = rightFilter.get().key; - upperBound = rightFilter.get().value; - } - - if (!lowerBound.is() && !upperBound.is() - && lowerKey == upperKey) { - return [NSPredicate predicateWithFormat:@"%K BETWEEN {%@, %@}", - @(lowerKey.c_str()), - mbgl::Value::visit(lowerBound, ValueEvaluator()), - mbgl::Value::visit(upperBound, ValueEvaluator())]; - } - } - - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count) { - return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; - } - return [NSPredicate predicateWithValue:YES]; - } - - NSPredicate *operator()(mbgl::style::NoneFilter filter) { - NSArray *subpredicates = getPredicates(filter.filters); - if (subpredicates.count > 1) { - NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; - return [NSCompoundPredicate notPredicateWithSubpredicate:predicate]; - } else if (subpredicates.count) { - return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; - } else { - return [NSPredicate predicateWithValue:YES]; - } - } - - NSPredicate *operator()(mbgl::style::HasFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != nil", @(filter.key.c_str())]; - } - - NSPredicate *operator()(mbgl::style::NotHasFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == nil", @(filter.key.c_str())]; - } - - NSPredicate *operator()(mbgl::style::HasIdentifierFilter filter) { - return [NSPredicate predicateWithFormat:@"%K != nil", @"$id"]; - } - - NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) { - return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"]; - } - - NSPredicate *operator()(mbgl::style::ExpressionFilter filter) { - id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression); - return [NSPredicate predicateWithMGLJSONObject:jsonObject]; - } -}; - @implementation NSPredicate (MGLPrivateAdditions) - (mbgl::style::Filter)mgl_filter @@ -224,8 +25,12 @@ public: + (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter { - FilterEvaluator evaluator; - return mbgl::style::Filter::visit(filter, evaluator); + if (filter.expression) { + id jsonObject = MGLJSONObjectFromMBGLExpression(**filter.expression); + return [NSPredicate predicateWithMGLJSONObject:jsonObject]; + } else { + return nil; + } } @end diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index b725c63140..4e7b3e7e4b 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -4,226 +4,12 @@ #import "NSPredicate+MGLPrivateAdditions.h" #import "MGLValueEvaluator.h" -namespace mbgl { - namespace style { - bool operator!=(const Filter &a, const Filter &b) { - return !(a == b); - } - } -} - -#define MGLAssertEqualFilters(actual, expected, ...) \ - XCTAssertTrue(actual.is<__typeof__(expected)>()); \ - if (actual.is<__typeof__(expected)>()) { \ - XCTAssertEqual(actual.get<__typeof__(expected)>(), expected, __VA_ARGS__); \ - } - @interface MGLPredicateTests : XCTestCase @end @implementation MGLPredicateTests -- (void)testPredication { - XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]); - - { - mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Point }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::LineString }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'LineString'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Polygon }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Polygon'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::IdentifierEqualsFilter filter = { .value = UINT64_C(67086180) }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::NotHasIdentifierFilter filter; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown }; - XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException); - } - - { - mbgl::style::NotHasFilter filter = { .key = "a" }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]); - } - - { - mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]); - } - - { - mbgl::style::TypeNotEqualsFilter filter = { .value = mbgl::FeatureType::Point }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::IdentifierNotEqualsFilter filter = { .value = UINT64_C(67086180) }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::HasIdentifierFilter filter; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::HasFilter filter = { .key = "a" }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]); - } - - { - mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]); - } - - { - mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]); - } - - { - mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]); - } - - { - mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") }, - mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]); - } - - { - mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat); - } - - { - mbgl::style::TypeInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat); - } - - { - mbgl::style::IdentifierInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat); - } - - { - mbgl::style::TypeNotInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {'LineString', 'Polygon'}", @"$type"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat); - } - - { - mbgl::style::IdentifierNotInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } }; - NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"]; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected); - } - - { - mbgl::style::AllFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]); - } - - { - mbgl::style::AllFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]); - } - - { - mbgl::style::AnyFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]); - } - - { - mbgl::style::AnyFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]); - } - - { - mbgl::style::NoneFilter filter; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]); - } - - { - mbgl::style::NoneFilter filter = { - .filters = { - mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") }, - mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") }, - }, - }; - XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"]); - } -} - - (void)testUnsupportedFilterPredicates { - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException); - XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException); XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException); diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 3988419265..362269b8e4 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -619,6 +621,9 @@ void GLFWView::onDidFinishLoadingStyle() { } void GLFWView::toggle3DExtrusions(bool visible) { + using namespace mbgl::style; + using namespace mbgl::style::expression; + show3DExtrusions = visible; // Satellite-only style does not contain building extrusions data. @@ -634,7 +639,9 @@ void GLFWView::toggle3DExtrusions(bool visible) { auto extrusionLayer = std::make_unique("3d-buildings", "composite"); extrusionLayer->setSourceLayer("building"); extrusionLayer->setMinZoom(15.0f); - extrusionLayer->setFilter(mbgl::style::EqualsFilter { "extrude", { std::string("true") } }); + + ParsingContext parsingContext; + extrusionLayer->setFilter(Filter(createCompoundExpression("filter-==", createLiteral("extrude"), createLiteral("true"), parsingContext))); auto colorFn = mbgl::style::SourceFunction { "height", mbgl::style::ExponentialStops { diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index c67786274a..57719de038 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 82a9255824..cd9bcbd585 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mbgl/style/conversion/filter.cpp b/src/mbgl/style/conversion/filter.cpp index 3c941945fd..386d85e921 100644 --- a/src/mbgl/style/conversion/filter.cpp +++ b/src/mbgl/style/conversion/filter.cpp @@ -1,17 +1,39 @@ #include +#include #include #include #include -#include +#include +#include namespace mbgl { namespace style { namespace conversion { -using GeometryValue = mapbox::geometry::value; +using namespace mbgl::style::expression; + +static bool isExpression(const Convertible& filter); +std::unique_ptr convertLegacyFilter(const Convertible& values, Error& error); + +optional Converter::operator()(const Convertible& value, Error& error) const { + if (isExpression(value)) { + ParsingContext parsingContext(type::Boolean); + ParseResult parseResult = parsingContext.parseExpression(value); + if (!parseResult) { + error = { parsingContext.getCombinedErrors() }; + return {}; + } else { + return { Filter(std::move(parseResult)) }; + } + } else { + std::unique_ptr expression = convertLegacyFilter(value, error); + if (!expression) return {}; + return Filter(optional>(std::move(expression))); + } +} // This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js -static bool isExpressionFilter(const Convertible& filter) { +bool isExpression(const Convertible& filter) { if (!isArray(filter) || arrayLength(filter) == 0) { return false; } @@ -20,7 +42,7 @@ static bool isExpressionFilter(const Convertible& filter) { if (!op) { return false; - + } else if (*op == "has") { if (arrayLength(filter) < 2) return false; optional operand = toString(arrayMember(filter, 1)); @@ -35,7 +57,7 @@ static bool isExpressionFilter(const Convertible& filter) { } else if (*op == "any" || *op == "all") { for (std::size_t i = 1; i < arrayLength(filter); i++) { Convertible f = arrayMember(filter, i); - if (!isExpressionFilter(f) && !toBool(f)) { + if (!isExpression(f) && !toBool(f)) { return false; } } @@ -46,257 +68,136 @@ static bool isExpressionFilter(const Convertible& filter) { } } -static optional normalizeValue(const optional& value, Error& error) { - if (!value) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return *value; - } -} +std::unique_ptr createExpression(std::string op, std::vector> args, Error& error) { + if (op == "any") { + return std::make_unique(std::move(args)); + + } else if (op == "all") { + return std::make_unique(std::move(args)); -static optional toFeatureType(const Convertible& value, Error& error) { - optional type = toString(value); - if (!type) { - error = { "value for $type filter must be a string" }; - return {}; - } else if (*type == "Point") { - return FeatureType::Point; - } else if (*type == "LineString") { - return FeatureType::LineString; - } else if (*type == "Polygon") { - return FeatureType::Polygon; } else { - error = { "value for $type filter must be Point, LineString, or Polygon" }; - return {}; + ParsingContext parsingContext(type::Boolean); + ParseResult parseResult = createCompoundExpression(op, std::move(args), parsingContext); + if (!parseResult) { + error = { parsingContext.getCombinedErrors() }; + return {}; + } else { + return std::move(*parseResult); + } } } -static optional toFeatureIdentifier(const Convertible& value, Error& error) { - optional identifier = toValue(value); - if (!identifier) { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - } else { - return (*identifier).match( - [] (uint64_t t) -> optional { return { t }; }, - [] ( int64_t t) -> optional { return { t }; }, - [] ( double t) -> optional { return { t }; }, - [] (const std::string& t) -> optional { return { t }; }, - [&] (const auto&) -> optional { - error = { "filter expression value must be a boolean, number, or string" }; - return {}; - }); - } +std::unique_ptr createExpression(std::string op, std::unique_ptr expression, Error& error) { + std::vector> args; + args.push_back(std::move(expression)); + return createExpression(op, std::move(args), error); } -template -optional convertUnaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must have 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$id") { - return { IdentifierFilterType {} }; +std::unique_ptr convertLiteral(const Convertible& convertible, Error& error) { + ParsingContext parsingContext; + ParseResult parseResult = Literal::parse(convertible, parsingContext); + if (parseResult) { + return std::move(*parseResult); } else { - return { FilterType { *key } }; + error = { parsingContext.getCombinedErrors() }; + return {}; } } -template -optional convertEqualityFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; +std::vector> convertLiteralArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLiteral(arrayMember(input, i), error)); } + return output; +} - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; +std::unique_ptr convertLegacyComparisonFilter(const Convertible& values, Error& error, optional opOverride = {}) { + optional op = opOverride ? opOverride : toString(arrayMember(values, 0)); + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; - } - - if (*key == "$type") { - optional filterValue = toFeatureType(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { TypeFilterType { *filterValue } }; - - } else if (*key == "$id") { - optional filterValue = toFeatureIdentifier(arrayMember(value, 2), error); - if (!filterValue) { - return {}; - } - - return { IdentifierFilterType { *filterValue } }; - + } else if (*property == "$type") { + return createExpression("filter-type-" + *op, convertLiteralArray(values, error, 2), error); + } else if (*property == "$id") { + return createExpression("filter-id-" + *op, convertLiteralArray(values, error, 2), error); } else { - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { - return {}; - } - - return { FilterType { *key, *filterValue } }; + return createExpression("filter-" + *op, convertLiteralArray(values, error, 1), error); } } - -template -optional convertBinaryFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 3) { - error = { "filter expression must have 3 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); - if (!filterValue) { + +std::unique_ptr convertLegacyHasFilter(const Convertible& values, Error& error) { + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; return {}; + } else if (*property == "$type") { + return std::make_unique(true); + } else if (*property == "$id") { + return createExpression("filter-has-id", std::vector>(), error); + } else { + return createExpression("filter-has", std::make_unique(*property), error); } - - return { FilterType { *key, *filterValue } }; } -template -optional convertSetFilter(const Convertible& value, Error& error) { - if (arrayLength(value) < 2) { - error = { "filter expression must at least 2 elements" }; - return {}; - } - - optional key = toString(arrayMember(value, 1)); - if (!key) { - error = { "filter expression key must be a string" }; - return {}; - } - - if (*key == "$type") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureType(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { TypeFilterType { std::move(values) } }; - - } else if (*key == "$id") { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = toFeatureIdentifier(arrayMember(value, i), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { IdentifierFilterType { std::move(values) } }; - +std::unique_ptr convertLegacyInFilter(const Convertible& values, Error& error) { + optional property = toString(arrayMember(values, 1)); + + if (!property) { + error = { "filter property must be a string" }; + return {}; + } else if (arrayLength(values) == 0) { + return std::make_unique(false); + } else if (*property == "$type") { + return createExpression("filter-type-in", convertLiteralArray(values, error, 2), error); + } else if (*property == "$id") { + return createExpression("filter-id-in", convertLiteralArray(values, error, 2), error); } else { - std::vector values; - for (std::size_t i = 2; i < arrayLength(value); ++i) { - optional filterValue = normalizeValue(toValue(arrayMember(value, i)), error); - if (!filterValue) { - return {}; - } - values.push_back(*filterValue); - } - - return { FilterType { *key, std::move(values) } }; + return createExpression("filter-in", convertLiteralArray(values, error, 1), error); } } -template -optional convertCompoundFilter(const Convertible& value, Error& error) { - std::vector filters; - for (std::size_t i = 1; i < arrayLength(value); ++i) { - optional element = convert(arrayMember(value, i), error); - if (!element) { - return {}; - } - filters.push_back(*element); +std::vector> convertLegacyFilterArray(const Convertible &input, Error& error, std::size_t startIndex = 0) { + std::vector> output; + for (std::size_t i = startIndex; i < arrayLength(input); i++) { + output.push_back(convertLegacyFilter(arrayMember(input, i), error)); } - - return { FilterType { std::move(filters) } }; + return output; } -optional convertExpressionFilter(const Convertible& value, Error& error) { - expression::ParsingContext ctx(expression::type::Boolean); - expression::ParseResult expression = ctx.parseExpression(value); - if (!expression) { - error = { ctx.getCombinedErrors() }; - return {}; - } - - return { ExpressionFilter { std::move(*expression) } }; -} - -optional Converter::operator()(const Convertible& value, Error& error) const { - if (isExpressionFilter(value)) { - return convertExpressionFilter(value, error); +std::unique_ptr convertLegacyFilter(const Convertible& values, Error& error) { + if (isUndefined(values)) { + return std::make_unique(true); } - if (!isArray(value)) { - error = { "filter expression must be an array" }; - return {}; - } - - if (arrayLength(value) < 1) { - error = { "filter expression must have at least 1 element" }; - return {}; - } + optional op = toString(arrayMember(values, 0)); - optional op = toString(arrayMember(value, 0)); if (!op) { error = { "filter operator must be a string" }; return {}; + } else if (arrayLength(values) <= 1) { + return std::make_unique(*op != "any"); + } else { + return ( + *op == "==" || + *op == "<" || + *op == ">" || + *op == "<=" || + *op == ">=" ? convertLegacyComparisonFilter(values, error) : + *op == "!=" ? createExpression("!", convertLegacyComparisonFilter(values, error, {"=="}), error) : + *op == "any" ? createExpression("any", convertLegacyFilterArray(values, error, 1), error) : + *op == "all" ? createExpression("all", convertLegacyFilterArray(values, error, 1), error) : + *op == "none" ? createExpression("!", createExpression("any", convertLegacyFilterArray(values, error, 1), error), error) : + *op == "in" ? convertLegacyInFilter(values, error) : + *op == "!in" ? createExpression("!", convertLegacyInFilter(values, error), error) : + *op == "has" ? convertLegacyHasFilter(values, error) : + *op == "!has" ? createExpression("!", convertLegacyHasFilter(values, error), error) : + std::make_unique(true) + ); } - - if (*op == "==") { - return convertEqualityFilter(value, error); - } else if (*op == "!=") { - return convertEqualityFilter(value, error); - } else if (*op == ">") { - return convertBinaryFilter(value, error); - } else if (*op == ">=") { - return convertBinaryFilter(value, error); - } else if (*op == "<") { - return convertBinaryFilter(value, error); - } else if (*op == "<=") { - return convertBinaryFilter(value, error); - } else if (*op == "in") { - return convertSetFilter(value, error); - } else if (*op == "!in") { - return convertSetFilter(value, error); - } else if (*op == "all") { - return convertCompoundFilter(value, error); - } else if (*op == "any") { - return convertCompoundFilter(value, error); - } else if (*op == "none") { - return convertCompoundFilter(value, error); - } else if (*op == "has") { - return convertUnaryFilter(value, error); - } else if (*op == "!has") { - return convertUnaryFilter(value, error); - } - - error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" }; - return {}; } } // namespace conversion diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp index 7b7727d7c4..74171763a0 100644 --- a/src/mbgl/style/conversion/stringify.hpp +++ b/src/mbgl/style/conversion/stringify.hpp @@ -126,162 +126,9 @@ void stringify(Writer& writer, const FeatureIdentifier& id) { } template -class StringifyFilter { -public: - Writer& writer; - - void operator()(const NullFilter&) { - writer.Null(); - } - - void operator()(const EqualsFilter& f) { - stringifyBinaryFilter(f, "=="); - } - - void operator()(const NotEqualsFilter& f) { - stringifyBinaryFilter(f, "!="); - } - - void operator()(const LessThanFilter& f) { - stringifyBinaryFilter(f, "<"); - } - - void operator()(const LessThanEqualsFilter& f) { - stringifyBinaryFilter(f, "<="); - } - - void operator()(const GreaterThanFilter& f) { - stringifyBinaryFilter(f, ">"); - } - - void operator()(const GreaterThanEqualsFilter& f) { - stringifyBinaryFilter(f, ">="); - } - - void operator()(const InFilter& f) { - stringifySetFilter(f, "in"); - } - - void operator()(const NotInFilter& f) { - stringifySetFilter(f, "!in"); - } - - void operator()(const AllFilter& f) { - stringifyCompoundFilter(f, "all"); - } - - void operator()(const AnyFilter& f) { - stringifyCompoundFilter(f, "any"); - } - - void operator()(const NoneFilter& f) { - stringifyCompoundFilter(f, "none"); - } - - void operator()(const HasFilter& f) { - stringifyUnaryFilter("has", f.key); - } - - void operator()(const NotHasFilter& f) { - stringifyUnaryFilter("!has", f.key); - } - - void operator()(const TypeEqualsFilter& f) { - stringifyBinaryFilter(f, "==", "$type"); - } - - void operator()(const TypeNotEqualsFilter& f) { - stringifyBinaryFilter(f, "!=", "$type"); - } - - void operator()(const TypeInFilter& f) { - stringifySetFilter(f, "in", "$type"); - } - - void operator()(const TypeNotInFilter& f) { - stringifySetFilter(f, "!in", "$type"); - } - - void operator()(const IdentifierEqualsFilter& f) { - stringifyBinaryFilter(f, "==", "$id"); - } - - void operator()(const IdentifierNotEqualsFilter& f) { - stringifyBinaryFilter(f, "!=", "$id"); - } - - void operator()(const IdentifierInFilter& f) { - stringifySetFilter(f, "in", "$id"); - } - - void operator()(const IdentifierNotInFilter& f) { - stringifySetFilter(f, "!in", "$id"); - } - - void operator()(const HasIdentifierFilter&) { - stringifyUnaryFilter("has", "$id"); - } - - void operator()(const NotHasIdentifierFilter&) { - stringifyUnaryFilter("!has", "$id"); - } - - void operator()(const ExpressionFilter& filter) { - stringify(writer, filter.expression->serialize()); - } - -private: - template - void stringifyBinaryFilter(const F& f, const char * op) { - stringifyBinaryFilter(f, op, f.key); - } - - template - void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - stringify(writer, f.value); - writer.EndArray(); - } - - template - void stringifySetFilter(const F& f, const char * op) { - stringifySetFilter(f, op, f.key); - } - - template - void stringifySetFilter(const F& f, const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - for (const auto& value : f.values) { - stringify(writer, value); - } - writer.EndArray(); - } - - template - void stringifyCompoundFilter(const F& f, const char * op) { - writer.StartArray(); - writer.String(op); - for (const auto& filter : f.filters) { - Filter::visit(filter, *this); - } - writer.EndArray(); - } - - void stringifyUnaryFilter(const char * op, const std::string& key) { - writer.StartArray(); - writer.String(op); - writer.String(key); - writer.EndArray(); - } -}; - -template -void stringify(Writer& writer, const Filter& f) { - Filter::visit(f, StringifyFilter { writer }); +void stringify(Writer& writer, const Filter& filter) { + if (!filter.expression) writer.Null(); + else stringify(writer, (*filter.expression)->serialize()); } template diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index c36ffa33e3..3bd8a836df 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -18,7 +18,7 @@ namespace detail { The Signature structs are wrappers around an "evaluate()" function whose purpose is to extract the necessary Type data from the evaluate function's type. There are three key (partial) specializations: - + Signature: Wraps a simple evaluate function (const T0&, const T1&, ...) -> Result @@ -29,9 +29,9 @@ namespace detail { Signature: Wraps an evaluate function that needs to access the expression evaluation parameters in addition to its subexpressions, i.e., - (const EvaluationParams& const T0&, const T1&, ...) -> Result. Needed + (const EvaluationParams&, const T0&, const T1&, ...) -> Result. Needed for expressions like ["zoom"], ["get", key], etc. - + In each of the above evaluate signatures, T0, T1, etc. are the types of the successfully evaluated subexpressions. */ @@ -42,7 +42,7 @@ struct Signature; template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - + Signature(R (*evaluate_)(Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -54,7 +54,7 @@ struct Signature : SignatureBase { EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); @@ -79,7 +79,7 @@ private: template struct Signature&)> : SignatureBase { using Args = std::vector>; - + Signature(R (*evaluate_)(const Varargs&), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -88,11 +88,11 @@ struct Signature&)> : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr makeExpression(std::vector> args) const override { return std::make_unique>(name, *this, std::move(args)); }; - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { Varargs evaluated; evaluated.reserve(args.size()); @@ -114,7 +114,7 @@ struct Signature&)> : SignatureBase { template struct Signature : SignatureBase { using Args = std::array, sizeof...(Params)>; - + Signature(R (*evaluate_)(const EvaluationContext&, Params...), std::string name_) : SignatureBase( valueTypeToExpressionType>(), @@ -123,17 +123,17 @@ struct Signature : SignatureBase { ), evaluate(evaluate_) {} - + std::unique_ptr makeExpression(std::vector> args) const override { typename Signature::Args argsArray; std::copy_n(std::make_move_iterator(args.begin()), sizeof...(Params), argsArray.begin()); return std::make_unique>(name, *this, std::move(argsArray)); } - + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { return applyImpl(evaluationParameters, args, std::index_sequence_for{}); } - + private: template EvaluationResult applyImpl(const EvaluationContext& evaluationParameters, const Args& args, std::index_sequence) const { @@ -149,6 +149,41 @@ private: R (*evaluate)(const EvaluationContext&, Params...); }; + +// Evaluate function needing EvaluationContext and Varargs +// (const EvaluationContext&, const Varargs&) -> Result +template +struct Signature&)> : SignatureBase { + using Args = std::vector>; + + Signature(R (*evaluate_)(const EvaluationContext&, const Varargs&), std::string name_) : + SignatureBase( + valueTypeToExpressionType>(), + VarargsType { valueTypeToExpressionType() }, + std::move(name_) + ), + evaluate(evaluate_) + {} + + std::unique_ptr makeExpression(std::vector> args) const override { + return std::make_unique>(name, *this, std::move(args)); + }; + + EvaluationResult apply(const EvaluationContext& evaluationParameters, const Args& args) const { + Varargs evaluated; + evaluated.reserve(args.size()); + for (const auto& arg : args) { + const EvaluationResult evaluatedArg = arg->evaluate(evaluationParameters); + if(!evaluatedArg) return evaluatedArg.error(); + evaluated.push_back(*fromExpressionValue>(*evaluatedArg)); + } + const R value = evaluate(evaluationParameters, evaluated); + if (!value) return value.error(); + return *value; + } + + R (*evaluate)(const EvaluationContext&, const Varargs&); +}; // Machinery to pull out function types from class methods, lambdas, etc. template @@ -180,18 +215,92 @@ static std::unique_ptr makeSignature(Fn evaluateFunction, return std::make_unique>(evaluateFunction, std::move(name)); } +Value featureIdAsExpressionValue(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return Null; + return id->match([](const auto& idid) { + return toExpressionValue(mbgl::Value(idid)); + }); +}; + +optional featurePropertyAsExpressionValue(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + return property ? toExpressionValue(*property) : optional(); +}; + +optional featureTypeAsString(FeatureType type) { + switch(type) { + case FeatureType::Point: + return optional("Point"); + case FeatureType::LineString: + return optional("LineString"); + case FeatureType::Polygon: + return optional("Polygon"); + case FeatureType::Unknown: + return optional("Unknown"); + default: + return {}; + } +}; + +optional featurePropertyAsDouble(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + if (!property) return {}; + return property->match( + [](double value) { return value; }, + [](uint64_t value) { return optional(static_cast(value)); }, + [](int64_t value) { return optional(static_cast(value)); }, + [](auto) { return optional(); } + ); +}; + +optional featurePropertyAsString(EvaluationContext params, const std::string& key) { + assert(params.feature); + auto property = params.feature->getValue(key); + if (!property) return {}; + return property->match( + [](std::string value) { return value; }, + [](auto) { return optional(); } + ); +}; + +optional featureIdAsDouble(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional(); + return id->match( + [](double value) { return value; }, + [](uint64_t value) { return optional(static_cast(value)); }, + [](int64_t value) { return optional(static_cast(value)); }, + [](auto) { return optional(); } + ); +}; + +optional featureIdAsString(EvaluationContext params) { + assert(params.feature); + auto id = params.feature->getID(); + if (!id) return optional(); + return id->match( + [](std::string value) { return value; }, + [](auto) { return optional(); } + ); +}; + std::unordered_map initializeDefinitions() { std::unordered_map definitions; auto define = [&](std::string name, auto fn) { definitions[name].push_back(makeSignature(fn, name)); }; - + define("e", []() -> Result { return 2.718281828459045; }); define("pi", []() -> Result { return 3.141592653589793; }); define("ln2", []() -> Result { return 0.6931471805599453; }); define("typeof", [](const Value& v) -> Result { return toString(typeOf(v)); }); - + define("to-string", [](const Value& value) -> Result { return value.match( [](const Color& c) -> Result { return c.stringify(); }, // avoid quoting @@ -199,10 +308,10 @@ std::unordered_map initiali [](const auto& v) -> Result { return stringify(v); } ); }); - + define("to-boolean", [](const Value& v) -> Result { return v.match( - [&] (double f) { return (bool)f; }, + [&] (double f) { return static_cast(f); }, [&] (const std::string& s) { return s.length() > 0; }, [&] (bool b) { return b; }, [&] (const NullValue&) { return false; }, @@ -212,10 +321,10 @@ std::unordered_map initiali define("to-rgba", [](const Color& color) -> Result> { return color.toArray(); }); - + define("rgba", rgba); define("rgb", [](double r, double g, double b) { return rgba(r, g, b, 1.0f); }); - + define("zoom", [](const EvaluationContext& params) -> Result { if (!params.zoom) { return EvaluationError { @@ -224,7 +333,7 @@ std::unordered_map initiali } return *(params.zoom); }); - + define("heatmap-density", [](const EvaluationContext& params) -> Result { if (!params.heatmapDensity) { return EvaluationError { @@ -240,7 +349,7 @@ std::unordered_map initiali "Feature data is unavailable in the current evaluation context." }; } - + return params.feature->getValue(key) ? true : false; }); define("has", [](const std::string& key, const std::unordered_map& object) -> Result { @@ -266,7 +375,7 @@ std::unordered_map initiali } return object.at(key); }); - + define("properties", [](const EvaluationContext& params) -> Result> { if (!params.feature) { return EvaluationError { @@ -280,14 +389,14 @@ std::unordered_map initiali } return result; }); - + define("geometry-type", [](const EvaluationContext& params) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } - + auto type = params.feature->getType(); if (type == FeatureType::Point) { return "Point"; @@ -299,14 +408,14 @@ std::unordered_map initiali return "Unknown"; } }); - + define("id", [](const EvaluationContext& params) -> Result { if (!params.feature) { return EvaluationError { "Feature data is unavailable in the current evaluation context." }; } - + auto id = params.feature->getID(); if (!id) { return Null; @@ -317,7 +426,7 @@ std::unordered_map initiali } ); }); - + define("+", [](const Varargs& args) -> Result { double sum = 0.0f; for (auto arg : args) { @@ -347,7 +456,7 @@ std::unordered_map initiali define("asin", [](double x) -> Result { return asin(x); }); define("acos", [](double x) -> Result { return acos(x); }); define("atan", [](double x) -> Result { return atan(x); }); - + define("min", [](const Varargs& args) -> Result { double result = std::numeric_limits::infinity(); for (double arg : args) { @@ -362,7 +471,7 @@ std::unordered_map initiali } return result; }); - + define("round", [](double x) -> Result { return std::round(x); }); define("floor", [](double x) -> Result { return std::floor(x); }); define("ceil", [](double x) -> Result { return std::ceil(x); }); @@ -376,9 +485,9 @@ std::unordered_map initiali define("<", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs < rhs; }); define("<=", [](double lhs, double rhs) -> Result { return lhs <= rhs; }); define("<=", [](const std::string& lhs, const std::string& rhs) -> Result { return lhs <= rhs; }); - + define("!", [](bool e) -> Result { return !e; }); - + define("upcase", [](const std::string& input) -> Result { return platform::uppercase(input); }); @@ -395,7 +504,130 @@ std::unordered_map initiali define("error", [](const std::string& input) -> Result { return EvaluationError { input }; }); + + // Legacy Filters + define("filter-==", [](const EvaluationContext& params, const std::string& key, const Value &lhs) -> Result { + const auto rhs = featurePropertyAsExpressionValue(params, key); + return rhs ? lhs == *rhs : false; + }); + + define("filter-id-==", [](const EvaluationContext& params, const Value &lhs) -> Result { + return lhs == featureIdAsExpressionValue(params); + }); + + define("filter-type-==", [](const EvaluationContext& params, const std::string &lhs) -> Result { + if (!params.feature) return false; + return featureTypeAsString(params.feature->getType()) == lhs; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-<", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs < lhs : false; + }); + + define("filter-id-<", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs < lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter->", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-id->", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs > lhs : false; + }); + + define("filter-<=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs <= lhs : false; + }); + define("filter-<=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter-id-<=", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs <= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, double lhs) -> Result { + auto rhs = featurePropertyAsDouble(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter->=", [](const EvaluationContext& params, const std::string& key, std::string lhs) -> Result { + auto rhs = featurePropertyAsString(params, key); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, double lhs) -> Result { + auto rhs = featureIdAsDouble(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-id->=", [](const EvaluationContext& params, std::string lhs) -> Result { + auto rhs = featureIdAsString(params); + return rhs ? rhs >= lhs : false; + }); + + define("filter-has", [](const EvaluationContext& params, const std::string& key) -> Result { + assert(params.feature); + return bool(params.feature->getValue(key)); + }); + + define("filter-has-id", [](const EvaluationContext& params) -> Result { + assert(params.feature); + return bool(params.feature->getID()); + }); + + define("filter-type-in", [](const EvaluationContext& params, const Varargs& types) -> Result { + assert(params.feature); + optional type = featureTypeAsString(params.feature->getType()); + return std::find(types.begin(), types.end(), type) != types.end(); + }); + + define("filter-id-in", [](const EvaluationContext& params, const Varargs& ids) -> Result { + auto id = featureIdAsExpressionValue(params); + return std::find(ids.begin(), ids.end(), id) != ids.end(); + }); + + define("filter-in", [](const EvaluationContext& params, const Varargs& varargs) -> Result { + if (varargs.size() < 2) return false; + assert(varargs[0].is()); + auto value = featurePropertyAsExpressionValue(params, varargs[0].get()); + return value ? std::find(varargs.begin() + 1, varargs.end(), *value) != varargs.end() : false; + }); + return definitions; } @@ -414,7 +646,7 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v return ParseResult(); } const CompoundExpressionRegistry::Definition& definition = it->second; - + auto length = arrayLength(value); // Check if we have a single signature with the correct number of @@ -440,14 +672,14 @@ ParseResult parseCompoundExpression(const std::string name, const Convertible& v args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { optional expected; - + if (singleMatchingSignature) { expected = definition[*singleMatchingSignature]->params.match( [](const VarargsType& varargs) { return varargs.type; }, [&](const std::vector& params_) { return params_[i - 1]; } ); } - + auto parsed = ctx.parse(arrayMember(value, i), i, expected); if (!parsed) { return parsed; @@ -474,7 +706,7 @@ ParseResult createCompoundExpression(const Definition& definition, for (const std::unique_ptr& signature : definition) { signatureContext.clearErrors(); - + if (signature->params.is>()) { const std::vector& params = signature->params.get>(); if (params.size() != args.size()) { @@ -502,12 +734,12 @@ ParseResult createCompoundExpression(const Definition& definition, } } } - + if (signatureContext.getErrors().size() == 0) { return ParseResult(signature->makeExpression(std::move(args))); } } - + if (definition.size() == 1) { ctx.appendErrors(std::move(signatureContext)); } else { @@ -540,10 +772,32 @@ ParseResult createCompoundExpression(const Definition& definition, } ctx.error("Expected arguments of type " + signatures + ", but found (" + actualTypes + ") instead."); } - + return ParseResult(); } +ParseResult createCompoundExpression(const std::string& name, ParsingContext& ctx) { + return createCompoundExpression(name, std::vector>(), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + ParsingContext& ctx) { + std::vector> args; + args.push_back(std::move(arg1)); + return createCompoundExpression(name, std::move(args), ctx); +} + +ParseResult createCompoundExpression(const std::string& name, + std::unique_ptr arg1, + std::unique_ptr arg2, + ParsingContext& ctx) { + std::vector> args; + args.push_back(std::move(arg1)); + args.push_back(std::move(arg2)); + return createCompoundExpression(name, std::move(args), ctx); +} + } // namespace expression } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/expression/literal.cpp b/src/mbgl/style/expression/literal.cpp index 8a63980dba..f68cfd5cf5 100644 --- a/src/mbgl/style/expression/literal.cpp +++ b/src/mbgl/style/expression/literal.cpp @@ -109,6 +109,14 @@ mbgl::Value Literal::serialize() const { return *fromExpressionValue(value); } } + +std::unique_ptr createLiteral(const char* value) { + return createLiteral(std::string(value)); +} + +std::unique_ptr createLiteral(Value value) { + return std::make_unique(value); +} } // namespace expression } // namespace style diff --git a/src/mbgl/style/filter.cpp b/src/mbgl/style/filter.cpp index 51aa6bcf82..2559eb4816 100644 --- a/src/mbgl/style/filter.cpp +++ b/src/mbgl/style/filter.cpp @@ -1,12 +1,20 @@ #include -#include #include namespace mbgl { namespace style { bool Filter::operator()(const expression::EvaluationContext &context) const { - return FilterBase::visit(*this, FilterEvaluator { context }); + + if (!this->expression) return true; + + const expression::EvaluationResult result = (*this->expression)->evaluate(context); + if (result) { + const optional typed = expression::fromExpressionValue(*result); + return typed ? *typed : false; + } else { + return true; + } } } // namespace style diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp deleted file mode 100644 index 72022172f4..0000000000 --- a/src/mbgl/style/filter_evaluator.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include -#include - -namespace mbgl { -namespace style { - -template -struct Comparator { - const Op& op; - - template - bool operator()(const T& lhs, const T& rhs) const { - return op(lhs, rhs); - } - - template - auto operator()(const T0& lhs, const T1& rhs) const - -> typename std::enable_if_t::value && !std::is_same::value && - std::is_arithmetic::value && !std::is_same::value, bool> { - return op(double(lhs), double(rhs)); - } - - template - auto operator()(const T0&, const T1&) const - -> typename std::enable_if_t::value || std::is_same::value || - !std::is_arithmetic::value || std::is_same::value, bool> { - return false; - } - - bool operator()(const NullValue&, - const NullValue&) const { - // Should be unreachable; null is not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const std::vector&, - const std::vector&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } - - bool operator()(const PropertyMap&, - const PropertyMap&) const { - // Should be unreachable; nested values are not currently allowed by the style specification. - assert(false); - return false; - } -}; - -template -bool compare(const Value& lhs, const Value& rhs, const Op& op) { - return Value::binary_visit(lhs, rhs, Comparator { op }); -} - -bool equal(const Value& lhs, const Value& rhs) { - return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; }); -} - -bool FilterEvaluator::operator()(const NullFilter&) const { - return true; -} - -bool FilterEvaluator::operator()(const EqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return !actual || !equal(*actual, filter.value); -} - -bool FilterEvaluator::operator()(const LessThanFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; }); -} - -bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; }); -} - -bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; }); -} - -bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; }); -} - -bool FilterEvaluator::operator()(const InFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - if (!actual) - return false; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const NotInFilter& filter) const { - optional actual = context.feature->getValue(filter.key); - if (!actual) - return true; - for (const auto& v: filter.values) { - if (equal(*actual, v)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const AnyFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const AllFilter& filter) const { - for (const auto& f: filter.filters) { - if (!Filter::visit(f, *this)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const NoneFilter& filter) const { - for (const auto& f: filter.filters) { - if (Filter::visit(f, *this)) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const HasFilter& filter) const { - return bool(context.feature->getValue(filter.key)); -} - -bool FilterEvaluator::operator()(const NotHasFilter& filter) const { - return !context.feature->getValue(filter.key); -} - -bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const { - return context.feature->getType() == filter.value; -} - -bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const { - return context.feature->getType() != filter.value; -} - -bool FilterEvaluator::operator()(const TypeInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getType() == v) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const { - return context.feature->getID() == filter.value; -} - -bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const { - return context.feature->getID() != filter.value; -} - -bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return true; - } - } - return false; -} - -bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const { - for (const auto& v: filter.values) { - if (context.feature->getID() == v) { - return false; - } - } - return true; -} - -bool FilterEvaluator::operator()(const HasIdentifierFilter&) const { - return bool(context.feature->getID()); -} - -bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const { - return !context.feature->getID(); -} - -bool FilterEvaluator::operator()(const ExpressionFilter& filter) const { - const expression::EvaluationResult result = filter.expression->evaluate(context); - if (result) { - const optional typed = expression::fromExpressionValue(*result); - return typed ? *typed : false; - } - return false; -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/tile/custom_geometry_tile.cpp b/src/mbgl/tile/custom_geometry_tile.cpp index a2fefcfa9f..272a1594d4 100644 --- a/src/mbgl/tile/custom_geometry_tile.cpp +++ b/src/mbgl/tile/custom_geometry_tile.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp index f211c03569..7a83da2267 100644 --- a/src/mbgl/tile/geojson_tile.cpp +++ b/src/mbgl/tile/geojson_tile.cpp @@ -2,7 +2,6 @@ #include #include #include -#include namespace mbgl { diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp index a99cb91d26..28f46f6f54 100644 --- a/src/mbgl/tile/geometry_tile.cpp +++ b/src/mbgl/tile/geometry_tile.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 1378ad5d3a..61bc0cb597 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/test/api/query.test.cpp b/test/api/query.test.cpp index c67ff9064c..ffab52692b 100644 --- a/test/api/query.test.cpp +++ b/test/api/query.test.cpp @@ -13,6 +13,7 @@ using namespace mbgl; using namespace mbgl::style; +using namespace mbgl::style::expression; namespace { @@ -67,18 +68,19 @@ TEST(Query, QueryRenderedFeaturesFilterLayer) { TEST(Query, QueryRenderedFeaturesFilter) { QueryTest test; + ParsingContext context; auto zz = test.map.pixelForLatLng({ 0, 0 }); - const EqualsFilter eqFilter = { "key1", std::string("value1") }; + const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context)); auto features1 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{}, { eqFilter }}); EXPECT_EQ(features1.size(), 1u); - const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") }; + const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context)); auto features2 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{{ "layer4" }}, { idNotEqFilter }}); EXPECT_EQ(features2.size(), 0u); - const GreaterThanFilter gtFilter = { "key2", 1.0 }; + const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context)); auto features3 = test.frontend.getRenderer()->queryRenderedFeatures(zz, {{ }, { gtFilter }}); EXPECT_EQ(features3.size(), 1u); } @@ -108,16 +110,17 @@ TEST(Query, QuerySourceFeaturesOptionValidation) { TEST(Query, QuerySourceFeaturesFilter) { QueryTest test; + ParsingContext context; - const EqualsFilter eqFilter = { "key1", std::string("value1") }; + const Filter eqFilter(createCompoundExpression("filter-==", createLiteral("key1"), createLiteral("value1"), context)); auto features1 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { eqFilter }}); EXPECT_EQ(features1.size(), 1u); - const IdentifierNotEqualsFilter idNotEqFilter = { std::string("feature1") }; + const Filter idNotEqFilter(createCompoundExpression("!", std::move(*createCompoundExpression("filter-id-==", createLiteral("feature1"), context)), context)); auto features2 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { idNotEqFilter }}); EXPECT_EQ(features2.size(), 0u); - const GreaterThanFilter gtFilter = { "key2", 1.0 }; + const Filter gtFilter(createCompoundExpression("filter->", createLiteral("key2"), createLiteral(1.0), context)); auto features3 = test.frontend.getRenderer()->querySourceFeatures("source4", {{}, { gtFilter }}); EXPECT_EQ(features3.size(), 1u); } diff --git a/test/renderer/group_by_layout.test.cpp b/test/renderer/group_by_layout.test.cpp index 958f1bdf24..6cf17e2279 100644 --- a/test/renderer/group_by_layout.test.cpp +++ b/test/renderer/group_by_layout.test.cpp @@ -5,9 +5,11 @@ #include #include #include +#include using namespace mbgl; using namespace mbgl::style; +using namespace mbgl::style::expression; static std::vector> toRenderLayers(const std::vector>& layers) { std::vector> result; @@ -39,7 +41,8 @@ TEST(GroupByLayout, UnrelatedFilter) { std::vector> layers; layers.push_back(std::make_unique("a", "source")); layers.push_back(std::make_unique("b", "source")); - layers[0]->as()->setFilter(EqualsFilter()); + ParsingContext context; + layers[0]->as()->setFilter(Filter(createCompoundExpression("filter-has-id", context))); auto result = groupByLayout(toRenderLayers(layers)); ASSERT_EQ(2u, result.size()); } diff --git a/test/style/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp index 136f276aaf..c3faf1f838 100644 --- a/test/style/conversion/stringify.test.cpp +++ b/test/style/conversion/stringify.test.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -75,8 +76,12 @@ TEST(Stringify, Value) { } TEST(Stringify, Filter) { - ASSERT_EQ(stringify(NullFilter()), "null"); - ASSERT_EQ(stringify(EqualsFilter { "a", 1.0 }), "[\"==\",\"a\",1.0]"); + using namespace mbgl::style::expression; + + ASSERT_EQ(stringify(Filter()), "null"); + + ParsingContext context; + ASSERT_EQ(stringify(Filter(createCompoundExpression("filter-==", createLiteral("a"), createLiteral(1.0), context))), "[\"filter-==\",\"a\",1.0]"); } TEST(Stringify, CameraFunction) { diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 49edcaef45..c59a73eab1 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -28,6 +27,24 @@ bool filter(const char * json, return (*filter)(context); } +void invalidFilter(const char * json) { + conversion::Error error; + optional filter = conversion::convertJSON(json, error); + EXPECT_FALSE(bool(filter)); + EXPECT_NE(error.message, ""); +} + +TEST(Filter, EqualsNull) { + auto f = R"(["==", "foo", null])"; + ASSERT_TRUE(filter(f, {{ "foo", mapbox::geometry::null_value }})); + + ASSERT_FALSE(filter(f, {{ "foo", int64_t(0) }})); + ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }})); + ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }})); + ASSERT_FALSE(filter(f, {{ "foo", true }})); + ASSERT_FALSE(filter(f, {{ "foo", false }})); + ASSERT_FALSE(filter(f, {{ }})); +} TEST(Filter, EqualsString) { auto f = R"(["==", "foo", "bar"])"; ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }})); @@ -53,6 +70,12 @@ TEST(Filter, EqualsType) { auto f = R"(["==", "$type", "LineString"])"; ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {})); + ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {})); + + invalidFilter("[\"==\", \"$type\"]"); + invalidFilter("[\"==\", \"$type\", null]"); + invalidFilter("[\"==\", \"$type\", \"foo\", 1]"); + invalidFilter("[\"==\", \"$type\", \"foo\", \"Point\"]"); } TEST(Filter, InType) { @@ -62,6 +85,14 @@ TEST(Filter, InType) { ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon)); } +TEST(Filter, InID) { + auto f = R"(["in", "$id", "123", "1234", 1234])"; + ASSERT_FALSE(filter(f)); + ASSERT_TRUE(filter(f, {{}}, { uint64_t(1234) })); + ASSERT_TRUE(filter(f, {{}}, { std::string("1234") })); + ASSERT_FALSE(filter(f, {{}}, { std::string("4321") })); +} + TEST(Filter, Any) { ASSERT_FALSE(filter("[\"any\"]")); ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -69,6 +100,13 @@ TEST(Filter, Any) { ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } +TEST(Filter, AnyExpression) { + ASSERT_FALSE(filter("[\"any\"]")); + ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_TRUE(filter("[\"any\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); +} + TEST(Filter, All) { ASSERT_TRUE(filter("[\"all\"]", {{}})); ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -76,6 +114,12 @@ TEST(Filter, All) { ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); } +TEST(Filter, AllExpression) { + ASSERT_TRUE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0]]", {{ std::string("foo"), int64_t(1) }})); + ASSERT_FALSE(filter("[\"all\", [\"==\", [\"get\", \"foo\"], 0], [\"==\", [\"get\", \"foo\"], 1]]", {{ std::string("foo"), int64_t(1) }})); +} + TEST(Filter, None) { ASSERT_TRUE(filter("[\"none\"]")); ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }})); @@ -88,6 +132,7 @@ TEST(Filter, Has) { ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }})); ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }})); ASSERT_FALSE(filter("[\"has\", \"foo\"]")); + ASSERT_FALSE(filter("[\"has\", \"$id\"]")); } TEST(Filter, NotHas) { @@ -101,7 +146,11 @@ TEST(Filter, ID) { FeatureIdentifier id1 { uint64_t{ 1234 } }; ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1)); ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1)); - + + FeatureIdentifier id2 { std::string{ "1" } }; + ASSERT_FALSE(filter("[\"<\", \"$id\", \"0\"]", {{}}, id2)); + ASSERT_TRUE(filter("[\"<\", \"$id\", \"1234\"]", {{}}, id2)); + ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }})); } @@ -115,6 +164,18 @@ TEST(Filter, PropertyExpression) { ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}})); } +TEST(Filter, LegacyProperty) { + ASSERT_TRUE(filter("[\"<=\", \"two\", 2]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", int64_t(2)}})); + + ASSERT_FALSE(filter("[\"<=\", \"two\", \"2\"]", {{"two", int64_t(2)}})); + ASSERT_FALSE(filter("[\"==\", \"bool\", false]", {{"two", true}})); + + ASSERT_TRUE(filter("[\"<=\", \"two\", \"2\"]", {{"two", std::string("2")}})); + ASSERT_FALSE(filter("[\"<\", \"two\", \"1\"]", {{"two", std::string("2")}})); + ASSERT_FALSE(filter("[\"==\", \"two\", 4]", {{"two", std::string("2")}})); +} + TEST(Filter, ZoomExpressionNested) { ASSERT_TRUE(filter(R"(["==", ["get", "two"], ["zoom"]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f)); ASSERT_FALSE(filter(R"(["==", ["get", "two"], ["+", ["zoom"], 1]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f)); diff --git a/test/style/style_layer.test.cpp b/test/style/style_layer.test.cpp index 77acca2868..624ed088c3 100644 --- a/test/style/style_layer.test.cpp +++ b/test/style/style_layer.test.cpp @@ -211,7 +211,7 @@ TEST(Layer, Observer) { EXPECT_EQ(layer.get(), &layer_); filterChanged = true; }; - layer->setFilter(NullFilter()); + layer->setFilter(Filter()); EXPECT_TRUE(filterChanged); // Notifies observer on visibility change. -- cgit v1.2.1 From b6f56db4fe0f3b40c13494e269855e57e4ed660c Mon Sep 17 00:00:00 2001 From: Tobrun Date: Mon, 7 May 2018 11:26:05 +0200 Subject: [android] - avoid rounding the pixelratio of the image addded through NativeMapView#addImage --- .../src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 0e77910c3d..305dd906dd 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -772,8 +772,9 @@ final class NativeMapView { return; } - // Determine pixel ratio - nativeAddImage(name, image, image.getDensity() / DisplayMetrics.DENSITY_DEFAULT); + // Determine pixel ratio, cast to float to avoid rounding, see mapbox-gl-native/issues/11809 + float pixelRatio = (float) image.getDensity() / DisplayMetrics.DENSITY_DEFAULT; + nativeAddImage(name, image, pixelRatio); } public void addImages(@NonNull HashMap bitmapHashMap) { -- cgit v1.2.1 From 36d30f13fcc6f4356e0d58318fc168c77d7dbecf Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Fri, 11 May 2018 16:00:45 -0400 Subject: [android] added more tests for LatLngBounds.union(), LatLngBounds.intersect() (#11777) fixed going over antimeridian for both --- .../mapbox/mapboxsdk/geometry/LatLngBounds.java | 226 ++++++++++++++++----- .../mapboxsdk/geometry/LatLngBoundsTest.java | 218 +++++++++++++++++++- 2 files changed, 391 insertions(+), 53 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 90cb56f605..c639e49013 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -294,6 +294,20 @@ public class LatLngBounds implements Parcelable { @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latSouth, double lonWest) { + checkParams(latNorth, lonEast, latSouth, lonWest); + + lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); + } + + private static void checkParams( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latNorth, + double lonEast, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latSouth, + double lonWest) { + if (Double.isNaN(latNorth) || Double.isNaN(latSouth)) { throw new IllegalArgumentException("latitude must not be NaN"); } @@ -314,11 +328,6 @@ public class LatLngBounds implements Parcelable { if (latNorth < latSouth) { throw new IllegalArgumentException("LatSouth cannot be less than latNorth"); } - - lonEast = LatLng.wrap(lonEast, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); - lonWest = LatLng.wrap(lonWest, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); - - return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); } private static double lat_(int z, int y) { @@ -395,7 +404,7 @@ public class LatLngBounds implements Parcelable { return (longitude <= eastLon) && (longitude >= westLon); } - return (longitude < eastLon) || (longitude > westLon); + return (longitude <= eastLon) || (longitude >= westLon); } /** @@ -426,36 +435,94 @@ public class LatLngBounds implements Parcelable { * @param bounds LatLngBounds to add * @return LatLngBounds */ - public LatLngBounds union(LatLngBounds bounds) { - return union(bounds.getLatNorth(), bounds.getLonEast(), bounds.getLatSouth(), bounds.getLonWest()); + public @NonNull LatLngBounds union(@NonNull LatLngBounds bounds) { + return unionNoParamCheck(bounds.getLatNorth(), bounds.getLonEast(), bounds.getLatSouth(), bounds.getLonWest()); } /** * Returns a new LatLngBounds that stretches to include another LatLngBounds, * given by corner points. + *

+ * This values of northLat and southLat should be in the range of [-90, 90], + * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, + * otherwise IllegalArgumentException will be thrown. + * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. + *

+ * This method doesn't recalculate most east or most west boundaries. + * Note that eastLon and westLon will be wrapped to be in the range of [-180, 180], + * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE} * - * @param latNorth Northern Latitude - * @param lonEast Eastern Longitude - * @param latSouth Southern Latitude - * @param lonWest Western Longitude - * @return BoundingBox - */ - public LatLngBounds union(final double latNorth, final double lonEast, final double latSouth, final double lonWest) { - double north = (this.latitudeNorth < latNorth) ? latNorth : this.latitudeNorth; - double south = (this.latitudeSouth > latSouth) ? latSouth : this.latitudeSouth; - - if (LatLngSpan.getLongitudeSpan(lonEast, this.longitudeWest) - < LatLngSpan.getLongitudeSpan(this.longitudeEast, lonWest)) { - return new LatLngBounds(north, - lonEast, - south, + * @param northLat Northern Latitude corner point + * @param eastLon Eastern Longitude corner point + * @param southLat Southern Latitude corner point + * @param westLon Western Longitude corner point + * @return LatLngBounds + */ + public @NonNull LatLngBounds union( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + checkParams(northLat, eastLon, southLat, westLon); + + return unionNoParamCheck(northLat, eastLon, southLat, westLon); + } + + private @NonNull LatLngBounds unionNoParamCheck( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + northLat = (this.latitudeNorth < northLat) ? northLat : this.latitudeNorth; + southLat = (this.latitudeSouth > southLat) ? southLat : this.latitudeSouth; + + eastLon = LatLng.wrap(eastLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + westLon = LatLng.wrap(westLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + // longitudes match + if (this.longitudeEast == eastLon && this.longitudeWest == westLon) { + return new LatLngBounds(northLat, eastLon, southLat, westLon); + } + + boolean eastInThis = containsLongitude(this.longitudeEast, this.longitudeWest, eastLon); + boolean westInThis = containsLongitude(this.longitudeEast, this.longitudeWest, westLon); + boolean thisEastInside = containsLongitude(eastLon, westLon, this.longitudeEast); + boolean thisWestInside = containsLongitude(eastLon, westLon, this.longitudeWest); + + // two intersections on each end - covers entire longitude + if (eastInThis && westInThis && thisEastInside && thisWestInside) { + return new LatLngBounds(northLat, GeometryConstants.MAX_LONGITUDE, southLat, GeometryConstants.MIN_LONGITUDE); + } + + if (eastInThis) { + if (westInThis) { + return new LatLngBounds(northLat, this.longitudeEast, southLat, this.longitudeWest); + } + return new LatLngBounds(northLat, this.longitudeEast, southLat, westLon); + } + + if (thisEastInside) { + if (thisWestInside) { + return new LatLngBounds(northLat, eastLon, southLat, westLon); + } + return new LatLngBounds(northLat, eastLon, southLat, this.longitudeWest); + } + + // bounds do not intersect, find where they will form shortest union + if (LatLngSpan.getLongitudeSpan(eastLon, this.longitudeWest) + < LatLngSpan.getLongitudeSpan(this.longitudeEast, westLon)) { + return new LatLngBounds(northLat, + eastLon, + southLat, this.longitudeWest); } - return new LatLngBounds(north, + return new LatLngBounds(northLat, this.longitudeEast, - south, - lonWest); + southLat, + westLon); } /** @@ -464,32 +531,89 @@ public class LatLngBounds implements Parcelable { * @param box LatLngBounds to intersect with * @return LatLngBounds */ - @Nullable - public LatLngBounds intersect(LatLngBounds box) { - double minLonWest = Math.max(getLonWest(), box.getLonWest()); - double maxLonEast = Math.min(getLonEast(), box.getLonEast()); - if (maxLonEast > minLonWest) { - double minLatSouth = Math.max(getLatSouth(), box.getLatSouth()); - double maxLatNorth = Math.min(getLatNorth(), box.getLatNorth()); - if (maxLatNorth > minLatSouth) { - return new LatLngBounds(maxLatNorth, maxLonEast, minLatSouth, minLonWest); - } - } - return null; + public @Nullable LatLngBounds intersect(@NonNull LatLngBounds box) { + return intersectNoParamCheck(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest()); } /** * Returns a new LatLngBounds that is the intersection of this with another LatLngBounds + *

+ * This values of northLat and southLat should be in the range of [-90, 90], + * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, + * otherwise IllegalArgumentException will be thrown. + * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. + *

+ * This method doesn't recalculate most east or most west boundaries. + * Note that eastLon and westLon will be wrapped to be in the range of [-180, 180], + * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE} * - * @param northLatitude Northern Longitude - * @param eastLongitude Eastern Latitude - * @param southLatitude Southern Longitude - * @param westLongitude Western Latitude + * @param northLat Northern Latitude corner point + * @param eastLon Eastern Longitude corner point + * @param southLat Southern Latitude corner point + * @param westLon Western Longitude corner point * @return LatLngBounds */ - public LatLngBounds intersect(double northLatitude, double eastLongitude, double southLatitude, - double westLongitude) { - return intersect(new LatLngBounds(northLatitude, eastLongitude, southLatitude, westLongitude)); + public @Nullable LatLngBounds intersect( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + checkParams(northLat, eastLon, southLat, westLon); + + return intersectNoParamCheck(northLat, eastLon, southLat, westLon); + } + + private @Nullable LatLngBounds intersectNoParamCheck( + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE)double northLat, + double eastLon, + @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double southLat, + double westLon) { + + double maxsouthLat = Math.max(getLatSouth(), Math.min(GeometryConstants.MAX_LATITUDE, southLat)); + double minnorthLat = Math.min(getLatNorth(), Math.max(GeometryConstants.MIN_LATITUDE, northLat)); + if (minnorthLat < maxsouthLat) { + return null; + } + + eastLon = LatLng.wrap(eastLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + westLon = LatLng.wrap(westLon, GeometryConstants.MIN_LONGITUDE, GeometryConstants.MAX_LONGITUDE); + + // longitudes match + if (this.longitudeEast == eastLon && this.longitudeWest == westLon) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, westLon); + } + + boolean eastInThis = containsLongitude(this.longitudeEast, this.longitudeWest, eastLon); + boolean westInThis = containsLongitude(this.longitudeEast, this.longitudeWest, westLon); + boolean thisEastInside = containsLongitude(eastLon, westLon, this.longitudeEast); + boolean thisWestInside = containsLongitude(eastLon, westLon, this.longitudeWest); + + // two intersections : find the one that has longest span + if (eastInThis && westInThis && thisEastInside && thisWestInside) { + + if (getLongitudeSpan(eastLon, this.longitudeWest) > getLongitudeSpan(this.longitudeEast, westLon)) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, this.longitudeWest); + } + + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, westLon); + } + + if (eastInThis) { + if (westInThis) { + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, westLon); + } + return new LatLngBounds(minnorthLat, eastLon, maxsouthLat, this.longitudeWest); + } + + if (thisEastInside) { + if (thisWestInside) { + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, this.longitudeWest); + } + return new LatLngBounds(minnorthLat, this.longitudeEast, maxsouthLat, westLon); + } + + return null; } /** @@ -518,7 +642,7 @@ public class LatLngBounds implements Parcelable { return (int) ((latitudeNorth + 90) + ((latitudeSouth + 90) * 1000) + ((longitudeEast + 180) * 1000000) - + ((longitudeEast + 180) * 1000000000)); + + ((longitudeWest + 180) * 1000000000)); } /** @@ -546,11 +670,11 @@ public class LatLngBounds implements Parcelable { } private static LatLngBounds readFromParcel(final Parcel in) { - final double lonNorth = in.readDouble(); - final double latEast = in.readDouble(); - final double lonSouth = in.readDouble(); - final double latWest = in.readDouble(); - return new LatLngBounds(lonNorth, latEast, lonSouth, latWest); + final double northLat = in.readDouble(); + final double eastLon = in.readDouble(); + final double southLat = in.readDouble(); + final double westLon = in.readDouble(); + return new LatLngBounds(northLat, eastLon, southLat, westLon); } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java index c66e4b6fda..789a1b2b37 100644 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java @@ -356,6 +356,58 @@ public class LatLngBoundsTest { assertNull(latLngBounds.intersect(this.latLngBounds)); } + @Test + public void intersectNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .intersect(200, 200, 0, 0); + } + + @Test + public void intersectSouthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(0, 0, -10, -10) + .intersect(0, 0, -200, -200); + } + + @Test + public void intersectSouthLessThanNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("LatSouth cannot be less than latNorth"); + + LatLngBounds intersectLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .intersect(0, 200, 20, 0); + } + + + @Test + public void intersectEastWrapCheck() { + + LatLngBounds latLngBounds1 = LatLngBounds.from(10, -150, 0, 0); + LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); + + LatLngBounds intersectLatLngBounds = LatLngBounds.from(10, -160, 0, 0); + + assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); + assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); + } + + @Test + public void intersectWestWrapCheck() { + LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, 150); + LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); + + LatLngBounds intersectLatLngBounds = LatLngBounds.from(0, 0, -10, 160); + + assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); + assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); + } + @Test public void innerUnion() { LatLngBounds latLngBounds = new LatLngBounds.Builder() @@ -391,14 +443,176 @@ public class LatLngBoundsTest { .include(new LatLng(-10, -160)) .build(); - assertEquals("outer union should match", - latLngBounds1.union(latLngBounds2), + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine2() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, 170)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, 165)) + .include(new LatLng(-10, -160)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine3() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -165)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, -170)) + .include(new LatLng(-10, -160)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLine4() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -160)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, -170)) + .include(new LatLng(-10, -175)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, new LatLngBounds.Builder() .include(new LatLng(10, 160)) .include(new LatLng(-10, -160)) .build()); + + assertEquals(union1, union2); } + @Test + public void unionOverDateLine5() { + LatLngBounds latLngBounds1 = new LatLngBounds.Builder() + .include(new LatLng(10, -160)) + .include(new LatLng(0, 160)) + .build(); + + LatLngBounds latLngBounds2 = new LatLngBounds.Builder() + .include(new LatLng(0, 170)) + .include(new LatLng(-10, 175)) + .build(); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, + new LatLngBounds.Builder() + .include(new LatLng(10, 160)) + .include(new LatLng(-10, -160)) + .build()); + + assertEquals(union1, union2); + } + + @Test + public void unionOverDateLineReturnWorldLonSpan() { + LatLngBounds latLngBounds1 = LatLngBounds.from(10, -160, -10, -10); + LatLngBounds latLngBounds2 = LatLngBounds.from(10, 10, -10, 160); + + LatLngBounds union1 = latLngBounds1.union(latLngBounds2); + LatLngBounds union2 = latLngBounds2.union(latLngBounds1); + + assertEquals(union1, union2); + assertEquals(union1, LatLngBounds.from(10, 180, -10, -180)); + } + + @Test + public void unionNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds unionLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .union(200, 200, 0, 0); + } + + @Test + public void unionSouthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("latitude must be between -90 and 90"); + LatLngBounds unionLatLngBounds = + LatLngBounds.from(0, 0, -10, -10) + .union(0, 0, -200, -200); + } + + @Test + public void unionSouthLessThanNorthCheck() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("LatSouth cannot be less than latNorth"); + + LatLngBounds unionLatLngBounds = + LatLngBounds.from(10, 10, 0, 0) + .union(0, 200, 20, 0); + } + + + @Test + public void unionEastWrapCheck() { + + LatLngBounds latLngBounds1 = LatLngBounds.from(10, 10, 0, 0); + LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); + LatLngBounds unionLatLngBounds = LatLngBounds.from(90, -160, 0, 0); + + assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); + assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); + } + + @Test + public void unionWestWrapCheck() { + LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, -10); + LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); + + LatLngBounds unionLatLngBounds = LatLngBounds.from(0, 0, -90, 160); + + assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); + assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); + } @Test public void northWest() { -- cgit v1.2.1 From 0d067e39253813b07c421a1c0a21c57a775fd8ec Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Fri, 11 May 2018 16:02:48 -0400 Subject: [ios, build] Re-add support for 32-bit simulators (i386) --- platform/ios/CHANGELOG.md | 6 +++++- platform/ios/INSTALL.md | 2 +- platform/ios/scripts/package.sh | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 9520a5d24e..7bf8f17389 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -4,10 +4,14 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ## 4.0.1 +### Packaging + +* Re-added support for 32-bit simulators (i386) to work around an issue in CocoaPods. ([#11891](https://github.com/mapbox/mapbox-gl-native/pull/11891)) + ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) -* Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) +* Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) ### Annotations diff --git a/platform/ios/INSTALL.md b/platform/ios/INSTALL.md index 01f4621b1e..9c46e7fbe0 100644 --- a/platform/ios/INSTALL.md +++ b/platform/ios/INSTALL.md @@ -12,7 +12,7 @@ The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the follo _Note: Support for iOS 8 will be removed in a future release and the minimum iOS deployment version will increase to iOS 9.0._ -Note that 32-bit simulators (such as the iPhone 5 or iPad 2) are not supported. +Note that debugging in 32-bit simulators (such as the iPhone 5 or iPad 2) is only partially supported. The Mapbox Maps SDK for iOS requires: diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh index 6df1b687b4..438fce240f 100755 --- a/platform/ios/scripts/package.sh +++ b/platform/ios/scripts/package.sh @@ -70,7 +70,7 @@ xcodebuild \ CURRENT_SHORT_VERSION=${SHORT_VERSION} \ CURRENT_SEMANTIC_VERSION=${SEM_VERSION} \ CURRENT_COMMIT_HASH=${HASH} \ - ARCHS="x86_64" \ + ONLY_ACTIVE_ARCH=NO \ -derivedDataPath ${DERIVED_DATA} \ -workspace ./platform/ios/ios.xcworkspace \ -scheme ${SCHEME} \ @@ -194,6 +194,10 @@ if [[ ${BUILD_DYNAMIC} == true && ${BUILDTYPE} == Release ]]; then validate_dsym \ "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" \ "${OUTPUT}/dynamic/${NAME}.framework/${NAME}" + + step "Removing i386 slice from dSYM" + lipo -remove i386 "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" -o "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" + lipo -info "${OUTPUT}/dynamic/${NAME}.framework.dSYM/Contents/Resources/DWARF/${NAME}" fi function create_podspec { -- cgit v1.2.1 From 7854dfa3a02a7c5425b1c5066012fb81028df967 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 17 Apr 2018 11:26:25 +0200 Subject: [android] - integrate view callback abstraction --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 34 +++++----- .../com/mapbox/mapboxsdk/maps/NativeMapView.java | 73 +++++++++++++--------- 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 22d5dd8f19..4ccbc88375 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -1,6 +1,7 @@ package com.mapbox.mapboxsdk.maps; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.PointF; import android.opengl.GLSurfaceView; import android.os.Build; @@ -40,6 +41,7 @@ import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.utils.BitmapUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -47,13 +49,10 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -import timber.log.Timber; - import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -71,13 +70,14 @@ import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; * Warning: Please note that you are responsible for getting permission to use the map data, * and for ensuring your use adheres to the relevant terms of use. */ -public class MapView extends FrameLayout { +public class MapView extends FrameLayout implements NativeMapView.ViewCallback { private final MapCallback mapCallback = new MapCallback(); private MapboxMap mapboxMap; private NativeMapView nativeMapView; private MapboxMapOptions mapboxMapOptions; + private MapRenderer mapRenderer; private boolean destroyed; private boolean hasSurface; @@ -90,9 +90,6 @@ public class MapView extends FrameLayout { private MapKeyListener mapKeyListener; private MapZoomButtonController mapZoomButtonController; private Bundle savedInstanceState; - private final CopyOnWriteArrayList onMapChangedListeners = new CopyOnWriteArrayList<>(); - - private MapRenderer mapRenderer; @UiThread public MapView(@NonNull Context context) { @@ -307,7 +304,7 @@ public class MapView extends FrameLayout { addView(glSurfaceView, 0); } - nativeMapView = new NativeMapView(this, mapRenderer); + nativeMapView = new NativeMapView(getContext(), this, mapRenderer); nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } @@ -566,19 +563,18 @@ public class MapView extends FrameLayout { } // - // Map events + // ViewCallback // - void onMapChange(int rawChange) { - for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { - try { - onMapChangedListener.onMapChanged(rawChange); - } catch (RuntimeException err) { - Timber.e(err, "Exception in MapView.OnMapChangedListener"); - } - } + @Override + public Bitmap getViewContent() { + return BitmapUtils.createBitmapFromView(this); } + // + // Map events + // + /** *

* Add a callback that's invoked when the displayed map view changes. @@ -590,7 +586,7 @@ public class MapView extends FrameLayout { */ public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - onMapChangedListeners.add(listener); + nativeMapView.addOnMapChangedListener(listener); } } @@ -602,7 +598,7 @@ public class MapView extends FrameLayout { */ public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - onMapChangedListeners.remove(listener); + nativeMapView.removeOnMapChangedListener(listener); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 305dd906dd..7cd1177523 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -5,6 +5,7 @@ import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; import android.os.AsyncTask; +import android.os.Handler; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -38,33 +39,36 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; // Class that wraps the native methods for convenience final class NativeMapView { - // Flag to indicating destroy was called - private boolean destroyed = false; - - // Holds the pointer to JNI NativeMapView - private long nativePtr = 0; - - // Used for callbacks - private MapView mapView; - //Hold a reference to prevent it from being GC'd as long as it's used on the native side private final FileSource fileSource; // Used to schedule work on the MapRenderer Thread - private MapRenderer mapRenderer; + private final MapRenderer mapRenderer; + + // Used for callbacks + private ViewCallback viewCallback; // Device density private final float pixelRatio; + // Flag to indicating destroy was called + private boolean destroyed = false; + + // Holds the pointer to JNI NativeMapView + private long nativePtr = 0; + // Listener invoked to return a bitmap of the map private MapboxMap.SnapshotReadyCallback snapshotReadyCallback; + private final CopyOnWriteArrayList onMapChangedListeners = new CopyOnWriteArrayList<>(); + static { LibraryLoader.load(); } @@ -73,14 +77,11 @@ final class NativeMapView { // Constructors // - public NativeMapView(final MapView mapView, MapRenderer mapRenderer) { + public NativeMapView(final Context context, final ViewCallback viewCallback, final MapRenderer mapRenderer) { this.mapRenderer = mapRenderer; - this.mapView = mapView; - - Context context = mapView.getContext(); - fileSource = FileSource.getInstance(context); - pixelRatio = context.getResources().getDisplayMetrics().density; - + this.viewCallback = viewCallback; + this.fileSource = FileSource.getInstance(context); + this.pixelRatio = context.getResources().getDisplayMetrics().density; nativeInitialize(this, fileSource, mapRenderer, pixelRatio); } @@ -100,7 +101,7 @@ final class NativeMapView { public void destroy() { nativeDestroy(); - mapView = null; + viewCallback = null; destroyed = true; } @@ -862,8 +863,12 @@ final class NativeMapView { // protected void onMapChanged(int rawChange) { - if (mapView != null) { - mapView.onMapChange(rawChange); + for (MapView.OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + try { + onMapChangedListener.onMapChanged(rawChange); + } catch (RuntimeException err) { + Timber.e(err, "Exception in MapView.OnMapChangedListener"); + } } } @@ -872,7 +877,7 @@ final class NativeMapView { return; } - Bitmap viewContent = BitmapUtils.createBitmapFromView(mapView); + Bitmap viewContent = viewCallback.getViewContent(); if (snapshotReadyCallback != null && mapContent != null && viewContent != null) { snapshotReadyCallback.onSnapshotReady(BitmapUtils.mergeBitmap(mapContent, viewContent)); } @@ -1070,14 +1075,14 @@ final class NativeMapView { if (isDestroyedOn("")) { return 0; } - return mapView.getWidth(); + return viewCallback.getWidth(); } int getHeight() { if (isDestroyedOn("")) { return 0; } - return mapView.getHeight(); + return viewCallback.getHeight(); } // @@ -1085,13 +1090,13 @@ final class NativeMapView { // void addOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - if (mapView != null) { - mapView.addOnMapChangedListener(listener); - } + onMapChangedListeners.add(listener); } void removeOnMapChangedListener(@NonNull MapView.OnMapChangedListener listener) { - mapView.removeOnMapChangedListener(listener); + if (onMapChangedListeners.contains(listener)) { + onMapChangedListeners.remove(listener); + } } // @@ -1107,15 +1112,15 @@ final class NativeMapView { } public void setOnFpsChangedListener(final MapboxMap.OnFpsChangedListener listener) { + final Handler handler = new Handler(); mapRenderer.queueEvent(new Runnable() { @Override public void run() { mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() { - @Override public void onFpsChanged(final double fps) { - mapView.post(new Runnable() { + handler.post(new Runnable() { @Override public void run() { @@ -1124,14 +1129,12 @@ final class NativeMapView { }); } - }); } }); } - // // Image conversion // @@ -1181,4 +1184,12 @@ final class NativeMapView { } } } + + public interface ViewCallback { + int getWidth(); + + int getHeight(); + + Bitmap getViewContent(); + } } -- cgit v1.2.1 From 5380d80c88087f20238592aaa19e8b48fc383a5e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 24 Apr 2018 11:39:40 +0200 Subject: [android] - allow early callback registration --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 23 +++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 4ccbc88375..9227aabdf3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -49,6 +49,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -73,9 +74,10 @@ import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; public class MapView extends FrameLayout implements NativeMapView.ViewCallback { private final MapCallback mapCallback = new MapCallback(); - private MapboxMap mapboxMap; + private final CopyOnWriteArrayList onMapChangedListeners = new CopyOnWriteArrayList<>(); private NativeMapView nativeMapView; + private MapboxMap mapboxMap; private MapboxMapOptions mapboxMapOptions; private MapRenderer mapRenderer; private boolean destroyed; @@ -137,7 +139,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { private void initialiseMap() { Context context = getContext(); - addOnMapChangedListener(mapCallback); + nativeMapView.addOnMapChangedListener(mapCallback); // callback for focal point invalidation final FocalPointInvalidator focalPointInvalidator = new FocalPointInvalidator(); @@ -305,6 +307,17 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { } nativeMapView = new NativeMapView(getContext(), this, mapRenderer); + nativeMapView.addOnMapChangedListener(new OnMapChangedListener() { + @Override + public void onMapChanged(int change) { + // dispatch events to external listeners + if (!onMapChangedListeners.isEmpty()) { + for (OnMapChangedListener onMapChangedListener : onMapChangedListeners) { + onMapChangedListener.onMapChanged(change); + } + } + } + }); nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } @@ -586,7 +599,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { */ public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { if (listener != null) { - nativeMapView.addOnMapChangedListener(listener); + onMapChangedListeners.add(listener); } } @@ -597,8 +610,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @see MapView#addOnMapChangedListener(OnMapChangedListener) */ public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null) { - nativeMapView.removeOnMapChangedListener(listener); + if (listener != null && onMapChangedListeners.contains(listener)) { + onMapChangedListeners.remove(listener); } } -- cgit v1.2.1 From 5653b340bc2de15b1d17661784bbb451b47bcece Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 24 Apr 2018 14:01:48 +0200 Subject: [android] - clear map change listeners when map is destroyed --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 1 + .../src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 9227aabdf3..4ecd7c9246 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -410,6 +410,7 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { @UiThread public void onDestroy() { destroyed = true; + onMapChangedListeners.clear(); mapCallback.clearOnMapReadyCallbacks(); if (nativeMapView != null && hasSurface) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 7cd1177523..ae18f741d4 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -100,9 +100,10 @@ final class NativeMapView { } public void destroy() { - nativeDestroy(); - viewCallback = null; destroyed = true; + onMapChangedListeners.clear(); + viewCallback = null; + nativeDestroy(); } public void update() { -- cgit v1.2.1 From 65743769a4fa7740eaffd91534577fe39014a13b Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 13 Apr 2018 15:43:42 +0200 Subject: [android] - improve stop javadoc to include interpolate --- .../java/com/mapbox/mapboxsdk/style/expressions/Expression.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java index bd5b40c6ce..44ad5e83ed 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java @@ -2878,7 +2878,11 @@ public class Expression { } /** - * Produces a stop value to be used as part of the step expression. + * Produces a stop value. + *

+ * Can be used for {@link #stop(Object, Object)} as part of varargs parameter in + * {@link #step(Number, Expression, Stop...)} or {@link #interpolate(Interpolator, Expression, Stop...)}. + *

*

* Example usage: *

-- cgit v1.2.1 From 28da56465e59702b4ae0219f1c7494a9271f6227 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 25 Apr 2018 14:35:05 +0200 Subject: [android] - promote pixel ratio to public api of NativeMapview. --- .../src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index ae18f741d4..d258064908 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -78,10 +78,15 @@ final class NativeMapView { // public NativeMapView(final Context context, final ViewCallback viewCallback, final MapRenderer mapRenderer) { + this(context, context.getResources().getDisplayMetrics().density, viewCallback, mapRenderer); + } + + public NativeMapView(final Context context, float pixelRatio, + final ViewCallback viewCallback, final MapRenderer mapRenderer) { this.mapRenderer = mapRenderer; this.viewCallback = viewCallback; this.fileSource = FileSource.getInstance(context); - this.pixelRatio = context.getResources().getDisplayMetrics().density; + this.pixelRatio = pixelRatio; nativeInitialize(this, fileSource, mapRenderer, pixelRatio); } -- cgit v1.2.1 From 07ad29d30da44ded2bf40418b3625fecfb817399 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Mon, 14 May 2018 16:44:43 +0300 Subject: [qt] Fix build when building the Qt Location plugin - MinGW has to explicitly know that is building a static library. - Android doesn't have 'round' on the std:: namespace when using g++. --- platform/qt/include/qmapbox.hpp | 10 +++++++--- src/mbgl/style/expression/compound_expression.cpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp index 369890343f..1ab04403cf 100644 --- a/platform/qt/include/qmapbox.hpp +++ b/platform/qt/include/qmapbox.hpp @@ -9,10 +9,14 @@ // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style -#if defined(QT_BUILD_MAPBOXGL_LIB) - #define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT +#if !defined(QT_MAPBOXGL_STATIC) +# if defined(QT_BUILD_MAPBOXGL_LIB) +# define Q_MAPBOXGL_EXPORT Q_DECL_EXPORT +# else +# define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT +# endif #else - #define Q_MAPBOXGL_EXPORT Q_DECL_IMPORT +# define Q_MAPBOXGL_EXPORT #endif namespace QMapbox { diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index c257dbf2bb..bcde09e1b6 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -364,7 +364,7 @@ std::unordered_map initiali return result; }); - define("round", [](double x) -> Result { return std::round(x); }); + define("round", [](double x) -> Result { return ::round(x); }); define("floor", [](double x) -> Result { return std::floor(x); }); define("ceil", [](double x) -> Result { return std::ceil(x); }); define("abs", [](double x) -> Result { return std::abs(x); }); -- cgit v1.2.1 From 62c875e01b07197024e3806e8b2882160ce1195c Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Mon, 14 May 2018 12:38:14 -0700 Subject: [core] Rework spec function/expression taxonomy Ports https://github.com/mapbox/mapbox-gl-js/pull/6521, updating codegen scripts to parse new expression taxonomy. --- mapbox-gl-js | 2 +- .../testapp/style/BackgroundLayerTest.java | 2 +- .../mapboxsdk/testapp/style/CircleLayerTest.java | 2 +- .../testapp/style/FillExtrusionLayerTest.java | 2 +- .../mapboxsdk/testapp/style/FillLayerTest.java | 2 +- .../mapboxsdk/testapp/style/HeatmapLayerTest.java | 2 +- .../testapp/style/HillshadeLayerTest.java | 2 +- .../mapboxsdk/testapp/style/LineLayerTest.java | 2 +- .../mapboxsdk/testapp/style/RasterLayerTest.java | 2 +- .../mapboxsdk/testapp/style/SymbolLayerTest.java | 2 +- .../mapbox/mapboxsdk/testapp/style/layer.junit.ejs | 4 +- platform/android/scripts/generate-style-code.js | 18 ++- platform/darwin/scripts/generate-style-code.js | 11 +- platform/darwin/src/MGLStyleLayer.mm.ejs | 12 +- .../darwin/test/MGLBackgroundStyleLayerTests.mm | 9 +- platform/darwin/test/MGLCircleStyleLayerTests.mm | 33 ++--- .../darwin/test/MGLFillExtrusionStyleLayerTests.mm | 21 +-- platform/darwin/test/MGLFillStyleLayerTests.mm | 21 +-- platform/darwin/test/MGLHeatmapStyleLayerTests.mm | 12 +- .../darwin/test/MGLHillshadeStyleLayerTests.mm | 18 +-- platform/darwin/test/MGLLineStyleLayerTests.mm | 42 ++---- platform/darwin/test/MGLRasterStyleLayerTests.mm | 21 +-- platform/darwin/test/MGLStyleLayerTests.mm.ejs | 14 +- platform/darwin/test/MGLSymbolStyleLayerTests.mm | 150 +++++++-------------- platform/node/test/ignores.json | 8 +- scripts/generate-style-code.js | 42 +++--- test/style/expression/expression.test.cpp | 6 +- 27 files changed, 184 insertions(+), 278 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 5f3bdb9b00..5b3af8e151 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 5f3bdb9b00915d702ae9b967c5ebc15ab33ba653 +Subproject commit 5b3af8e1515ee311967661b5aabdab65f1376e36 diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java index 2d16291832..2a8ac36507 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java @@ -160,4 +160,4 @@ public class BackgroundLayerTest extends BaseActivityTest { assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java index a851fde6dd..101d22a531 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java @@ -518,4 +518,4 @@ public class CircleLayerTest extends BaseActivityTest { }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java index f22956072d..84b3e7bd68 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java @@ -354,4 +354,4 @@ public class FillExtrusionLayerTest extends BaseActivityTest { }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java index bdbd8958d2..3e1cbf666d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java @@ -353,4 +353,4 @@ public class FillLayerTest extends BaseActivityTest { assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon"); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java index 549f309e4f..3a81786df4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java @@ -237,4 +237,4 @@ public class HeatmapLayerTest extends BaseActivityTest { assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java index 1fdc6d6dab..e0121a704a 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java @@ -252,4 +252,4 @@ public class HillshadeLayerTest extends BaseActivityTest { assertEquals(layer.getHillshadeAccentColorAsInt(), Color.RED); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java index 40cf0f2927..e35f0edcc4 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java @@ -545,4 +545,4 @@ public class LineLayerTest extends BaseActivityTest { assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon"); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java index 0410d09369..6e5afdb479 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java @@ -254,4 +254,4 @@ public class RasterLayerTest extends BaseActivityTest { assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java index 62f73b1507..fe38fef253 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java @@ -1392,4 +1392,4 @@ public class SymbolLayerTest extends BaseActivityTest { assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP); }); } -} \ No newline at end of file +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs index 935813899f..575f64e809 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs @@ -150,7 +150,7 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest { assertEquals((<%- propertyType(property) %>) layer.get<%- camelize(property.name) %>().getValue(), (<%- propertyType(property) %>) <%- defaultValueJava(property) %>); }); } -<% if (isDataDriven(property)) { -%> +<% if (property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven') { -%> <% if (!(property.name.endsWith("-font")||property.name.endsWith("-offset"))) { -%> @Test @@ -188,4 +188,4 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest { <% } -%> <% } -%> <% } -%> -} \ No newline at end of file +} diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js index 3b0363cc19..05ca957974 100755 --- a/platform/android/scripts/generate-style-code.js +++ b/platform/android/scripts/generate-style-code.js @@ -267,19 +267,17 @@ global.propertyValueDoc = function (property, value) { return doc; }; -global.isDataDriven = function (property) { - return property['property-function'] === true; -}; - global.isLightProperty = function (property) { return property['light-property'] === true; }; global.propertyValueType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenPropertyValue<${evaluatedType(property)}>`; - } else { - return `PropertyValue<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + default: + return `PropertyValue<${evaluatedType(property)}>`; } }; @@ -318,11 +316,11 @@ global.evaluatedType = function (property) { }; global.supportsZoomFunction = function (property) { - return property['zoom-function'] === true; + return property.expression && property.expression.parameters.indexOf('zoom') > -1; }; global.supportsPropertyFunction = function (property) { - return property['property-function'] === true; + return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; }; // Template processing // diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index c7b54b326a..47106eeac4 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -318,10 +318,11 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { doc += '* Predefined functions, including mathematical and string operators\n' + '* Conditional expressions\n' + '* Variable assignments and references to assigned variables\n'; - const inputVariable = property.name === 'heatmap-color' ? '$heatmapDensity' : '$zoomLevel'; - if (property["property-function"]) { + const inputVariable = property.expression && property['property-type'] === 'color-ramp' ? + '$' + camelizeWithLeadingLowercase(property.expression.parameters[0]) : '$zoomLevel'; + if (isDataDriven(property)) { doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable and/or feature attributes\n`; - } else if (property.function === "interpolated") { + } else if (property.expression && property.expression.interpolated) { doc += `* Interpolation and step functions applied to the \`${inputVariable}\` variable\n\n` + 'This property does not support applying interpolation or step functions to feature attributes.'; } else { @@ -332,6 +333,10 @@ global.propertyDoc = function (propertyName, property, layerType, kind) { return doc; }; +global.isDataDriven = function (property) { + return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; +}; + global.propertyReqs = function (property, propertiesByName, type) { return 'This property is only applied to the style if ' + property.requires.map(function (req) { if (typeof req === 'string') { diff --git a/platform/darwin/src/MGLStyleLayer.mm.ejs b/platform/darwin/src/MGLStyleLayer.mm.ejs index 42940083b5..5405598124 100644 --- a/platform/darwin/src/MGLStyleLayer.mm.ejs +++ b/platform/darwin/src/MGLStyleLayer.mm.ejs @@ -121,7 +121,7 @@ namespace mbgl { - (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); -<% if (property["property-function"]) { -%> +<% if (isDataDriven(property)) { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); <% } else { -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); @@ -162,11 +162,15 @@ namespace mbgl { - (void)set<%- camelize(property.name) %>:(NSExpression *)<%- objCName(property) %> { MGLAssertStyleLayerIsValid(); -<% if (property.name === 'heatmap-color') { -%> +<% switch (property['property-type']) { + case 'color-ramp': -%> auto mbglValue = MGLStyleValueTransformer().toPropertyValue(heatmapColor); -<% } else if (property["property-function"]) { -%> +<% break + case 'data-driven': + case 'cross-faded-data-driven': -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); -<% } else { -%> +<% break + default: -%> auto mbglValue = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toPropertyValue>>(<%- objCName(property) %>); <% } -%> self.rawLayer->set<%- camelize(originalPropertyName(property)) %>(mbglValue); diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm index de8080f425..bf18b2d30e 100644 --- a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm +++ b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm @@ -50,13 +50,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundColor(), propertyValue, @"Setting backgroundColor to a camera expression should update background-color."); XCTAssertEqualObjects(layer.backgroundColor, functionExpression, @"backgroundColor should round-trip camera expressions."); - layer.backgroundColor = nil; XCTAssertTrue(rawLayer->getBackgroundColor().isUndefined(), @@ -103,13 +102,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundOpacity(), propertyValue, @"Setting backgroundOpacity to a camera expression should update background-opacity."); XCTAssertEqualObjects(layer.backgroundOpacity, functionExpression, @"backgroundOpacity should round-trip camera expressions."); - layer.backgroundOpacity = nil; XCTAssertTrue(rawLayer->getBackgroundOpacity().isUndefined(), @@ -156,13 +154,12 @@ { 18, "Background Pattern" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getBackgroundPattern(), propertyValue, @"Setting backgroundPattern to a camera expression should update background-pattern."); XCTAssertEqualObjects(layer.backgroundPattern, functionExpression, @"backgroundPattern should round-trip camera expressions."); - layer.backgroundPattern = nil; XCTAssertTrue(rawLayer->getBackgroundPattern().isUndefined(), diff --git a/platform/darwin/test/MGLCircleStyleLayerTests.mm b/platform/darwin/test/MGLCircleStyleLayerTests.mm index d7bf2a5afd..41f7238da2 100644 --- a/platform/darwin/test/MGLCircleStyleLayerTests.mm +++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm @@ -71,7 +71,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleBlur(), propertyValue, @"Setting circleBlur to a camera expression should update circle-blur."); XCTAssertEqualObjects(layer.circleBlur, functionExpression, @@ -102,7 +102,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression, @"circleBlur should round-trip camera-data expressions."); - layer.circleBlur = nil; XCTAssertTrue(rawLayer->getCircleBlur().isUndefined(), @@ -143,7 +142,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleColor(), propertyValue, @"Setting circleColor to a camera expression should update circle-color."); XCTAssertEqualObjects(layer.circleColor, functionExpression, @@ -174,7 +173,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression, @"circleColor should round-trip camera-data expressions."); - layer.circleColor = nil; XCTAssertTrue(rawLayer->getCircleColor().isUndefined(), @@ -215,7 +213,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleOpacity(), propertyValue, @"Setting circleOpacity to a camera expression should update circle-opacity."); XCTAssertEqualObjects(layer.circleOpacity, functionExpression, @@ -246,7 +244,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression, @"circleOpacity should round-trip camera-data expressions."); - layer.circleOpacity = nil; XCTAssertTrue(rawLayer->getCircleOpacity().isUndefined(), @@ -287,13 +284,12 @@ { 18, mbgl::style::AlignmentType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCirclePitchAlignment(), propertyValue, @"Setting circlePitchAlignment to a camera expression should update circle-pitch-alignment."); XCTAssertEqualObjects(layer.circlePitchAlignment, functionExpression, @"circlePitchAlignment should round-trip camera expressions."); - layer.circlePitchAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchAlignment().isUndefined(), @@ -331,7 +327,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleRadius(), propertyValue, @"Setting circleRadius to a camera expression should update circle-radius."); XCTAssertEqualObjects(layer.circleRadius, functionExpression, @@ -362,7 +358,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression, @"circleRadius should round-trip camera-data expressions."); - layer.circleRadius = nil; XCTAssertTrue(rawLayer->getCircleRadius().isUndefined(), @@ -403,13 +398,12 @@ { 18, mbgl::style::CirclePitchScaleType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCirclePitchScale(), propertyValue, @"Setting circleScaleAlignment to a camera expression should update circle-pitch-scale."); XCTAssertEqualObjects(layer.circleScaleAlignment, functionExpression, @"circleScaleAlignment should round-trip camera expressions."); - layer.circleScaleAlignment = nil; XCTAssertTrue(rawLayer->getCirclePitchScale().isUndefined(), @@ -447,7 +441,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeColor(), propertyValue, @"Setting circleStrokeColor to a camera expression should update circle-stroke-color."); XCTAssertEqualObjects(layer.circleStrokeColor, functionExpression, @@ -478,7 +472,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression, @"circleStrokeColor should round-trip camera-data expressions."); - layer.circleStrokeColor = nil; XCTAssertTrue(rawLayer->getCircleStrokeColor().isUndefined(), @@ -519,7 +512,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeOpacity(), propertyValue, @"Setting circleStrokeOpacity to a camera expression should update circle-stroke-opacity."); XCTAssertEqualObjects(layer.circleStrokeOpacity, functionExpression, @@ -550,7 +543,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression, @"circleStrokeOpacity should round-trip camera-data expressions."); - layer.circleStrokeOpacity = nil; XCTAssertTrue(rawLayer->getCircleStrokeOpacity().isUndefined(), @@ -591,7 +583,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleStrokeWidth(), propertyValue, @"Setting circleStrokeWidth to a camera expression should update circle-stroke-width."); XCTAssertEqualObjects(layer.circleStrokeWidth, functionExpression, @@ -622,7 +614,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression, @"circleStrokeWidth should round-trip camera-data expressions."); - layer.circleStrokeWidth = nil; XCTAssertTrue(rawLayer->getCircleStrokeWidth().isUndefined(), @@ -669,13 +660,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleTranslate(), propertyValue, @"Setting circleTranslation to a camera expression should update circle-translate."); XCTAssertEqualObjects(layer.circleTranslation, functionExpression, @"circleTranslation should round-trip camera expressions."); - layer.circleTranslation = nil; XCTAssertTrue(rawLayer->getCircleTranslate().isUndefined(), @@ -713,13 +703,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getCircleTranslateAnchor(), propertyValue, @"Setting circleTranslationAnchor to a camera expression should update circle-translate-anchor."); XCTAssertEqualObjects(layer.circleTranslationAnchor, functionExpression, @"circleTranslationAnchor should round-trip camera expressions."); - layer.circleTranslationAnchor = nil; XCTAssertTrue(rawLayer->getCircleTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm index 6081d104e1..ac858d0edc 100644 --- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm @@ -71,7 +71,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionBase(), propertyValue, @"Setting fillExtrusionBase to a camera expression should update fill-extrusion-base."); XCTAssertEqualObjects(layer.fillExtrusionBase, functionExpression, @@ -102,7 +102,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression, @"fillExtrusionBase should round-trip camera-data expressions."); - layer.fillExtrusionBase = nil; XCTAssertTrue(rawLayer->getFillExtrusionBase().isUndefined(), @@ -143,7 +142,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionColor(), propertyValue, @"Setting fillExtrusionColor to a camera expression should update fill-extrusion-color."); XCTAssertEqualObjects(layer.fillExtrusionColor, functionExpression, @@ -174,7 +173,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression, @"fillExtrusionColor should round-trip camera-data expressions."); - layer.fillExtrusionColor = nil; XCTAssertTrue(rawLayer->getFillExtrusionColor().isUndefined(), @@ -215,7 +213,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionHeight(), propertyValue, @"Setting fillExtrusionHeight to a camera expression should update fill-extrusion-height."); XCTAssertEqualObjects(layer.fillExtrusionHeight, functionExpression, @@ -246,7 +244,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression, @"fillExtrusionHeight should round-trip camera-data expressions."); - layer.fillExtrusionHeight = nil; XCTAssertTrue(rawLayer->getFillExtrusionHeight().isUndefined(), @@ -287,13 +284,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionOpacity(), propertyValue, @"Setting fillExtrusionOpacity to a camera expression should update fill-extrusion-opacity."); XCTAssertEqualObjects(layer.fillExtrusionOpacity, functionExpression, @"fillExtrusionOpacity should round-trip camera expressions."); - layer.fillExtrusionOpacity = nil; XCTAssertTrue(rawLayer->getFillExtrusionOpacity().isUndefined(), @@ -340,13 +336,12 @@ { 18, "Fill Extrusion Pattern" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionPattern(), propertyValue, @"Setting fillExtrusionPattern to a camera expression should update fill-extrusion-pattern."); XCTAssertEqualObjects(layer.fillExtrusionPattern, functionExpression, @"fillExtrusionPattern should round-trip camera expressions."); - layer.fillExtrusionPattern = nil; XCTAssertTrue(rawLayer->getFillExtrusionPattern().isUndefined(), @@ -399,13 +394,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionTranslate(), propertyValue, @"Setting fillExtrusionTranslation to a camera expression should update fill-extrusion-translate."); XCTAssertEqualObjects(layer.fillExtrusionTranslation, functionExpression, @"fillExtrusionTranslation should round-trip camera expressions."); - layer.fillExtrusionTranslation = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslate().isUndefined(), @@ -443,13 +437,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillExtrusionTranslateAnchor(), propertyValue, @"Setting fillExtrusionTranslationAnchor to a camera expression should update fill-extrusion-translate-anchor."); XCTAssertEqualObjects(layer.fillExtrusionTranslationAnchor, functionExpression, @"fillExtrusionTranslationAnchor should round-trip camera expressions."); - layer.fillExtrusionTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillExtrusionTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLFillStyleLayerTests.mm b/platform/darwin/test/MGLFillStyleLayerTests.mm index a5019f1032..45bae07f43 100644 --- a/platform/darwin/test/MGLFillStyleLayerTests.mm +++ b/platform/darwin/test/MGLFillStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, false }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillAntialias(), propertyValue, @"Setting fillAntialiased to a camera expression should update fill-antialias."); XCTAssertEqualObjects(layer.fillAntialiased, functionExpression, @"fillAntialiased should round-trip camera expressions."); - layer.fillAntialiased = nil; XCTAssertTrue(rawLayer->getFillAntialias().isUndefined(), @@ -115,7 +114,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillColor(), propertyValue, @"Setting fillColor to a camera expression should update fill-color."); XCTAssertEqualObjects(layer.fillColor, functionExpression, @@ -146,7 +145,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression, @"fillColor should round-trip camera-data expressions."); - layer.fillColor = nil; XCTAssertTrue(rawLayer->getFillColor().isUndefined(), @@ -187,7 +185,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillOpacity(), propertyValue, @"Setting fillOpacity to a camera expression should update fill-opacity."); XCTAssertEqualObjects(layer.fillOpacity, functionExpression, @@ -218,7 +216,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression, @"fillOpacity should round-trip camera-data expressions."); - layer.fillOpacity = nil; XCTAssertTrue(rawLayer->getFillOpacity().isUndefined(), @@ -259,7 +256,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillOutlineColor(), propertyValue, @"Setting fillOutlineColor to a camera expression should update fill-outline-color."); XCTAssertEqualObjects(layer.fillOutlineColor, functionExpression, @@ -290,7 +287,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression, @"fillOutlineColor should round-trip camera-data expressions."); - layer.fillOutlineColor = nil; XCTAssertTrue(rawLayer->getFillOutlineColor().isUndefined(), @@ -331,13 +327,12 @@ { 18, "Fill Pattern" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillPattern(), propertyValue, @"Setting fillPattern to a camera expression should update fill-pattern."); XCTAssertEqualObjects(layer.fillPattern, functionExpression, @"fillPattern should round-trip camera expressions."); - layer.fillPattern = nil; XCTAssertTrue(rawLayer->getFillPattern().isUndefined(), @@ -390,13 +385,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getFillTranslate(), propertyValue, @"Setting fillTranslation to a camera expression should update fill-translate."); XCTAssertEqualObjects(layer.fillTranslation, functionExpression, @"fillTranslation should round-trip camera expressions."); - layer.fillTranslation = nil; XCTAssertTrue(rawLayer->getFillTranslate().isUndefined(), @@ -434,13 +428,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getFillTranslateAnchor(), propertyValue, @"Setting fillTranslationAnchor to a camera expression should update fill-translate-anchor."); XCTAssertEqualObjects(layer.fillTranslationAnchor, functionExpression, @"fillTranslationAnchor should round-trip camera expressions."); - layer.fillTranslationAnchor = nil; XCTAssertTrue(rawLayer->getFillTranslateAnchor().isUndefined(), diff --git a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm index e4b1917257..db15778872 100644 --- a/platform/darwin/test/MGLHeatmapStyleLayerTests.mm +++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapIntensity(), propertyValue, @"Setting heatmapIntensity to a camera expression should update heatmap-intensity."); XCTAssertEqualObjects(layer.heatmapIntensity, functionExpression, @"heatmapIntensity should round-trip camera expressions."); - layer.heatmapIntensity = nil; XCTAssertTrue(rawLayer->getHeatmapIntensity().isUndefined(), @@ -124,13 +123,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapOpacity(), propertyValue, @"Setting heatmapOpacity to a camera expression should update heatmap-opacity."); XCTAssertEqualObjects(layer.heatmapOpacity, functionExpression, @"heatmapOpacity should round-trip camera expressions."); - layer.heatmapOpacity = nil; XCTAssertTrue(rawLayer->getHeatmapOpacity().isUndefined(), @@ -177,7 +175,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapRadius(), propertyValue, @"Setting heatmapRadius to a camera expression should update heatmap-radius."); XCTAssertEqualObjects(layer.heatmapRadius, functionExpression, @@ -208,7 +206,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression, @"heatmapRadius should round-trip camera-data expressions."); - layer.heatmapRadius = nil; XCTAssertTrue(rawLayer->getHeatmapRadius().isUndefined(), @@ -249,7 +246,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHeatmapWeight(), propertyValue, @"Setting heatmapWeight to a camera expression should update heatmap-weight."); XCTAssertEqualObjects(layer.heatmapWeight, functionExpression, @@ -280,7 +277,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression, @"heatmapWeight should round-trip camera-data expressions."); - layer.heatmapWeight = nil; XCTAssertTrue(rawLayer->getHeatmapWeight().isUndefined(), diff --git a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm index 34937d1674..2495b32d97 100644 --- a/platform/darwin/test/MGLHillshadeStyleLayerTests.mm +++ b/platform/darwin/test/MGLHillshadeStyleLayerTests.mm @@ -53,13 +53,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeAccentColor(), propertyValue, @"Setting hillshadeAccentColor to a camera expression should update hillshade-accent-color."); XCTAssertEqualObjects(layer.hillshadeAccentColor, functionExpression, @"hillshadeAccentColor should round-trip camera expressions."); - layer.hillshadeAccentColor = nil; XCTAssertTrue(rawLayer->getHillshadeAccentColor().isUndefined(), @@ -106,13 +105,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeExaggeration(), propertyValue, @"Setting hillshadeExaggeration to a camera expression should update hillshade-exaggeration."); XCTAssertEqualObjects(layer.hillshadeExaggeration, functionExpression, @"hillshadeExaggeration should round-trip camera expressions."); - layer.hillshadeExaggeration = nil; XCTAssertTrue(rawLayer->getHillshadeExaggeration().isUndefined(), @@ -159,13 +157,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeHighlightColor(), propertyValue, @"Setting hillshadeHighlightColor to a camera expression should update hillshade-highlight-color."); XCTAssertEqualObjects(layer.hillshadeHighlightColor, functionExpression, @"hillshadeHighlightColor should round-trip camera expressions."); - layer.hillshadeHighlightColor = nil; XCTAssertTrue(rawLayer->getHillshadeHighlightColor().isUndefined(), @@ -212,13 +209,12 @@ { 18, mbgl::style::HillshadeIlluminationAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeIlluminationAnchor(), propertyValue, @"Setting hillshadeIlluminationAnchor to a camera expression should update hillshade-illumination-anchor."); XCTAssertEqualObjects(layer.hillshadeIlluminationAnchor, functionExpression, @"hillshadeIlluminationAnchor should round-trip camera expressions."); - layer.hillshadeIlluminationAnchor = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationAnchor().isUndefined(), @@ -256,13 +252,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeIlluminationDirection(), propertyValue, @"Setting hillshadeIlluminationDirection to a camera expression should update hillshade-illumination-direction."); XCTAssertEqualObjects(layer.hillshadeIlluminationDirection, functionExpression, @"hillshadeIlluminationDirection should round-trip camera expressions."); - layer.hillshadeIlluminationDirection = nil; XCTAssertTrue(rawLayer->getHillshadeIlluminationDirection().isUndefined(), @@ -300,13 +295,12 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getHillshadeShadowColor(), propertyValue, @"Setting hillshadeShadowColor to a camera expression should update hillshade-shadow-color."); XCTAssertEqualObjects(layer.hillshadeShadowColor, functionExpression, @"hillshadeShadowColor should round-trip camera expressions."); - layer.hillshadeShadowColor = nil; XCTAssertTrue(rawLayer->getHillshadeShadowColor().isUndefined(), diff --git a/platform/darwin/test/MGLLineStyleLayerTests.mm b/platform/darwin/test/MGLLineStyleLayerTests.mm index 5490278e98..aa24bc8c15 100644 --- a/platform/darwin/test/MGLLineStyleLayerTests.mm +++ b/platform/darwin/test/MGLLineStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, mbgl::style::LineCapType::Square }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineCap(), propertyValue, @"Setting lineCap to a camera expression should update line-cap."); XCTAssertEqualObjects(layer.lineCap, functionExpression, @"lineCap should round-trip camera expressions."); - layer.lineCap = nil; XCTAssertTrue(rawLayer->getLineCap().isUndefined(), @@ -115,13 +114,12 @@ { 18, mbgl::style::LineJoinType::Miter }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineJoin(), propertyValue, @"Setting lineJoin to a camera expression should update line-join."); XCTAssertEqualObjects(layer.lineJoin, functionExpression, @"lineJoin should round-trip camera expressions."); - layer.lineJoin = nil; XCTAssertTrue(rawLayer->getLineJoin().isUndefined(), @@ -153,13 +151,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineMiterLimit(), propertyValue, @"Setting lineMiterLimit to a camera expression should update line-miter-limit."); XCTAssertEqualObjects(layer.lineMiterLimit, functionExpression, @"lineMiterLimit should round-trip camera expressions."); - layer.lineMiterLimit = nil; XCTAssertTrue(rawLayer->getLineMiterLimit().isUndefined(), @@ -197,13 +194,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineRoundLimit(), propertyValue, @"Setting lineRoundLimit to a camera expression should update line-round-limit."); XCTAssertEqualObjects(layer.lineRoundLimit, functionExpression, @"lineRoundLimit should round-trip camera expressions."); - layer.lineRoundLimit = nil; XCTAssertTrue(rawLayer->getLineRoundLimit().isUndefined(), @@ -241,7 +237,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineBlur(), propertyValue, @"Setting lineBlur to a camera expression should update line-blur."); XCTAssertEqualObjects(layer.lineBlur, functionExpression, @@ -272,7 +268,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression, @"lineBlur should round-trip camera-data expressions."); - layer.lineBlur = nil; XCTAssertTrue(rawLayer->getLineBlur().isUndefined(), @@ -313,7 +308,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineColor(), propertyValue, @"Setting lineColor to a camera expression should update line-color."); XCTAssertEqualObjects(layer.lineColor, functionExpression, @@ -344,7 +339,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression, @"lineColor should round-trip camera-data expressions."); - layer.lineColor = nil; XCTAssertTrue(rawLayer->getLineColor().isUndefined(), @@ -385,13 +379,12 @@ { 18, {1, 2} }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineDasharray(), propertyValue, @"Setting lineDashPattern to a camera expression should update line-dasharray."); XCTAssertEqualObjects(layer.lineDashPattern, functionExpression, @"lineDashPattern should round-trip camera expressions."); - layer.lineDashPattern = nil; XCTAssertTrue(rawLayer->getLineDasharray().isUndefined(), @@ -429,7 +422,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineGapWidth(), propertyValue, @"Setting lineGapWidth to a camera expression should update line-gap-width."); XCTAssertEqualObjects(layer.lineGapWidth, functionExpression, @@ -460,7 +453,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression, @"lineGapWidth should round-trip camera-data expressions."); - layer.lineGapWidth = nil; XCTAssertTrue(rawLayer->getLineGapWidth().isUndefined(), @@ -501,7 +493,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineOffset(), propertyValue, @"Setting lineOffset to a camera expression should update line-offset."); XCTAssertEqualObjects(layer.lineOffset, functionExpression, @@ -532,7 +524,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression, @"lineOffset should round-trip camera-data expressions."); - layer.lineOffset = nil; XCTAssertTrue(rawLayer->getLineOffset().isUndefined(), @@ -573,7 +564,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineOpacity(), propertyValue, @"Setting lineOpacity to a camera expression should update line-opacity."); XCTAssertEqualObjects(layer.lineOpacity, functionExpression, @@ -604,7 +595,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression, @"lineOpacity should round-trip camera-data expressions."); - layer.lineOpacity = nil; XCTAssertTrue(rawLayer->getLineOpacity().isUndefined(), @@ -645,13 +635,12 @@ { 18, "Line Pattern" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLinePattern(), propertyValue, @"Setting linePattern to a camera expression should update line-pattern."); XCTAssertEqualObjects(layer.linePattern, functionExpression, @"linePattern should round-trip camera expressions."); - layer.linePattern = nil; XCTAssertTrue(rawLayer->getLinePattern().isUndefined(), @@ -704,13 +693,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getLineTranslate(), propertyValue, @"Setting lineTranslation to a camera expression should update line-translate."); XCTAssertEqualObjects(layer.lineTranslation, functionExpression, @"lineTranslation should round-trip camera expressions."); - layer.lineTranslation = nil; XCTAssertTrue(rawLayer->getLineTranslate().isUndefined(), @@ -748,13 +736,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineTranslateAnchor(), propertyValue, @"Setting lineTranslationAnchor to a camera expression should update line-translate-anchor."); XCTAssertEqualObjects(layer.lineTranslationAnchor, functionExpression, @"lineTranslationAnchor should round-trip camera expressions."); - layer.lineTranslationAnchor = nil; XCTAssertTrue(rawLayer->getLineTranslateAnchor().isUndefined(), @@ -792,7 +779,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getLineWidth(), propertyValue, @"Setting lineWidth to a camera expression should update line-width."); XCTAssertEqualObjects(layer.lineWidth, functionExpression, @@ -823,7 +810,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression, @"lineWidth should round-trip camera-data expressions."); - layer.lineWidth = nil; XCTAssertTrue(rawLayer->getLineWidth().isUndefined(), diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm index c8e454743e..cf5175812e 100644 --- a/platform/darwin/test/MGLRasterStyleLayerTests.mm +++ b/platform/darwin/test/MGLRasterStyleLayerTests.mm @@ -53,13 +53,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterBrightnessMax(), propertyValue, @"Setting maximumRasterBrightness to a camera expression should update raster-brightness-max."); XCTAssertEqualObjects(layer.maximumRasterBrightness, functionExpression, @"maximumRasterBrightness should round-trip camera expressions."); - layer.maximumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMax().isUndefined(), @@ -97,13 +96,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterBrightnessMin(), propertyValue, @"Setting minimumRasterBrightness to a camera expression should update raster-brightness-min."); XCTAssertEqualObjects(layer.minimumRasterBrightness, functionExpression, @"minimumRasterBrightness should round-trip camera expressions."); - layer.minimumRasterBrightness = nil; XCTAssertTrue(rawLayer->getRasterBrightnessMin().isUndefined(), @@ -141,13 +139,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterContrast(), propertyValue, @"Setting rasterContrast to a camera expression should update raster-contrast."); XCTAssertEqualObjects(layer.rasterContrast, functionExpression, @"rasterContrast should round-trip camera expressions."); - layer.rasterContrast = nil; XCTAssertTrue(rawLayer->getRasterContrast().isUndefined(), @@ -194,13 +191,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterFadeDuration(), propertyValue, @"Setting rasterFadeDuration to a camera expression should update raster-fade-duration."); XCTAssertEqualObjects(layer.rasterFadeDuration, functionExpression, @"rasterFadeDuration should round-trip camera expressions."); - layer.rasterFadeDuration = nil; XCTAssertTrue(rawLayer->getRasterFadeDuration().isUndefined(), @@ -238,13 +234,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterHueRotate(), propertyValue, @"Setting rasterHueRotation to a camera expression should update raster-hue-rotate."); XCTAssertEqualObjects(layer.rasterHueRotation, functionExpression, @"rasterHueRotation should round-trip camera expressions."); - layer.rasterHueRotation = nil; XCTAssertTrue(rawLayer->getRasterHueRotate().isUndefined(), @@ -282,13 +277,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterOpacity(), propertyValue, @"Setting rasterOpacity to a camera expression should update raster-opacity."); XCTAssertEqualObjects(layer.rasterOpacity, functionExpression, @"rasterOpacity should round-trip camera expressions."); - layer.rasterOpacity = nil; XCTAssertTrue(rawLayer->getRasterOpacity().isUndefined(), @@ -335,13 +329,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getRasterSaturation(), propertyValue, @"Setting rasterSaturation to a camera expression should update raster-saturation."); XCTAssertEqualObjects(layer.rasterSaturation, functionExpression, @"rasterSaturation should round-trip camera expressions."); - layer.rasterSaturation = nil; XCTAssertTrue(rawLayer->getRasterSaturation().isUndefined(), diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs index f70f0bba23..57e5fce533 100644 --- a/platform/darwin/test/MGLStyleLayerTests.mm.ejs +++ b/platform/darwin/test/MGLStyleLayerTests.mm.ejs @@ -59,7 +59,7 @@ MGLTransition transitionTest = MGLTransitionMake(5, 4); <% for (const property of properties) { -%> -<% if (property.name === 'heatmap-color') continue; -%> +<% if (property['property-type'] === 'color-ramp') continue; -%> // <%- originalPropertyName(property) %> { @@ -69,7 +69,7 @@ NSExpression *constantExpression = [NSExpression expressionWithFormat:<%- objCTestValue(property, type, true, 3) %>]; layer.<%- objCName(property) %> = constantExpression; -<% if (property["property-function"]) { -%> +<% if (isDataDriven(property)) { -%> mbgl::style::DataDrivenPropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; <% } else { -%> mbgl::style::PropertyValue<<%- mbglType(property) %>> propertyValue = { <%- mbglTestValue(property, type) %> }; @@ -88,13 +88,13 @@ { 18, <%- mbglTestValue(property, type) %> }, }}; propertyValue = mbgl::style::CameraFunction<<%- mbglType(property) %>> { intervalStops }; - + XCTAssertEqual(rawLayer->get<%- camelize(originalPropertyName(property)) %>(), propertyValue, @"Setting <%- objCName(property) %> to a camera expression should update <%- originalPropertyName(property) %>."); XCTAssertEqualObjects(layer.<%- objCName(property) %>, functionExpression, @"<%- objCName(property) %> should round-trip camera expressions."); -<% if (property["property-function"] && isInterpolatable(property)) { -%> +<% if (isDataDriven(property) && isInterpolatable(property)) { -%> functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, 'linear', nil, %@)", @{@18: constantExpression}]; layer.<%- objCName(property) %> = functionExpression; @@ -120,7 +120,7 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression, @"<%- objCName(property) %> should round-trip camera-data expressions."); -<% } -%> +<% } -%> <% if (!property.required) { -%> layer.<%- objCName(property) %> = nil; @@ -129,7 +129,7 @@ XCTAssertEqualObjects(layer.<%- objCName(property) %>, defaultExpression, @"<%- objCName(property) %> should return the default value after being unset."); <% } -%> -<% if (!property["property-function"]) { -%> +<% if (!isDataDriven(property)) { -%> functionExpression = [NSExpression expressionForKeyPath:@"bogus"]; XCTAssertThrowsSpecificNamed(layer.<%- objCName(property) %> = functionExpression, NSException, NSInvalidArgumentException, @"MGL<%- camelize(type) %>Layer should raise an exception if a camera-data expression is applied to a property that does not support key paths to feature attributes."); @@ -154,7 +154,7 @@ - (void)testPropertyNames { <% for (const property of properties) { -%> -<% if (property.name === 'heatmap-color') continue; -%> +<% if (property['property-type'] === 'color-ramp') continue; -%> [self testPropertyName:@"<%- property.getter || property.name %>" isBoolean:<%- property.type === 'boolean' ? 'YES' : 'NO' %>]; <% } -%> } diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index cf2f80125a..e1a7ee4e0e 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -71,13 +71,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconAllowOverlap(), propertyValue, @"Setting iconAllowsOverlap to a camera expression should update icon-allow-overlap."); XCTAssertEqualObjects(layer.iconAllowsOverlap, functionExpression, @"iconAllowsOverlap should round-trip camera expressions."); - layer.iconAllowsOverlap = nil; XCTAssertTrue(rawLayer->getIconAllowOverlap().isUndefined(), @@ -115,13 +114,12 @@ { 18, mbgl::style::SymbolAnchorType::BottomRight }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconAnchor(), propertyValue, @"Setting iconAnchor to a camera expression should update icon-anchor."); XCTAssertEqualObjects(layer.iconAnchor, functionExpression, @"iconAnchor should round-trip camera expressions."); - layer.iconAnchor = nil; XCTAssertTrue(rawLayer->getIconAnchor().isUndefined(), @@ -153,13 +151,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconIgnorePlacement(), propertyValue, @"Setting iconIgnoresPlacement to a camera expression should update icon-ignore-placement."); XCTAssertEqualObjects(layer.iconIgnoresPlacement, functionExpression, @"iconIgnoresPlacement should round-trip camera expressions."); - layer.iconIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getIconIgnorePlacement().isUndefined(), @@ -197,13 +194,12 @@ { 18, "Icon Image" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconImage(), propertyValue, @"Setting iconImageName to a camera expression should update icon-image."); XCTAssertEqualObjects(layer.iconImageName, functionExpression, @"iconImageName should round-trip camera expressions."); - layer.iconImageName = nil; XCTAssertTrue(rawLayer->getIconImage().isUndefined(), @@ -241,7 +237,7 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOffset(), propertyValue, @"Setting iconOffset to a camera expression should update icon-offset."); XCTAssertEqualObjects(layer.iconOffset, functionExpression, @@ -272,7 +268,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression, @"iconOffset should round-trip camera-data expressions."); - layer.iconOffset = nil; XCTAssertTrue(rawLayer->getIconOffset().isUndefined(), @@ -304,13 +299,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOptional(), propertyValue, @"Setting iconOptional to a camera expression should update icon-optional."); XCTAssertEqualObjects(layer.iconOptional, functionExpression, @"iconOptional should round-trip camera expressions."); - layer.iconOptional = nil; XCTAssertTrue(rawLayer->getIconOptional().isUndefined(), @@ -348,13 +342,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconPadding(), propertyValue, @"Setting iconPadding to a camera expression should update icon-padding."); XCTAssertEqualObjects(layer.iconPadding, functionExpression, @"iconPadding should round-trip camera expressions."); - layer.iconPadding = nil; XCTAssertTrue(rawLayer->getIconPadding().isUndefined(), @@ -392,13 +385,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, @"Setting iconPitchAlignment to a camera expression should update icon-pitch-alignment."); XCTAssertEqualObjects(layer.iconPitchAlignment, functionExpression, @"iconPitchAlignment should round-trip camera expressions."); - layer.iconPitchAlignment = nil; XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), @@ -436,7 +428,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconRotate(), propertyValue, @"Setting iconRotation to a camera expression should update icon-rotate."); XCTAssertEqualObjects(layer.iconRotation, functionExpression, @@ -467,7 +459,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression, @"iconRotation should round-trip camera-data expressions."); - layer.iconRotation = nil; XCTAssertTrue(rawLayer->getIconRotate().isUndefined(), @@ -499,13 +490,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconRotationAlignment(), propertyValue, @"Setting iconRotationAlignment to a camera expression should update icon-rotation-alignment."); XCTAssertEqualObjects(layer.iconRotationAlignment, functionExpression, @"iconRotationAlignment should round-trip camera expressions."); - layer.iconRotationAlignment = nil; XCTAssertTrue(rawLayer->getIconRotationAlignment().isUndefined(), @@ -543,7 +533,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconSize(), propertyValue, @"Setting iconScale to a camera expression should update icon-size."); XCTAssertEqualObjects(layer.iconScale, functionExpression, @@ -574,7 +564,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression, @"iconScale should round-trip camera-data expressions."); - layer.iconScale = nil; XCTAssertTrue(rawLayer->getIconSize().isUndefined(), @@ -606,13 +595,12 @@ { 18, mbgl::style::IconTextFitType::Both }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTextFit(), propertyValue, @"Setting iconTextFit to a camera expression should update icon-text-fit."); XCTAssertEqualObjects(layer.iconTextFit, functionExpression, @"iconTextFit should round-trip camera expressions."); - layer.iconTextFit = nil; XCTAssertTrue(rawLayer->getIconTextFit().isUndefined(), @@ -656,13 +644,12 @@ { 18, { 1, 1, 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTextFitPadding(), propertyValue, @"Setting iconTextFitPadding to a camera expression should update icon-text-fit-padding."); XCTAssertEqualObjects(layer.iconTextFitPadding, functionExpression, @"iconTextFitPadding should round-trip camera expressions."); - layer.iconTextFitPadding = nil; XCTAssertTrue(rawLayer->getIconTextFitPadding().isUndefined(), @@ -700,13 +687,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconKeepUpright(), propertyValue, @"Setting keepsIconUpright to a camera expression should update icon-keep-upright."); XCTAssertEqualObjects(layer.keepsIconUpright, functionExpression, @"keepsIconUpright should round-trip camera expressions."); - layer.keepsIconUpright = nil; XCTAssertTrue(rawLayer->getIconKeepUpright().isUndefined(), @@ -744,13 +730,12 @@ { 18, false }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextKeepUpright(), propertyValue, @"Setting keepsTextUpright to a camera expression should update text-keep-upright."); XCTAssertEqualObjects(layer.keepsTextUpright, functionExpression, @"keepsTextUpright should round-trip camera expressions."); - layer.keepsTextUpright = nil; XCTAssertTrue(rawLayer->getTextKeepUpright().isUndefined(), @@ -788,13 +773,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextMaxAngle(), propertyValue, @"Setting maximumTextAngle to a camera expression should update text-max-angle."); XCTAssertEqualObjects(layer.maximumTextAngle, functionExpression, @"maximumTextAngle should round-trip camera expressions."); - layer.maximumTextAngle = nil; XCTAssertTrue(rawLayer->getTextMaxAngle().isUndefined(), @@ -832,7 +816,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextMaxWidth(), propertyValue, @"Setting maximumTextWidth to a camera expression should update text-max-width."); XCTAssertEqualObjects(layer.maximumTextWidth, functionExpression, @@ -863,7 +847,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression, @"maximumTextWidth should round-trip camera-data expressions."); - layer.maximumTextWidth = nil; XCTAssertTrue(rawLayer->getTextMaxWidth().isUndefined(), @@ -895,13 +878,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolAvoidEdges(), propertyValue, @"Setting symbolAvoidsEdges to a camera expression should update symbol-avoid-edges."); XCTAssertEqualObjects(layer.symbolAvoidsEdges, functionExpression, @"symbolAvoidsEdges should round-trip camera expressions."); - layer.symbolAvoidsEdges = nil; XCTAssertTrue(rawLayer->getSymbolAvoidEdges().isUndefined(), @@ -939,13 +921,12 @@ { 18, mbgl::style::SymbolPlacementType::Line }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolPlacement(), propertyValue, @"Setting symbolPlacement to a camera expression should update symbol-placement."); XCTAssertEqualObjects(layer.symbolPlacement, functionExpression, @"symbolPlacement should round-trip camera expressions."); - layer.symbolPlacement = nil; XCTAssertTrue(rawLayer->getSymbolPlacement().isUndefined(), @@ -983,13 +964,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getSymbolSpacing(), propertyValue, @"Setting symbolSpacing to a camera expression should update symbol-spacing."); XCTAssertEqualObjects(layer.symbolSpacing, functionExpression, @"symbolSpacing should round-trip camera expressions."); - layer.symbolSpacing = nil; XCTAssertTrue(rawLayer->getSymbolSpacing().isUndefined(), @@ -1027,13 +1007,12 @@ { 18, "Text Field" }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextField(), propertyValue, @"Setting text to a camera expression should update text-field."); XCTAssertEqualObjects(layer.text, functionExpression, @"text should round-trip camera expressions."); - layer.text = nil; XCTAssertTrue(rawLayer->getTextField().isUndefined(), @@ -1065,13 +1044,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextAllowOverlap(), propertyValue, @"Setting textAllowsOverlap to a camera expression should update text-allow-overlap."); XCTAssertEqualObjects(layer.textAllowsOverlap, functionExpression, @"textAllowsOverlap should round-trip camera expressions."); - layer.textAllowsOverlap = nil; XCTAssertTrue(rawLayer->getTextAllowOverlap().isUndefined(), @@ -1109,13 +1087,12 @@ { 18, mbgl::style::SymbolAnchorType::BottomRight }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextAnchor(), propertyValue, @"Setting textAnchor to a camera expression should update text-anchor."); XCTAssertEqualObjects(layer.textAnchor, functionExpression, @"textAnchor should round-trip camera expressions."); - layer.textAnchor = nil; XCTAssertTrue(rawLayer->getTextAnchor().isUndefined(), @@ -1147,13 +1124,12 @@ { 18, { "Text Font", "Tnof Txet" } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextFont(), propertyValue, @"Setting textFontNames to a camera expression should update text-font."); XCTAssertEqualObjects(layer.textFontNames, functionExpression, @"textFontNames should round-trip camera expressions."); - layer.textFontNames = nil; XCTAssertTrue(rawLayer->getTextFont().isUndefined(), @@ -1185,7 +1161,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextSize(), propertyValue, @"Setting textFontSize to a camera expression should update text-size."); XCTAssertEqualObjects(layer.textFontSize, functionExpression, @@ -1216,7 +1192,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression, @"textFontSize should round-trip camera-data expressions."); - layer.textFontSize = nil; XCTAssertTrue(rawLayer->getTextSize().isUndefined(), @@ -1248,13 +1223,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextIgnorePlacement(), propertyValue, @"Setting textIgnoresPlacement to a camera expression should update text-ignore-placement."); XCTAssertEqualObjects(layer.textIgnoresPlacement, functionExpression, @"textIgnoresPlacement should round-trip camera expressions."); - layer.textIgnoresPlacement = nil; XCTAssertTrue(rawLayer->getTextIgnorePlacement().isUndefined(), @@ -1292,13 +1266,12 @@ { 18, mbgl::style::TextJustifyType::Right }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextJustify(), propertyValue, @"Setting textJustification to a camera expression should update text-justify."); XCTAssertEqualObjects(layer.textJustification, functionExpression, @"textJustification should round-trip camera expressions."); - layer.textJustification = nil; XCTAssertTrue(rawLayer->getTextJustify().isUndefined(), @@ -1330,7 +1303,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextLetterSpacing(), propertyValue, @"Setting textLetterSpacing to a camera expression should update text-letter-spacing."); XCTAssertEqualObjects(layer.textLetterSpacing, functionExpression, @@ -1361,7 +1334,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression, @"textLetterSpacing should round-trip camera-data expressions."); - layer.textLetterSpacing = nil; XCTAssertTrue(rawLayer->getTextLetterSpacing().isUndefined(), @@ -1393,13 +1365,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextLineHeight(), propertyValue, @"Setting textLineHeight to a camera expression should update text-line-height."); XCTAssertEqualObjects(layer.textLineHeight, functionExpression, @"textLineHeight should round-trip camera expressions."); - layer.textLineHeight = nil; XCTAssertTrue(rawLayer->getTextLineHeight().isUndefined(), @@ -1443,7 +1414,7 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOffset(), propertyValue, @"Setting textOffset to a camera expression should update text-offset."); XCTAssertEqualObjects(layer.textOffset, functionExpression, @@ -1474,7 +1445,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression, @"textOffset should round-trip camera-data expressions."); - layer.textOffset = nil; XCTAssertTrue(rawLayer->getTextOffset().isUndefined(), @@ -1506,13 +1476,12 @@ { 18, true }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOptional(), propertyValue, @"Setting textOptional to a camera expression should update text-optional."); XCTAssertEqualObjects(layer.textOptional, functionExpression, @"textOptional should round-trip camera expressions."); - layer.textOptional = nil; XCTAssertTrue(rawLayer->getTextOptional().isUndefined(), @@ -1550,13 +1519,12 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextPadding(), propertyValue, @"Setting textPadding to a camera expression should update text-padding."); XCTAssertEqualObjects(layer.textPadding, functionExpression, @"textPadding should round-trip camera expressions."); - layer.textPadding = nil; XCTAssertTrue(rawLayer->getTextPadding().isUndefined(), @@ -1594,13 +1562,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextPitchAlignment(), propertyValue, @"Setting textPitchAlignment to a camera expression should update text-pitch-alignment."); XCTAssertEqualObjects(layer.textPitchAlignment, functionExpression, @"textPitchAlignment should round-trip camera expressions."); - layer.textPitchAlignment = nil; XCTAssertTrue(rawLayer->getTextPitchAlignment().isUndefined(), @@ -1638,7 +1605,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextRotate(), propertyValue, @"Setting textRotation to a camera expression should update text-rotate."); XCTAssertEqualObjects(layer.textRotation, functionExpression, @@ -1669,7 +1636,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression, @"textRotation should round-trip camera-data expressions."); - layer.textRotation = nil; XCTAssertTrue(rawLayer->getTextRotate().isUndefined(), @@ -1701,13 +1667,12 @@ { 18, mbgl::style::AlignmentType::Auto }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextRotationAlignment(), propertyValue, @"Setting textRotationAlignment to a camera expression should update text-rotation-alignment."); XCTAssertEqualObjects(layer.textRotationAlignment, functionExpression, @"textRotationAlignment should round-trip camera expressions."); - layer.textRotationAlignment = nil; XCTAssertTrue(rawLayer->getTextRotationAlignment().isUndefined(), @@ -1745,13 +1710,12 @@ { 18, mbgl::style::TextTransformType::Lowercase }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTransform(), propertyValue, @"Setting textTransform to a camera expression should update text-transform."); XCTAssertEqualObjects(layer.textTransform, functionExpression, @"textTransform should round-trip camera expressions."); - layer.textTransform = nil; XCTAssertTrue(rawLayer->getTextTransform().isUndefined(), @@ -1783,7 +1747,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, @"Setting iconColor to a camera expression should update icon-color."); XCTAssertEqualObjects(layer.iconColor, functionExpression, @@ -1814,7 +1778,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression, @"iconColor should round-trip camera-data expressions."); - layer.iconColor = nil; XCTAssertTrue(rawLayer->getIconColor().isUndefined(), @@ -1855,7 +1818,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, @"Setting iconHaloBlur to a camera expression should update icon-halo-blur."); XCTAssertEqualObjects(layer.iconHaloBlur, functionExpression, @@ -1886,7 +1849,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression, @"iconHaloBlur should round-trip camera-data expressions."); - layer.iconHaloBlur = nil; XCTAssertTrue(rawLayer->getIconHaloBlur().isUndefined(), @@ -1927,7 +1889,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, @"Setting iconHaloColor to a camera expression should update icon-halo-color."); XCTAssertEqualObjects(layer.iconHaloColor, functionExpression, @@ -1958,7 +1920,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression, @"iconHaloColor should round-trip camera-data expressions."); - layer.iconHaloColor = nil; XCTAssertTrue(rawLayer->getIconHaloColor().isUndefined(), @@ -1999,7 +1960,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, @"Setting iconHaloWidth to a camera expression should update icon-halo-width."); XCTAssertEqualObjects(layer.iconHaloWidth, functionExpression, @@ -2030,7 +1991,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression, @"iconHaloWidth should round-trip camera-data expressions."); - layer.iconHaloWidth = nil; XCTAssertTrue(rawLayer->getIconHaloWidth().isUndefined(), @@ -2071,7 +2031,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, @"Setting iconOpacity to a camera expression should update icon-opacity."); XCTAssertEqualObjects(layer.iconOpacity, functionExpression, @@ -2102,7 +2062,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression, @"iconOpacity should round-trip camera-data expressions."); - layer.iconOpacity = nil; XCTAssertTrue(rawLayer->getIconOpacity().isUndefined(), @@ -2149,13 +2108,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTranslate(), propertyValue, @"Setting iconTranslation to a camera expression should update icon-translate."); XCTAssertEqualObjects(layer.iconTranslation, functionExpression, @"iconTranslation should round-trip camera expressions."); - layer.iconTranslation = nil; XCTAssertTrue(rawLayer->getIconTranslate().isUndefined(), @@ -2193,13 +2151,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getIconTranslateAnchor(), propertyValue, @"Setting iconTranslationAnchor to a camera expression should update icon-translate-anchor."); XCTAssertEqualObjects(layer.iconTranslationAnchor, functionExpression, @"iconTranslationAnchor should round-trip camera expressions."); - layer.iconTranslationAnchor = nil; XCTAssertTrue(rawLayer->getIconTranslateAnchor().isUndefined(), @@ -2237,7 +2194,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, @"Setting textColor to a camera expression should update text-color."); XCTAssertEqualObjects(layer.textColor, functionExpression, @@ -2268,7 +2225,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression, @"textColor should round-trip camera-data expressions."); - layer.textColor = nil; XCTAssertTrue(rawLayer->getTextColor().isUndefined(), @@ -2309,7 +2265,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, @"Setting textHaloBlur to a camera expression should update text-halo-blur."); XCTAssertEqualObjects(layer.textHaloBlur, functionExpression, @@ -2340,7 +2296,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression, @"textHaloBlur should round-trip camera-data expressions."); - layer.textHaloBlur = nil; XCTAssertTrue(rawLayer->getTextHaloBlur().isUndefined(), @@ -2381,7 +2336,7 @@ { 18, { 1, 0, 0, 1 } }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, @"Setting textHaloColor to a camera expression should update text-halo-color."); XCTAssertEqualObjects(layer.textHaloColor, functionExpression, @@ -2412,7 +2367,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression, @"textHaloColor should round-trip camera-data expressions."); - layer.textHaloColor = nil; XCTAssertTrue(rawLayer->getTextHaloColor().isUndefined(), @@ -2453,7 +2407,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, @"Setting textHaloWidth to a camera expression should update text-halo-width."); XCTAssertEqualObjects(layer.textHaloWidth, functionExpression, @@ -2484,7 +2438,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression, @"textHaloWidth should round-trip camera-data expressions."); - layer.textHaloWidth = nil; XCTAssertTrue(rawLayer->getTextHaloWidth().isUndefined(), @@ -2525,7 +2478,7 @@ { 18, 0xff }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, @"Setting textOpacity to a camera expression should update text-opacity."); XCTAssertEqualObjects(layer.textOpacity, functionExpression, @@ -2556,7 +2509,6 @@ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}]; XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression, @"textOpacity should round-trip camera-data expressions."); - layer.textOpacity = nil; XCTAssertTrue(rawLayer->getTextOpacity().isUndefined(), @@ -2603,13 +2555,12 @@ { 18, { 1, 1 } }, }}; propertyValue = mbgl::style::CameraFunction> { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTranslate(), propertyValue, @"Setting textTranslation to a camera expression should update text-translate."); XCTAssertEqualObjects(layer.textTranslation, functionExpression, @"textTranslation should round-trip camera expressions."); - layer.textTranslation = nil; XCTAssertTrue(rawLayer->getTextTranslate().isUndefined(), @@ -2647,13 +2598,12 @@ { 18, mbgl::style::TranslateAnchorType::Viewport }, }}; propertyValue = mbgl::style::CameraFunction { intervalStops }; - + XCTAssertEqual(rawLayer->getTextTranslateAnchor(), propertyValue, @"Setting textTranslationAnchor to a camera expression should update text-translate-anchor."); XCTAssertEqualObjects(layer.textTranslationAnchor, functionExpression, @"textTranslationAnchor should round-trip camera expressions."); - layer.textTranslationAnchor = nil; XCTAssertTrue(rawLayer->getTextTranslateAnchor().isUndefined(), diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index a1d4d68511..47cb07c85a 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -22,6 +22,9 @@ "query-tests/geometry/polygon": "needs investigation", "query-tests/world-wrapping/box": "skip - needs issue", "query-tests/world-wrapping/point": "skip - needs issue", + "query-tests/circle-radius/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "query-tests/feature-state/default": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "query-tests/regressions/mapbox-gl-js#6555": "skip - no querySourceFeatures in mbgl-node; needs issue", "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841", "render-tests/debug/collision-lines": "https://github.com/mapbox/mapbox-gl-native/issues/10412", @@ -106,5 +109,8 @@ "render-tests/combinations/fill-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/line-translucent--fill-extrusion-translucent": "needs investigation", "render-tests/combinations/raster-translucent--fill-extrusion-translucent": "needs investigation", - "render-tests/combinations/symbol-translucent--fill-extrusion-translucent": "needs investigation" + "render-tests/combinations/symbol-translucent--fill-extrusion-translucent": "needs investigation", + "render-tests/feature-state/composite-expression": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "render-tests/feature-state/data-expression": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", + "render-tests/feature-state/vector-source": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue" } diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index 6ddb787f19..0059863e05 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -15,10 +15,6 @@ function parseCSSColor(str) { ]; } -global.isDataDriven = function (property) { - return property['property-function'] === true; -}; - global.isLightProperty = function (property) { return property['light-property'] === true; }; @@ -77,30 +73,36 @@ function attributeUniformType(property, type) { } global.layoutPropertyType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenLayoutProperty<${evaluatedType(property)}>`; - } else { - return `LayoutProperty<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenLayoutProperty<${evaluatedType(property)}>`; + default: + return `LayoutProperty<${evaluatedType(property)}>`; } }; global.paintPropertyType = function (property, type) { - if (isDataDriven(property)) { - return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; - } else if (/-pattern$/.test(property.name) || property.name === 'line-dasharray') { - return `CrossFadedPaintProperty<${evaluatedType(property)}>`; - } else { - return `PaintProperty<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPaintProperty<${evaluatedType(property)}, ${attributeUniformType(property, type)}>`; + case 'cross-faded': + return `CrossFadedPaintProperty<${evaluatedType(property)}>`; + default: + return `PaintProperty<${evaluatedType(property)}>`; } }; global.propertyValueType = function (property) { - if (isDataDriven(property)) { - return `DataDrivenPropertyValue<${evaluatedType(property)}>`; - } else if (property.name === 'heatmap-color') { - return `HeatmapColorPropertyValue`; - } else { - return `PropertyValue<${evaluatedType(property)}>`; + switch (property['property-type']) { + case 'data-driven': + case 'cross-faded-data-driven': + return `DataDrivenPropertyValue<${evaluatedType(property)}>`; + case 'color-ramp': + return `HeatmapColorPropertyValue`; + default: + return `PropertyValue<${evaluatedType(property)}>`; } }; diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp index 9d5e172888..709624a50f 100644 --- a/test/style/expression/expression.test.cpp +++ b/test/style/expression/expression.test.cpp @@ -24,12 +24,12 @@ TEST(Expression, IsExpression) { spec["expression_name"].IsObject() && spec["expression_name"].HasMember("values") && spec["expression_name"]["values"].IsObject()); - + const auto& allExpressions = spec["expression_name"]["values"]; - + for(auto& entry : allExpressions.GetObject()) { const std::string name { entry.name.GetString(), entry.name.GetStringLength() }; - if (name == "collator" || name == "line-progress" || name == "resolved-locale") { + if (name == "collator" || name == "line-progress" || name == "resolved-locale" || name == "feature-state") { // Not yet implemented continue; } -- cgit v1.2.1 From 4b1530ff422d9ba77ddb9ef34d64c2e04f356380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Mon, 14 May 2018 12:18:13 -0700 Subject: =?UTF-8?q?[core]=20Convert=20null=20to=20empty=20string,=20not=20?= =?UTF-8?q?=E2=80=9Cnull=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mapbox-gl-js | 2 +- platform/android/CHANGELOG.md | 3 +++ platform/ios/CHANGELOG.md | 2 +- platform/macos/CHANGELOG.md | 1 + src/mbgl/style/expression/compound_expression.cpp | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 6b96d69ab5..cc3bb6afac 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 6b96d69ab54b149db1f6ef06daed37379ac07447 +Subproject commit cc3bb6afac758258a559e6ab3f7c22d99f41b3bb diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 384dcdf883..6da01aacf3 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## 6.1.2 + - `"to-string"` expression operator converts `null` to empty string rather than to `"null"` [#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904) + ## 6.1.1 - May 7, 2018 - Update telemetry to 3.1.0 [#11855](https://github.com/mapbox/mapbox-gl-native/pull/11855) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 7bf8f17389..99da024ee1 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -12,7 +12,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) * Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) - +* Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) ### Annotations diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 1ae0212b97..89ef484ca6 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -7,6 +7,7 @@ * Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) * Added `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) * Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) +* Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) ### Other changes diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 3bd8a836df..4739d50168 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -303,6 +303,7 @@ std::unordered_map initiali define("to-string", [](const Value& value) -> Result { return value.match( + [](const NullValue&) -> Result { return std::string(); }, [](const Color& c) -> Result { return c.stringify(); }, // avoid quoting [](const std::string& s) -> Result { return s; }, // avoid quoting [](const auto& v) -> Result { return stringify(v); } -- cgit v1.2.1 From baf5fd7d01c3618e415389c9dca05886e00ff307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Mon, 14 May 2018 13:38:32 -0700 Subject: [ios, macos] Fixed English priority during label localization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Respect English in the Preferred Languages setting even if other Mapbox Streets source–supported languages are listed too. --- platform/darwin/src/MGLVectorTileSource.mm | 23 +++-------------------- platform/darwin/src/MGLVectorTileSource_Private.h | 2 -- platform/darwin/test/MGLStyleTests.mm | 4 ++++ platform/ios/CHANGELOG.md | 1 + platform/macos/CHANGELOG.md | 1 + 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm index 96629997d6..6b9d857ad2 100644 --- a/platform/darwin/src/MGLVectorTileSource.mm +++ b/platform/darwin/src/MGLVectorTileSource.mm @@ -112,7 +112,8 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ return [[NSLocale localeWithLocaleIdentifier:language].languageCode isEqualToString:@"en"]; }]].count; - NSArray *preferredLanguages = [NSBundle preferredLocalizationsFromArray:MGLMapboxStreetsAlternativeLanguages + NSArray *availableLanguages = acceptsEnglish ? MGLMapboxStreetsLanguages : MGLMapboxStreetsAlternativeLanguages; + NSArray *preferredLanguages = [NSBundle preferredLocalizationsFromArray:availableLanguages forPreferences:preferencesArray]; NSString *mostSpecificLanguage; for (NSString *language in preferredLanguages) { @@ -120,10 +121,7 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ mostSpecificLanguage = language; } } - if ([mostSpecificLanguage isEqualToString:@"mul"]) { - return acceptsEnglish ? @"en" : nil; - } - return mostSpecificLanguage; + return [mostSpecificLanguage isEqualToString:@"mul"] ? nil : mostSpecificLanguage; } - (BOOL)isMapboxStreets { @@ -135,19 +133,4 @@ static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[ return [identifiers containsObject:@"mapbox.mapbox-streets-v7"] || [identifiers containsObject:@"mapbox.mapbox-streets-v6"]; } -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage { - if (!self.mapboxStreets) { - return @{}; - } - - // Replace {name} and {name_*} with the matching localized name tag. - NSString *localizedKey = preferredLanguage ? [NSString stringWithFormat:@"name_%@", preferredLanguage] : @"name"; - NSMutableDictionary *localizedKeysByKey = [NSMutableDictionary dictionaryWithObject:localizedKey forKey:@"name"]; - for (NSString *languageCode in [MGLVectorTileSource mapboxStreetsLanguages]) { - NSString *key = [NSString stringWithFormat:@"name_%@", languageCode]; - localizedKeysByKey[key] = localizedKey; - } - return localizedKeysByKey; -} - @end diff --git a/platform/darwin/src/MGLVectorTileSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h index 77521869f1..109f66a432 100644 --- a/platform/darwin/src/MGLVectorTileSource_Private.h +++ b/platform/darwin/src/MGLVectorTileSource_Private.h @@ -11,8 +11,6 @@ NS_ASSUME_NONNULL_BEGIN + (nullable NSString *)preferredMapboxStreetsLanguage; + (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray *)preferencesArray; -- (NS_DICTIONARY_OF(NSString *, NSString *) *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage; - @end NS_ASSUME_NONNULL_END diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 6048f39ea3..32243c1bec 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -447,6 +447,10 @@ NSArray *preferences = @[@"zh-Hant"]; XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]); } + { + NSArray *preferences = @[@"en", @"fr", @"el"]; + XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en"); + } { NSArray *preferences = @[@"tlh"]; XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 99da024ee1..136b7634b7 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -23,6 +23,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes * Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) +* If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 89ef484ca6..4acff874b5 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -13,6 +13,7 @@ * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) +* If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) * Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) -- cgit v1.2.1 From 8383ef4eaeb601ca2280d36f5ecfe0f35d0d013e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Mon, 14 May 2018 12:18:13 -0700 Subject: [ios] Copyedit changelog for ios-v4.0.1 --- platform/ios/CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 136b7634b7..400e220473 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -2,30 +2,30 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started. -## 4.0.1 +## 4.0.1 - May 14, 2018 ### Packaging * Re-added support for 32-bit simulators (i386) to work around an issue in CocoaPods. ([#11891](https://github.com/mapbox/mapbox-gl-native/pull/11891)) +* Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) ### Style layers -* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) * Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) * Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) ### Annotations * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) -* Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` is `YES`. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) +* Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` was enabled. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) * Fixed `MGLAnnotationView.rotatesToMatchCamera` overriding other transforms that might be applied to annotation views that had this property enabled. ([#11842](https://github.com/mapbox/mapbox-gl-native/pull/11842)) ### Other changes -* Added a Korean localization. ([#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)) * If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) -* Reduce per-frame render CPU time ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) +* Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) * Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) -- cgit v1.2.1 From 76b66b14096ff1baf99c705fea940a516b7bcd85 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 14 May 2018 15:30:44 -0400 Subject: Add v3.7.7 & v3.7.8 sections to changelog --- platform/ios/CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 400e220473..84b4c2e0c6 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -30,6 +30,14 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) * Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) +## 3.7.8 - May 7, 2018 + +* Improved compatibility with Mapbox China APIs. ([#11845](https://github.com/mapbox/mapbox-gl-native/pull/11845)) + +## 3.7.7 - May 3, 2018 + +* Fixed a crash when removing an `MGLOfflinePack`. ([#11821](https://github.com/mapbox/mapbox-gl-native/issues/11821)) + ## 4.0.0 - April 19, 2018 The 4.0._x_ series of releases will be the last to support iOS 8. The minimum iOS deployment version will increase to iOS 9.0 in a future release. -- cgit v1.2.1 From d8976456003c2924e15b08888a08435630ab10d6 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 14 May 2018 15:31:26 -0400 Subject: [ios] Prepare ios-v4.0.1 release --- platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 7ea1b993ef..83b7bc51f9 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0' + version = '4.0.1' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 47a021598f..05694e5b6f 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0' + version = '4.0.1' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 73412890c8..3fa57839b6 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.0' + version = '4.0.1' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From 8a55c2cc816f3645a0c79f9fe2576873aa75ac99 Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 14 May 2018 15:44:57 -0400 Subject: Update macOS changelog --- platform/macos/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 4acff874b5..6e0e916082 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -4,14 +4,15 @@ ### Style layers -* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) * Added `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) -* Added `FISRT`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) +* Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) * Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) ### Other changes * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) -- cgit v1.2.1 From c0191535eba64813baff7141a33343c9893360cc Mon Sep 17 00:00:00 2001 From: Jason Wray Date: Mon, 14 May 2018 15:45:22 -0400 Subject: Remove changelog entry for crash fixed in v3.7.7 --- platform/ios/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 84b4c2e0c6..be17b7c20d 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -26,7 +26,6 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) -* Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) * Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) -- cgit v1.2.1 From 0d5a33df677018bc66a40e6fc1f0bf9752d98f52 Mon Sep 17 00:00:00 2001 From: Jordan Kiley Date: Tue, 15 May 2018 10:48:21 -0700 Subject: [ios, macos] Moved numbers away from start of lines in documentation (#11881) --- platform/darwin/scripts/generate-style-code.js | 2 +- platform/darwin/src/MGLBackgroundStyleLayer.h | 2 +- platform/darwin/src/MGLCircleStyleLayer.h | 10 +++---- platform/darwin/src/MGLFillExtrusionStyleLayer.h | 6 ++--- platform/darwin/src/MGLFillStyleLayer.h | 2 +- platform/darwin/src/MGLHeatmapStyleLayer.h | 8 +++--- platform/darwin/src/MGLHillshadeStyleLayer.h | 4 +-- platform/darwin/src/MGLLight.h | 2 +- platform/darwin/src/MGLLineStyleLayer.h | 14 +++++----- platform/darwin/src/MGLRasterStyleLayer.h | 14 +++++----- platform/darwin/src/MGLSymbolStyleLayer.h | 34 ++++++++++++------------ 11 files changed, 49 insertions(+), 49 deletions(-) diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.js index c7b54b326a..e4e4d3dcc1 100755 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.js @@ -403,7 +403,7 @@ global.describeValue = function (value, property, layerType) { case 'boolean': return value ? '`YES`' : '`NO`'; case 'number': - return 'the float ' + formatNumber(value); + return 'the float ' + '`' + formatNumber(value) + '`'; case 'string': if (value === '') { return 'the empty string'; diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h index d5c3ed5403..31755c8bad 100644 --- a/platform/darwin/src/MGLBackgroundStyleLayer.h +++ b/platform/darwin/src/MGLBackgroundStyleLayer.h @@ -96,7 +96,7 @@ which it is added. The opacity at which the background will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index 06b4de32f0..69b6e41c9c 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -117,7 +117,7 @@ MGL_EXPORT full opacity. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -186,7 +186,7 @@ MGL_EXPORT The opacity at which the circle will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -235,7 +235,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 5. Set this property to `nil` to reset it to the default value. + `5`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -334,7 +334,7 @@ MGL_EXPORT The opacity of the circle's stroke. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -361,7 +361,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h index 7c3a0773e4..bca2a99f1e 100644 --- a/platform/darwin/src/MGLFillExtrusionStyleLayer.h +++ b/platform/darwin/src/MGLFillExtrusionStyleLayer.h @@ -78,7 +78,7 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `fillExtrusionHeight` is non-`nil`. Otherwise, it is ignored. @@ -164,7 +164,7 @@ MGL_EXPORT This property is measured in meters. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -189,7 +189,7 @@ MGL_EXPORT per-layer, not per-feature, basis, and data-driven styling is not available. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLFillStyleLayer.h b/platform/darwin/src/MGLFillStyleLayer.h index a159a924e6..ea19cf13cc 100644 --- a/platform/darwin/src/MGLFillStyleLayer.h +++ b/platform/darwin/src/MGLFillStyleLayer.h @@ -151,7 +151,7 @@ MGL_EXPORT value will also affect the 1pt stroke around the fill, if the stroke is used. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h index ad7ba5de01..167c5bafbe 100644 --- a/platform/darwin/src/MGLHeatmapStyleLayer.h +++ b/platform/darwin/src/MGLHeatmapStyleLayer.h @@ -116,7 +116,7 @@ MGL_EXPORT Primarily used for adjusting the heatmap based on zoom level. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -142,7 +142,7 @@ MGL_EXPORT The global opacity at which the heatmap layer will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -171,7 +171,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 30. Set this property to `nil` to reset it to the default value. + `30`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -197,7 +197,7 @@ MGL_EXPORT Especially useful when combined with clustering. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLHillshadeStyleLayer.h b/platform/darwin/src/MGLHillshadeStyleLayer.h index 224680160a..45b0e66751 100644 --- a/platform/darwin/src/MGLHillshadeStyleLayer.h +++ b/platform/darwin/src/MGLHillshadeStyleLayer.h @@ -130,7 +130,7 @@ MGL_EXPORT Intensity of the hillshade The default value of this property is an expression that evaluates to the float - 0.5. Set this property to `nil` to reset it to the default value. + `0.5`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -232,7 +232,7 @@ MGL_EXPORT `hillshadeIlluminationAnchor` is set to `MGLHillshadeIlluminationAnchorMap`. The default value of this property is an expression that evaluates to the float - 335. Set this property to `nil` to reset it to the default value. + `335`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLLight.h b/platform/darwin/src/MGLLight.h index cf4aa50112..13c925d9bd 100644 --- a/platform/darwin/src/MGLLight.h +++ b/platform/darwin/src/MGLLight.h @@ -188,7 +188,7 @@ MGL_EXPORT more extreme contrast. The default value of this property is an expression that evaluates to the float - 0.5. + `0.5`. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLLineStyleLayer.h b/platform/darwin/src/MGLLineStyleLayer.h index a7510142fc..9620ec438e 100644 --- a/platform/darwin/src/MGLLineStyleLayer.h +++ b/platform/darwin/src/MGLLineStyleLayer.h @@ -180,7 +180,7 @@ MGL_EXPORT Used to automatically convert miter joins to bevel joins for sharp angles. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `miter`. Otherwise, it is ignored. @@ -202,7 +202,7 @@ MGL_EXPORT Used to automatically convert round joins to miter joins for shallow angles. The default value of this property is an expression that evaluates to the float - 1.05. Set this property to `nil` to reset it to the default value. + `1.05`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `lineJoin` is set to an expression that evaluates to `round`. Otherwise, it is ignored. @@ -228,7 +228,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -343,7 +343,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -372,7 +372,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -396,7 +396,7 @@ MGL_EXPORT The opacity at which the line will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -542,7 +542,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h index bca9649e5d..ff055d24f6 100644 --- a/platform/darwin/src/MGLRasterStyleLayer.h +++ b/platform/darwin/src/MGLRasterStyleLayer.h @@ -64,7 +64,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-brightness-max @@ -97,7 +97,7 @@ MGL_EXPORT brightness. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-brightness-min @@ -129,7 +129,7 @@ MGL_EXPORT Increase or reduce the contrast of the image. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -157,7 +157,7 @@ MGL_EXPORT This property is measured in milliseconds. The default value of this property is an expression that evaluates to the float - 300. Set this property to `nil` to reset it to the default value. + `300`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -178,7 +178,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This attribute corresponds to the raster-hue-rotate @@ -210,7 +210,7 @@ MGL_EXPORT The opacity at which the image will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: @@ -236,7 +236,7 @@ MGL_EXPORT Increase or reduce the saturation of the image. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. You can set this property to an expression containing any of the following: diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index e27f039b75..2c899fe76f 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -564,7 +564,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -615,7 +615,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -678,7 +678,7 @@ MGL_EXPORT This property is measured in factor of the original icon sizes. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -858,7 +858,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 45. Set this property to `nil` to reset it to the default value. + `45`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`, and `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, @@ -890,7 +890,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 10. Set this property to `nil` to reset it to the default value. + `10`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -973,7 +973,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 250. Set this property to `nil` to reset it to the default value. + `250`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `symbolPlacement` is set to an expression that evaluates to `line`. Otherwise, it is ignored. @@ -1129,7 +1129,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 16. Set this property to `nil` to reset it to the default value. + `16`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1219,7 +1219,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1241,7 +1241,7 @@ MGL_EXPORT This property is measured in ems. The default value of this property is an expression that evaluates to the float - 1.2. Set this property to `nil` to reset it to the default value. + `1.2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1338,7 +1338,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 2. Set this property to `nil` to reset it to the default value. + `2`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1389,7 +1389,7 @@ MGL_EXPORT This property is measured in degrees. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1529,7 +1529,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1611,7 +1611,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1638,7 +1638,7 @@ MGL_EXPORT The opacity at which the icon will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `iconImageName` is non-`nil`. Otherwise, it is ignored. @@ -1819,7 +1819,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1900,7 +1900,7 @@ MGL_EXPORT This property is measured in points. The default value of this property is an expression that evaluates to the float - 0. Set this property to `nil` to reset it to the default value. + `0`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. @@ -1927,7 +1927,7 @@ MGL_EXPORT The opacity at which the text will be drawn. The default value of this property is an expression that evaluates to the float - 1. Set this property to `nil` to reset it to the default value. + `1`. Set this property to `nil` to reset it to the default value. This property is only applied to the style if `text` is non-`nil`. Otherwise, it is ignored. -- cgit v1.2.1 From 87e3c2571ab76e1f94f8ffe90ce41fa641978647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 15 May 2018 12:47:22 -0700 Subject: [ios, macos] Copyedited changelogs --- platform/ios/CHANGELOG.md | 5 +++-- platform/macos/CHANGELOG.md | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index be17b7c20d..35df4cb98d 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -12,22 +12,23 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) +* Added an `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) * Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) * Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) ### Annotations * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) * Fixed an issue where annotation views could become distorted if `rotatesToMatchCamera` was enabled. ([#11817](https://github.com/mapbox/mapbox-gl-native/pull/11817)) * Fixed `MGLAnnotationView.rotatesToMatchCamera` overriding other transforms that might be applied to annotation views that had this property enabled. ([#11842](https://github.com/mapbox/mapbox-gl-native/pull/11842)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) ### Other changes * If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) * Fixed an issue where `-[MGLMapView metersPerPixelAtLatitude:]` was removed, but not marked as unavailable. ([#11765](https://github.com/mapbox/mapbox-gl-native/pull/11765)) * Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) -* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) -* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) ## 3.7.8 - May 7, 2018 diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 6e0e916082..6c01d7add5 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -5,18 +5,21 @@ ### Style layers * Deprecated `+[NSExpression featurePropertiesVariableExpression]`; use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748)) -* Added `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) +* Added an `-[NSPredicate(MGLAdditions) predicateWithMGLJSONObject:]` method and `NSPredicate.mgl_jsonExpressionObject` property. ([#11810](https://github.com/mapbox/mapbox-gl-native/pull/11810)) * Added `FIRST`, `LAST`, and `SIZE` symbolic array subscripting support to expressions. ([#11770](https://github.com/mapbox/mapbox-gl-native/pull/11770)) * Inside an expression, casting `nil` to a string turns it into the empty string instead of the string `"null"`. ([#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904)) +* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) -### Other changes +### Annotations * Fixed an issue where selecting an onscreen annotation could move the map unintentionally. ([#11731](https://github.com/mapbox/mapbox-gl-native/pull/11731)) +* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) + +### Other changes + * Reduced per-frame render CPU time. ([#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)) * Fixed a crash when removing an `MGLOfflinePack`. ([#6092](https://github.com/mapbox/mapbox-gl-native/issues/6092)) * If English is the first language listed in the user’s Preferred Languages setting, `-[MGLStyle localizeLabelsIntoLocale:]` no longer prioritizes other languages over English. ([#11907](https://github.com/mapbox/mapbox-gl-native/pull/11907)) -* Fixed an issue where an `MGLOverlay` object straddling the antimeridian had an empty `MGLOverlay.overlayBounds` value. ([#11783](https://github.com/mapbox/mapbox-gl-native/pull/11783)) -* Fixed an issue where certain colors were being misrepresented in `NSExpression` obtained from `MGLStyleLayer` getters. ([#11725](https://github.com/mapbox/mapbox-gl-native/pull/11725)) ## 0.7.0 - April 19, 2018 -- cgit v1.2.1 From 146057adf90e85e3edc80446f02d20e5f6cab378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Tue, 15 May 2018 12:47:38 -0700 Subject: macos-v0.7.1 --- platform/macos/Mapbox-macOS-SDK-symbols.podspec | 2 +- platform/macos/Mapbox-macOS-SDK.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec index 597f97d03c..48e43c63bb 100644 --- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec +++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.7.0' + version = '0.7.1' m.name = 'Mapbox-macOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec index 734ab19390..27151ae69d 100644 --- a/platform/macos/Mapbox-macOS-SDK.podspec +++ b/platform/macos/Mapbox-macOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '0.7.0' + version = '0.7.1' m.name = 'Mapbox-macOS-SDK' m.version = version -- cgit v1.2.1 From 5af104bfa5bd7ebd55b05843a2d77d1df06ec93f Mon Sep 17 00:00:00 2001 From: Osana Babayan <32496536+osana@users.noreply.github.com> Date: Wed, 16 May 2018 08:18:21 -0400 Subject: bumped mapbox-java to 3.1.0 (#11916) --- platform/android/gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index 07a9938915..d50f6cef08 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -8,7 +8,7 @@ ext { ] versions = [ - mapboxServices : '3.0.1', + mapboxServices : '3.1.0', mapboxTelemetry: '3.1.0', mapboxGestures : '0.2.0', supportLib : '25.4.0', -- cgit v1.2.1 From d05fa404a8b2d67c908c857cbebacd38f594052a Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Wed, 16 May 2018 10:41:48 -0700 Subject: Fix bug (always true + incorrect calculation) in fill_extrusion_bucket edgedistance check --- src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp index 5e2c937091..4fcc761280 100644 --- a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -84,7 +84,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, if (nVertices == 0) continue; - auto edgeDistance = 0; + std::size_t edgeDistance = 0; for (uint32_t i = 0; i < nVertices; i++) { const auto& p1 = ring[i]; @@ -102,7 +102,7 @@ void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, const Point perp = util::unit(util::perp(d1 - d2)); const auto dist = util::dist(d1, d2); - if (dist > std::numeric_limits::max()) { + if (edgeDistance + dist > std::numeric_limits::max()) { edgeDistance = 0; } -- cgit v1.2.1 From 7d1e52a3255d4eecdcd37e4fb600eb76fa9333f8 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Thu, 17 May 2018 17:16:49 +0300 Subject: [build] Use Qt 5.6.3 instead of 5.7 in AppVeyor --- appveyor.yml | 2 +- platform/qt/qt.cmake | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d31160261f..eb53ad58bc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,7 +36,7 @@ before_build: build_script: - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 - - cmake -G "Ninja" -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\5.7\msvc2015_64\lib\cmake -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" .. + - cmake -G "Ninja" -DMBGL_PLATFORM=qt -DWITH_QT_DECODERS=ON -DWITH_QT_I18N=ON -DWITH_NODEJS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\5.6.3\msvc2015_64\lib\cmake -DCMAKE_MAKE_PROGRAM="%APPVEYOR_BUILD_FOLDER%\platform\qt\ninja.exe" .. - cmake --build . -- -j %NUMBER_OF_PROCESSORS% after_build: diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index d1d597bda6..989618a59a 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -145,6 +145,7 @@ elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") add_definitions("-Wno-unused-command-line-argument") add_definitions("-Wno-unused-local-typedef") add_definitions("-Wno-unused-private-field") + add_definitions("-Wno-inconsistent-missing-override") list(APPEND MBGL_QT_CORE_FILES PRIVATE platform/qt/src/thread.cpp -- cgit v1.2.1 From 4f2002b856346a2de4673b94a140c9b2677cefae Mon Sep 17 00:00:00 2001 From: tobrun Date: Wed, 16 May 2018 16:24:18 +0200 Subject: [android] - expose MapView#setOfflineRegionDefinition --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 39 +++++++++++++++++++--- .../activity/offline/UpdateMetadataActivity.java | 30 +++++++++++++++-- .../main/res/layout/activity_metadata_update.xml | 1 + 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 4ecd7c9246..452f6a615d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -23,7 +23,6 @@ import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; - import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.telemetry.AppUserTurnstile; import com.mapbox.android.telemetry.Event; @@ -33,6 +32,8 @@ import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; @@ -40,9 +41,13 @@ import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; +import com.mapbox.mapboxsdk.offline.OfflineRegionDefinition; +import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.mapbox.mapboxsdk.storage.FileSource; import com.mapbox.mapboxsdk.utils.BitmapUtils; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -51,9 +56,6 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; @@ -535,6 +537,35 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { nativeMapView.setStyleUrl(url); } + /** + * Loads a new style from the specified offline region definition and moves the map camera to that region. + * + * @param definition the offline region definition + * @see OfflineRegionDefinition + */ + public void setOfflineRegionDefinition(OfflineRegionDefinition definition) { + if (destroyed) { + return; + } + + OfflineTilePyramidRegionDefinition regionDefinition = (OfflineTilePyramidRegionDefinition) definition; + setStyleUrl(regionDefinition.getStyleURL()); + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(regionDefinition.getBounds().getCenter()) + .zoom(regionDefinition.getMinZoom()) + .build(); + + if (!isMapInitialized()) { + mapboxMapOptions.camera(cameraPosition); + mapboxMapOptions.minZoomPreference(regionDefinition.getMinZoom()); + mapboxMapOptions.maxZoomPreference(regionDefinition.getMaxZoom()); + return; + } + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + mapboxMap.setMinZoomPreference(regionDefinition.getMinZoom()); + mapboxMap.setMaxZoomPreference(regionDefinition.getMaxZoom()); + } + // // Rendering // diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java index c5ad467673..e1a524790d 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java @@ -14,7 +14,7 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; - +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.offline.OfflineManager; import com.mapbox.mapboxsdk.offline.OfflineRegion; import com.mapbox.mapboxsdk.testapp.R; @@ -27,10 +27,13 @@ import java.util.List; /** * Test activity showing integration of updating metadata of an OfflineRegion. */ -public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { +public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { private OfflineRegionMetadataAdapter adapter; + private MapView mapView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -40,6 +43,7 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter listView.setAdapter(adapter = new OfflineRegionMetadataAdapter(this)); listView.setEmptyView(findViewById(android.R.id.empty)); listView.setOnItemClickListener(this); + listView.setOnItemLongClickListener(this); } @Override @@ -64,6 +68,18 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter builder.show(); } + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + ViewGroup container = (ViewGroup) findViewById(R.id.container); + container.removeAllViews(); + container.addView(mapView = new MapView(view.getContext())); + mapView.setOfflineRegionDefinition(adapter.getItem(position).getDefinition()); + mapView.onCreate(null); + mapView.onStart(); + mapView.onResume(); + return true; + } + private void updateMetadata(OfflineRegion region, byte[] metadata) { region.updateMetadata(metadata, new OfflineRegion.OfflineRegionUpdateMetadataCallback() { @Override @@ -104,6 +120,16 @@ public class UpdateMetadataActivity extends AppCompatActivity implements Adapter }); } + @Override + protected void onDestroy() { + super.onDestroy(); + if (mapView != null) { + mapView.onPause(); + mapView.onStop(); + mapView.onDestroy(); + } + } + private static class OfflineRegionMetadataAdapter extends BaseAdapter { private Context context; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml index 1eb999caf5..501bc55743 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml @@ -1,5 +1,6 @@ Date: Wed, 16 May 2018 17:37:04 +0200 Subject: [android] - add nullability annotations to public api for kotlin language integration --- .../java/com/mapbox/mapboxsdk/maps/MapView.java | 18 ++-- .../java/com/mapbox/mapboxsdk/maps/MapboxMap.java | 103 ++++++++++++--------- .../java/com/mapbox/mapboxsdk/maps/Projection.java | 13 ++- .../java/com/mapbox/mapboxsdk/maps/UiSettings.java | 6 +- 4 files changed, 81 insertions(+), 59 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 452f6a615d..23fb3c22dd 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -629,10 +629,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param listener The callback that's invoked on every frame rendered to the map view. * @see MapView#removeOnMapChangedListener(OnMapChangedListener) */ - public void addOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null) { - onMapChangedListeners.add(listener); - } + public void addOnMapChangedListener(@NonNull OnMapChangedListener listener) { + onMapChangedListeners.add(listener); } /** @@ -641,8 +639,8 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param listener The previously added callback to remove. * @see MapView#addOnMapChangedListener(OnMapChangedListener) */ - public void removeOnMapChangedListener(@Nullable OnMapChangedListener listener) { - if (listener != null && onMapChangedListeners.contains(listener)) { + public void removeOnMapChangedListener(@NonNull OnMapChangedListener listener) { + if (onMapChangedListeners.contains(listener)) { onMapChangedListeners.remove(listener); } } @@ -653,13 +651,11 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { * @param callback The callback object that will be triggered when the map is ready to be used. */ @UiThread - public void getMapAsync(final OnMapReadyCallback callback) { - if (!mapCallback.isInitialLoad() && callback != null) { + public void getMapAsync(final @NonNull OnMapReadyCallback callback) { + if (!mapCallback.isInitialLoad()) { callback.onMapReady(mapboxMap); } else { - if (callback != null) { - mapCallback.addOnMapReadyCallback(callback); - } + mapCallback.addOnMapReadyCallback(callback); } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index cfa7143671..aed918cb79 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -120,7 +120,7 @@ public final class MapboxMap { * * @param outState the bundle to save the state to. */ - void onSaveInstanceState(Bundle outState) { + void onSaveInstanceState(@NonNull Bundle outState) { outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); @@ -132,7 +132,7 @@ public final class MapboxMap { * * @param savedInstanceState the bundle containing the saved state */ - void onRestoreInstanceState(Bundle savedInstanceState) { + void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); uiSettings.onRestoreInstanceState(savedInstanceState); @@ -266,6 +266,7 @@ public final class MapboxMap { * * @return all the layers in the current style */ + @NonNull public List getLayers() { return nativeMapView.getLayers(); } @@ -377,6 +378,7 @@ public final class MapboxMap { * * @return all the sources in the current style */ + @NonNull public List getSources() { return nativeMapView.getSources(); } @@ -463,10 +465,11 @@ public final class MapboxMap { * * @param name the name of the image to remove */ - public void removeImage(String name) { + public void removeImage(@NonNull String name) { nativeMapView.removeImage(name); } + @Nullable public Bitmap getImage(@NonNull String name) { return nativeMapView.getImage(name); } @@ -537,6 +540,7 @@ public final class MapboxMap { * * @return the UiSettings associated with this map */ + @NonNull public UiSettings getUiSettings() { return uiSettings; } @@ -551,6 +555,7 @@ public final class MapboxMap { * * @return the Projection associated with this map */ + @NonNull public Projection getProjection() { return projection; } @@ -564,7 +569,7 @@ public final class MapboxMap { * * @return the global light source */ - @Nullable + @NonNull public Light getLight() { return nativeMapView.getLight(); } @@ -590,6 +595,7 @@ public final class MapboxMap { * * @return The current position of the Camera. */ + @NonNull public final CameraPosition getCameraPosition() { return transform.getCameraPosition(); } @@ -612,7 +618,7 @@ public final class MapboxMap { * * @param update The change that should be applied to the camera. */ - public final void moveCamera(CameraUpdate update) { + public final void moveCamera(@NonNull CameraUpdate update) { moveCamera(update, null); } @@ -624,7 +630,8 @@ public final class MapboxMap { * @param update The change that should be applied to the camera * @param callback the callback to be invoked when an animation finishes or is canceled */ - public final void moveCamera(final CameraUpdate update, final MapboxMap.CancelableCallback callback) { + public final void moveCamera(@NonNull final CameraUpdate update, + @Nullable final MapboxMap.CancelableCallback callback) { transform.moveCamera(MapboxMap.this, update, callback); } @@ -650,7 +657,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void easeCamera(CameraUpdate update, int durationMs) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs) { easeCamera(update, durationMs, null); } @@ -673,7 +680,8 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void easeCamera(CameraUpdate update, int durationMs, final MapboxMap.CancelableCallback callback) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs, + @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, true, callback); } @@ -691,7 +699,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @param easingInterpolator True for easing interpolator, false for linear. */ - public final void easeCamera(CameraUpdate update, int durationMs, boolean easingInterpolator) { + public final void easeCamera(@NonNull CameraUpdate update, int durationMs, boolean easingInterpolator) { easeCamera(update, durationMs, easingInterpolator, null); } @@ -711,8 +719,10 @@ public final class MapboxMap { * by a later camera movement or a user gesture, onCancel() will be called. * Do not update or ease the camera from within onCancel(). */ - public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, - final MapboxMap.CancelableCallback callback) { + public final void easeCamera(@NonNull final CameraUpdate update, + final int durationMs, + final boolean easingInterpolator, + @Nullable final MapboxMap.CancelableCallback callback) { easeCamera(update, durationMs, easingInterpolator, callback, false); } @@ -733,8 +743,9 @@ public final class MapboxMap { * Do not update or ease the camera from within onCancel(). * @param isDismissable true will allow animated camera changes dismiss a tracking mode. */ - public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator, - final MapboxMap.CancelableCallback callback, final boolean isDismissable) { + public final void easeCamera(@NonNull final CameraUpdate update, final int durationMs, + final boolean easingInterpolator, @Nullable final MapboxMap.CancelableCallback callback, + final boolean isDismissable) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into easeCamera"); @@ -751,7 +762,7 @@ public final class MapboxMap { * @param update The change that should be applied to the camera. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update) { + public final void animateCamera(@NonNull CameraUpdate update) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); } @@ -767,7 +778,7 @@ public final class MapboxMap { * called. Do not update or animate the camera from within onCancel(). * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update, MapboxMap.CancelableCallback callback) { + public final void animateCamera(@NonNull CameraUpdate update, @Nullable MapboxMap.CancelableCallback callback) { animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); } @@ -782,7 +793,7 @@ public final class MapboxMap { * positive, otherwise an IllegalArgumentException will be thrown. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(CameraUpdate update, int durationMs) { + public final void animateCamera(@NonNull CameraUpdate update, int durationMs) { animateCamera(update, durationMs, null); } @@ -804,8 +815,8 @@ public final class MapboxMap { * isn't required, leave it as null. * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. */ - public final void animateCamera(final CameraUpdate update, final int durationMs, - final MapboxMap.CancelableCallback callback) { + public final void animateCamera(@NonNull final CameraUpdate update, final int durationMs, + @Nullable final MapboxMap.CancelableCallback callback) { if (durationMs <= 0) { throw new IllegalArgumentException("Null duration passed into animageCamera"); } @@ -1069,6 +1080,7 @@ public final class MapboxMap { * * @return The json of the map style */ + @NonNull public String getStyleJson() { return nativeMapView.getStyleJson(); } @@ -1232,7 +1244,7 @@ public final class MapboxMap { * * @param polyline An updated polyline object. */ - public void updatePolyline(Polyline polyline) { + public void updatePolyline(@NonNull Polyline polyline) { annotationManager.updatePolyline(polyline); } @@ -1263,7 +1275,7 @@ public final class MapboxMap { * * @param polygon An updated polygon object */ - public void updatePolygon(Polygon polygon) { + public void updatePolygon(@NonNull Polygon polygon) { annotationManager.updatePolygon(polygon); } @@ -1468,6 +1480,7 @@ public final class MapboxMap { * * @return The currently selected marker. */ + @NonNull public List getSelectedMarkers() { return annotationManager.getSelectedMarkers(); } @@ -1477,6 +1490,7 @@ public final class MapboxMap { * * @return the associated MarkerViewManager */ + @NonNull public MarkerViewManager getMarkerViewManager() { return annotationManager.getMarkerViewManager(); } @@ -1550,6 +1564,7 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ + @NonNull public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { // get padded camera position from LatLngBounds return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); @@ -1563,6 +1578,7 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ + @NonNull public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) { // get padded camera position from Geometry return nativeMapView.getCameraForGeometry(geometry, bearing, padding); @@ -1591,11 +1607,7 @@ public final class MapboxMap { * @param bottom The bottom margin in pixels. */ public void setPadding(int left, int top, int right, int bottom) { - setPadding(new int[] {left, top, right, bottom}); - } - - private void setPadding(int[] padding) { - projection.setContentPadding(padding); + projection.setContentPadding(new int[] {left, top, right, bottom}); uiSettings.invalidate(); } @@ -1604,6 +1616,7 @@ public final class MapboxMap { * * @return An array with length 4 in the LTRB order. */ + @NonNull public int[] getPadding() { return projection.getContentPadding(); } @@ -1755,6 +1768,7 @@ public final class MapboxMap { } // used by MapView + @Nullable OnFpsChangedListener getOnFpsChangedListener() { return onFpsChangedListener; } @@ -1902,7 +1916,7 @@ public final class MapboxMap { * will be added to the passed gestures manager. * @see mapbox-gestures-android library */ - public void setGesturesManager(AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, + public void setGesturesManager(@NonNull AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, boolean setDefaultMutuallyExclusives) { onGesturesManagerInteractionListener.setGesturesManager( androidGesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); @@ -1914,6 +1928,7 @@ public final class MapboxMap { * * @return Current gestures manager. */ + @NonNull public AndroidGesturesManager getGesturesManager() { return onGesturesManagerInteractionListener.getGesturesManager(); } @@ -2000,6 +2015,7 @@ public final class MapboxMap { * * @return Current active InfoWindow Click Listener */ + @Nullable public OnInfoWindowClickListener getOnInfoWindowClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener(); } @@ -2020,6 +2036,7 @@ public final class MapboxMap { * * @return Current active InfoWindow long Click Listener */ + @Nullable public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowLongClickListener(); } @@ -2038,6 +2055,7 @@ public final class MapboxMap { * * @return Current active InfoWindow Close Listener */ + @Nullable public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener(); } @@ -2147,11 +2165,11 @@ public final class MapboxMap { * @see MapboxMap#addOnMoveListener(OnMoveListener) */ public interface OnMoveListener { - void onMoveBegin(MoveGestureDetector detector); + void onMoveBegin(@NonNull MoveGestureDetector detector); - void onMove(MoveGestureDetector detector); + void onMove(@NonNull MoveGestureDetector detector); - void onMoveEnd(MoveGestureDetector detector); + void onMoveEnd(@NonNull MoveGestureDetector detector); } /** @@ -2160,11 +2178,11 @@ public final class MapboxMap { * @see MapboxMap#addOnRotateListener(OnRotateListener) */ public interface OnRotateListener { - void onRotateBegin(RotateGestureDetector detector); + void onRotateBegin(@NonNull RotateGestureDetector detector); - void onRotate(RotateGestureDetector detector); + void onRotate(@NonNull RotateGestureDetector detector); - void onRotateEnd(RotateGestureDetector detector); + void onRotateEnd(@NonNull RotateGestureDetector detector); } /** @@ -2173,11 +2191,11 @@ public final class MapboxMap { * @see MapboxMap#addOnScaleListener(OnScaleListener) */ public interface OnScaleListener { - void onScaleBegin(StandardScaleGestureDetector detector); + void onScaleBegin(@NonNull StandardScaleGestureDetector detector); - void onScale(StandardScaleGestureDetector detector); + void onScale(@NonNull StandardScaleGestureDetector detector); - void onScaleEnd(StandardScaleGestureDetector detector); + void onScaleEnd(@NonNull StandardScaleGestureDetector detector); } /** @@ -2186,11 +2204,11 @@ public final class MapboxMap { * @see MapboxMap#addOnShoveListener(OnShoveListener) */ public interface OnShoveListener { - void onShoveBegin(ShoveGestureDetector detector); + void onShoveBegin(@NonNull ShoveGestureDetector detector); - void onShove(ShoveGestureDetector detector); + void onShove(@NonNull ShoveGestureDetector detector); - void onShoveEnd(ShoveGestureDetector detector); + void onShoveEnd(@NonNull ShoveGestureDetector detector); } /** @@ -2442,7 +2460,7 @@ public final class MapboxMap { * * @param marker The marker were the info window is attached to */ - void onInfoWindowLongClick(Marker marker); + void onInfoWindowLongClick(@NonNull Marker marker); } /** @@ -2457,7 +2475,7 @@ public final class MapboxMap { * * @param marker The marker of the info window that was closed. */ - void onInfoWindowClose(Marker marker); + void onInfoWindowClose(@NonNull Marker marker); } /** @@ -2637,7 +2655,7 @@ public final class MapboxMap { * * @param snapshot the snapshot bitmap */ - void onSnapshotReady(Bitmap snapshot); + void onSnapshotReady(@NonNull Bitmap snapshot); } /** @@ -2649,12 +2667,13 @@ public final class MapboxMap { * * @param style the style that has been loaded */ - void onStyleLoaded(String style); + void onStyleLoaded(@NonNull String style); } // // Used for instrumentation testing // + @NonNull Transform getTransform() { return transform; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index f35355533d..d5166c17b0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -43,14 +43,16 @@ public class Projection { /** * Returns the spherical Mercator projected meters for a LatLng. */ - public ProjectedMeters getProjectedMetersForLatLng(LatLng latLng) { + @NonNull + public ProjectedMeters getProjectedMetersForLatLng(@NonNull LatLng latLng) { return nativeMapView.projectedMetersForLatLng(latLng); } /** * Returns the LatLng for a spherical Mercator projected meters. */ - public LatLng getLatLngForProjectedMeters(ProjectedMeters projectedMeters) { + @NonNull + public LatLng getLatLngForProjectedMeters(@NonNull ProjectedMeters projectedMeters) { return nativeMapView.latLngForProjectedMeters(projectedMeters); } @@ -77,7 +79,8 @@ public class Projection { * @return The LatLng corresponding to the point on the screen, or null if the ray through * the given screen point does not intersect the ground plane. */ - public LatLng fromScreenLocation(PointF point) { + @NonNull + public LatLng fromScreenLocation(@NonNull PointF point) { return nativeMapView.latLngForPixel(point); } @@ -87,6 +90,7 @@ public class Projection { * * @return The projection of the viewing frustum in its current state. */ + @NonNull public VisibleRegion getVisibleRegion() { float left = 0; float right = nativeMapView.getWidth(); @@ -151,7 +155,8 @@ public class Projection { * @param location A LatLng on the map to convert to a screen location. * @return A Point representing the screen location in screen pixels. */ - public PointF toScreenLocation(LatLng location) { + @NonNull + public PointF toScreenLocation(@NonNull LatLng location) { return nativeMapView.pixelForLatLng(location); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index c1daebbe52..100787fbf0 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -332,7 +332,7 @@ public final class UiSettings { * * @param compass the drawable to show as image compass */ - public void setCompassImage(Drawable compass) { + public void setCompassImage(@NonNull Drawable compass) { compassView.setCompassImage(compass); } @@ -409,6 +409,7 @@ public final class UiSettings { * * @return the drawable used as compass image */ + @NonNull public Drawable getCompassImage() { return compassView.getCompassImage(); } @@ -544,7 +545,7 @@ public final class UiSettings { * * @param attributionDialogManager the manager class used for showing attribution */ - public void setAttributionDialogManager(AttributionDialogManager attributionDialogManager) { + public void setAttributionDialogManager(@NonNull AttributionDialogManager attributionDialogManager) { this.attributionDialogManager = attributionDialogManager; } @@ -553,6 +554,7 @@ public final class UiSettings { * * @return the active manager class used for showing attribution */ + @NonNull public AttributionDialogManager getAttributionDialogManager() { return attributionDialogManager; } -- cgit v1.2.1 From 4ceb687f6c7bdb4cd768310eeae558d826dcf506 Mon Sep 17 00:00:00 2001 From: tobrun Date: Thu, 17 May 2018 18:26:28 +0200 Subject: [android] - expose MapView created callbacks on MapFragment and MapSupportFragment --- .../com/mapbox/mapboxsdk/maps/MapFragment.java | 44 +++++++++++- .../mapbox/mapboxsdk/maps/SupportMapFragment.java | 19 +++++ .../activity/fragment/MapFragmentActivity.java | 79 +++++++++++++-------- .../fragment/SupportMapFragmentActivity.java | 82 ++++++++++++++-------- 4 files changed, 160 insertions(+), 64 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java index 5fcf206a5a..280877d61a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java @@ -8,7 +8,6 @@ import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import com.mapbox.mapboxsdk.utils.MapFragmentUtils; import java.util.ArrayList; @@ -31,6 +30,7 @@ import java.util.List; public final class MapFragment extends Fragment implements OnMapReadyCallback { private final List mapReadyCallbackList = new ArrayList<>(); + private OnMapViewReadyCallback mapViewReadyCallback; private MapboxMap mapboxMap; private MapView map; @@ -55,6 +55,19 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { return mapFragment; } + /** + * Called when the context attaches to this fragment. + * + * @param context the context attaching + */ + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnMapViewReadyCallback) { + mapViewReadyCallback = (OnMapViewReadyCallback) context; + } + } + /** * Creates the fragment view hierarchy. * @@ -75,15 +88,25 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { * Called when the fragment view hierarchy is created. * * @param view The content view of the fragment - * @param savedInstanceState THe saved instance state of the framgnt + * @param savedInstanceState The saved instance state of the fragment */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); map.getMapAsync(this); + + // notify listeners about mapview creation + if (mapViewReadyCallback != null) { + mapViewReadyCallback.onMapViewReady(map); + } } + /** + * Called when the style of the map has successfully loaded. + * + * @param mapboxMap The public api controller of the map + */ @Override public void onMapReady(MapboxMap mapboxMap) { this.mapboxMap = mapboxMap; @@ -170,4 +193,21 @@ public final class MapFragment extends Fragment implements OnMapReadyCallback { onMapReadyCallback.onMapReady(mapboxMap); } } + + /** + * Callback to be invoked when the map fragment has inflated its MapView. + *

+ * To use this interface the context hosting the fragment must implement this interface. + * That instance will be set as part of Fragment#onAttach(Context context). + *

+ */ + public interface OnMapViewReadyCallback { + + /** + * Called when the map has been created. + * + * @param mapView The created mapview + */ + void onMapViewReady(MapView mapView); + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java index 8aa4c7fd09..307b33b0c7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java @@ -31,6 +31,7 @@ import java.util.List; public class SupportMapFragment extends Fragment implements OnMapReadyCallback { private final List mapReadyCallbackList = new ArrayList<>(); + private MapFragment.OnMapViewReadyCallback mapViewReadyCallback; private MapboxMap mapboxMap; private MapView map; @@ -55,6 +56,19 @@ public class SupportMapFragment extends Fragment implements OnMapReadyCallback { return mapFragment; } + /** + * Called when the context attaches to this fragment. + * + * @param context the context attaching + */ + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof MapFragment.OnMapViewReadyCallback) { + mapViewReadyCallback = (MapFragment.OnMapViewReadyCallback) context; + } + } + /** * Creates the fragment view hierarchy. * @@ -82,6 +96,11 @@ public class SupportMapFragment extends Fragment implements OnMapReadyCallback { super.onViewCreated(view, savedInstanceState); map.onCreate(savedInstanceState); map.getMapAsync(this); + + // notify listeners about MapView creation + if (mapViewReadyCallback != null) { + mapViewReadyCallback.onMapViewReady(map); + } } @Override diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java index 930626078d..f5e0371aad 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java @@ -1,14 +1,13 @@ package com.mapbox.mapboxsdk.testapp.activity.fragment; -import android.app.FragmentTransaction; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; - import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -20,53 +19,71 @@ import com.mapbox.mapboxsdk.testapp.R; * Uses MapboxMapOptions to initialise the Fragment. *

*/ -public class MapFragmentActivity extends AppCompatActivity implements OnMapReadyCallback { +public class MapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_fragment); - - MapFragment mapFragment; if (savedInstanceState == null) { - final FragmentTransaction transaction = getFragmentManager().beginTransaction(); + MapFragment mapFragment = MapFragment.newInstance(createFragmentOptions()); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container,mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } - MapboxMapOptions options = new MapboxMapOptions(); - options.styleUrl(Style.OUTDOORS); + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.OUTDOORS); - options.scrollGesturesEnabled(false); - options.zoomGesturesEnabled(false); - options.tiltGesturesEnabled(false); - options.rotateGesturesEnabled(false); + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); - options.debugActive(false); + LatLng dc = new LatLng(38.90252, -77.02291); - LatLng dc = new LatLng(38.90252, -77.02291); + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } - options.minZoomPreference(9); - options.maxZoomPreference(11); - options.camera(new CameraPosition.Builder() - .target(dc) - .zoom(11) - .build()); + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); + } - mapFragment = MapFragment.newInstance(options); + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } - transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map"); - transaction.commit(); - } else { - mapFragment = (MapFragment) getFragmentManager().findFragmentByTag("com.mapbox.map"); + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; } - - mapFragment.getMapAsync(this); } @Override - public void onMapReady(MapboxMap map) { - mapboxMap = map; - mapboxMap.animateCamera( - CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000); + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); } } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java index 4f828060ee..f494a9231e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java @@ -1,13 +1,13 @@ package com.mapbox.mapboxsdk.testapp.activity.fragment; import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; - import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.MapboxMapOptions; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -20,51 +20,71 @@ import com.mapbox.mapboxsdk.testapp.R; * Uses MapboxMapOptions to initialise the Fragment. *

*/ -public class SupportMapFragmentActivity extends AppCompatActivity implements OnMapReadyCallback { +public class SupportMapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_fragment); - - SupportMapFragment mapFragment; if (savedInstanceState == null) { - final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - - MapboxMapOptions options = new MapboxMapOptions(); - options.styleUrl(Style.SATELLITE_STREETS); - - options.debugActive(false); - options.compassEnabled(false); - options.attributionEnabled(false); - options.logoEnabled(false); + SupportMapFragment mapFragment = SupportMapFragment.newInstance(createFragmentOptions()); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } - LatLng dc = new LatLng(38.90252, -77.02291); + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.MAPBOX_STREETS); - options.minZoomPreference(9); - options.maxZoomPreference(11); - options.camera(new CameraPosition.Builder() - .target(dc) - .zoom(11) - .build()); + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); - mapFragment = SupportMapFragment.newInstance(options); + LatLng dc = new LatLng(38.90252, -77.02291); - transaction.add(R.id.fragment_container, mapFragment, "com.mapbox.map"); - transaction.commit(); - } else { - mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentByTag("com.mapbox.map"); - } + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } - mapFragment.getMapAsync(this); + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); } @Override public void onMapReady(MapboxMap map) { mapboxMap = map; - mapboxMap.animateCamera( - CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 10000); } -} + + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); + } +} \ No newline at end of file -- cgit v1.2.1 From 1aa5c67837a19d5f8ba8f7336f183da83e68441c Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Thu, 17 May 2018 14:47:01 -0400 Subject: [ios, macos] Support array values in match expressions. (#11866) * [ios, macos] Support array values in match expressions. * [ios, macos] Update mgl_match documentation. * [ios, macos] Update changelogs. * [ios, macos] Clarify match expressions documentation. --- .../docs/guides/Predicates and Expressions.md | 4 +++ .../src/NSComparisonPredicate+MGLAdditions.mm | 17 ++++++------ platform/darwin/src/NSExpression+MGLAdditions.mm | 25 +++++++++++++++--- platform/darwin/test/MGLExpressionTests.mm | 6 +++++ platform/darwin/test/MGLPredicateTests.mm | 30 +++++++++++----------- platform/ios/CHANGELOG.md | 4 +++ platform/macos/CHANGELOG.md | 4 +++ 7 files changed, 62 insertions(+), 28 deletions(-) diff --git a/platform/darwin/docs/guides/Predicates and Expressions.md b/platform/darwin/docs/guides/Predicates and Expressions.md index 18eccda569..e0d4755d4a 100644 --- a/platform/darwin/docs/guides/Predicates and Expressions.md +++ b/platform/darwin/docs/guides/Predicates and Expressions.md @@ -536,6 +536,10 @@ expression that contains references to those variables. An input expression, then any number of argument pairs, followed by a default expression. Each argument pair consists of a constant value followed by an expression to produce as a result of matching that constant value. + If the input value is an aggregate expression, then any of the constant values within + that aggregate expression result in the following argument. This is shorthand for + specifying an argument pair for each of the constant values within that aggregate + expression. It is not possible to match the aggregate expression itself. diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm index 380215ff32..15aa71419d 100644 --- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm +++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm @@ -115,15 +115,14 @@ return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject]; } case NSInPredicateOperatorType: { - NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil]; - NSArray *optionsExpressions = self.rightExpression.constantValue; - for (id object in optionsExpressions) { - id option = ((NSExpression *)object).mgl_jsonExpressionObject; - [elements addObject:option]; - [elements addObject:@YES]; - } - [elements addObject:@NO]; - return elements; + + NSExpression *matchExpression = [NSExpression expressionForFunction:@"MGL_MATCH" + arguments:@[self.leftExpression, + self.rightExpression, + [NSExpression expressionForConstantValue:@YES], + [NSExpression expressionForConstantValue:@NO]]]; + + return matchExpression.mgl_jsonExpressionObject; } case NSContainsPredicateOperatorType: { NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm index be93b13f3c..af4a197662 100644 --- a/platform/darwin/src/NSExpression+MGLAdditions.mm +++ b/platform/darwin/src/NSExpression+MGLAdditions.mm @@ -763,6 +763,11 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *array = (NSArray *)object; NSString *op = array.firstObject; + if (![op isKindOfClass:[NSString class]]) { + NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array); + return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions]; + } + NSArray *argumentObjects = [array subarrayWithRange:NSMakeRange(1, array.count - 1)]; NSString *functionName = MGLFunctionNamesByExpressionOperator[op]; @@ -940,9 +945,13 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments]; } else if ([op isEqualToString:@"match"]) { NSMutableArray *optionsArray = [NSMutableArray array]; - NSEnumerator *optionsEnumerator = argumentObjects.objectEnumerator; - while (id object = optionsEnumerator.nextObject) { - NSExpression *option = [NSExpression expressionWithMGLJSONObject:object]; + + for (NSUInteger index = 0; index < argumentObjects.count; index++) { + NSExpression *option = [NSExpression expressionWithMGLJSONObject:argumentObjects[index]]; + // match operators with arrays as matching values should not parse arrays as generic functions. + if (index > 0 && index < argumentObjects.count - 1 && !(index % 2 == 0) && [argumentObjects[index] isKindOfClass:[NSArray class]]) { + option = [NSExpression expressionForAggregate:MGLSubexpressionsWithJSONObjects(argumentObjects[index])]; + } [optionsArray addObject:option]; } @@ -1346,7 +1355,15 @@ NSArray *MGLSubexpressionsWithJSONObjects(NSArray *objects) { NSArray *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue; for (NSUInteger index = minimumIndex; index < arguments.count; index++) { - [expressionObject addObject:arguments[index].mgl_jsonExpressionObject]; + NSArray *argumentObject = arguments[index].mgl_jsonExpressionObject; + // match operators with arrays as matching values should not parse arrays using the literal operator. + if (index > 0 && index < arguments.count - 1 && !(index % 2 == 0) + && (arguments[index].expressionType == NSAggregateExpressionType || + (arguments[index].expressionType == NSConstantValueExpressionType && [arguments[index].constantValue isKindOfClass:[NSArray class]]))) { + + argumentObject = argumentObject.count == 2 ? argumentObject[1] : argumentObject; + } + [expressionObject addObject:argumentObject]; } return expressionObject; diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm index e514ec22a7..dbdad2ed13 100644 --- a/platform/darwin/test/MGLExpressionTests.mm +++ b/platform/darwin/test/MGLExpressionTests.mm @@ -777,6 +777,12 @@ using namespace std::string_literals; XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); } + { + NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(x, {'a', 'A'}, 'Apple', {'b', 'B'}, 'Banana', 'Kumquat')"]; + NSArray *jsonExpression = @[@"match", @[@"get", @"x"], @[@"a", @"A"], @"Apple", @[@"b", @"B"], @"Banana", @"Kumquat"]; + XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression); + XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression); + } } - (void)testCoalesceExpressionObject { diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index 4e7b3e7e4b..a21774a8f3 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -216,64 +216,64 @@ mustRoundTrip:NO]; } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]]; + NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO]; + NSArray *expected = @[@"match", @[@"get", @"a"], @[@"b", @"c"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), { 'b', 'c' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"]; + NSArray *expected = @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO]; + NSArray *expected = @[@"match", @[@"geometry-type"], @[@"LineString", @"Polygon"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, { 'LineString', 'Polygon' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); } { - NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]; + NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index f70330300b..27ade8b4b0 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -8,6 +8,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * The minimum deployment target for this SDK is now iOS 9.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) +### Style layers + +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) + ## 4.0.1 - May 14, 2018 ### Packaging diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index ab71e62ec6..08093f2415 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -6,6 +6,10 @@ * The minimum deployment target for this SDK is now macOS 10.11.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776)) +### Style layers + +* Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) + ## 0.7.1 ### Style layers -- cgit v1.2.1 From e1f89bf369dc9ef88ce19f737a01ff247830a46a Mon Sep 17 00:00:00 2001 From: tobrun Date: Thu, 17 May 2018 19:42:55 +0200 Subject: [android] - add changelog for 6.2.0-alpha.1 --- platform/android/CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index a25656012b..d96957a07b 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,8 +2,13 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## 6.1.2 +## 6.2.0-alpha.1 - May 17, 2018 + - `"to-string"` expression operator converts `null` to empty string rather than to `"null"` [#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904) + - Expose MapView#setOfflineRegion [#1922](https://github.com/mapbox/mapbox-gl-native/pull/11922) + - Add nullability annotations to public API for kotlin language integration [#11925](https://github.com/mapbox/mapbox-gl-native/pull/11925) + - Expose MapView created callbacks on MapFragment and SupportMapFragment [#11934](https://github.com/mapbox/mapbox-gl-native/pull/11934) + - Update mapbox-java to 3.1.0 [#11916](https://github.com/mapbox/mapbox-gl-native/pull/11916) ## 6.1.1 - May 7, 2018 - Update telemetry to 3.1.0 [#11855](https://github.com/mapbox/mapbox-gl-native/pull/11855) -- cgit v1.2.1 From e70c73249bcb2a47f976b5b7a6592d84adbda7ae Mon Sep 17 00:00:00 2001 From: Tobrun Date: Fri, 18 May 2018 10:23:10 +0200 Subject: [android] - bump telemetry to v3.1.1 --- platform/android/CHANGELOG.md | 22 ++++++++++++---------- platform/android/gradle/dependencies.gradle | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index d96957a07b..7b8d2a82ef 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,8 +2,10 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. -## 6.2.0-alpha.1 - May 17, 2018 +## 6.1.2 - May 18, 2018 + - Update telemetry to 3.1.1 [#11942](https://github.com/mapbox/mapbox-gl-native/pull/11942) +## 6.2.0-alpha.1 - May 17, 2018 - `"to-string"` expression operator converts `null` to empty string rather than to `"null"` [#11904](https://github.com/mapbox/mapbox-gl-native/pull/11904) - Expose MapView#setOfflineRegion [#1922](https://github.com/mapbox/mapbox-gl-native/pull/11922) - Add nullability annotations to public API for kotlin language integration [#11925](https://github.com/mapbox/mapbox-gl-native/pull/11925) @@ -105,9 +107,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Update javadoc configuration for Gradle 4.4 [#11384](https://github.com/mapbox/mapbox-gl-native/pull/11384) - Rework zoomIn and zoomOut to use ValueAnimators [#11382](https://github.com/mapbox/mapbox-gl-native/pull/11382) - Delete LocalRef when converting Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350) - - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) + - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367) - Validate width/height when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364) - + ## 6.0.0-beta.3 - March 2, 2018 - Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) - Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201) @@ -130,7 +132,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Add missing DeleteLocalRefs [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272) - Continue loading style even if we mutate it [#11294](https://github.com/mapbox/mapbox-gl-native/pull/11294) - Update telemetry version for OkHttp [#11338](https://github.com/mapbox/mapbox-gl-native/pull/11338) - + ## 6.0.0-beta.2 - February 13, 2018 - Deprecate LocationEngine [#11185](https://github.com/mapbox/mapbox-gl-native/pull/11185) - Remove LOST from SDK [11186](https://github.com/mapbox/mapbox-gl-native/pull/11186) @@ -140,7 +142,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - AddImage performance improvement [#11111](https://github.com/mapbox/mapbox-gl-native/pull/11111) - Migrate MAS to 3.0.0, refactor GeoJson integration [#11149](https://github.com/mapbox/mapbox-gl-native/pull/11149) - Remove @jar and @aar dependency suffixes [#11161](https://github.com/mapbox/mapbox-gl-native/pull/11161) - + ## 5.4.1 - February 9, 2018 - Don't recreate TextureView surface as part of view resizing, solves OOM crashes [#11148](https://github.com/mapbox/mapbox-gl-native/pull/11148) - Don't invoke OnLowMemory before map is ready, solves startup crash on low memory devices [#11109](https://github.com/mapbox/mapbox-gl-native/pull/11109) @@ -175,7 +177,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Custom library loader [#10733](https://github.com/mapbox/mapbox-gl-native/pull/10733) - Inconsistent parameters for LatLngBounds.union [#10728](https://github.com/mapbox/mapbox-gl-native/pull/10728) - Gradle 4.1 / AS 3.0 [#10549](https://github.com/mapbox/mapbox-gl-native/pull/10549) - + ## 5.3.2 - January 22, 2018 - Validate surface creation before destroying [#10890](https://github.com/mapbox/mapbox-gl-native/pull/10890) - Add filesource activation ot OfflineRegion [#10904](https://github.com/mapbox/mapbox-gl-native/pull/10904) @@ -185,7 +187,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Allow changing the used OkHttpClient [#10948](https://github.com/mapbox/mapbox-gl-native/pull/10948) - Validate zoom level before creating telemetry event [#10959](https://github.com/mapbox/mapbox-gl-native/pull/10959) - Handle null call instances in HttpRequest [#10987](https://github.com/mapbox/mapbox-gl-native/pull/10987) - + ## 5.3.1 - January 10, 2018 - Blacklist binary program loading for Vivante GC4000 GPUs [#10862](https://github.com/mapbox/mapbox-gl-native/pull/10862) - Support Genymotion [#10841](https://github.com/mapbox/mapbox-gl-native/pull/10841) @@ -196,11 +198,11 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Allow configuring Http url logging when a request fails [#10830](https://github.com/mapbox/mapbox-gl-native/pull/10830) - Don't send double tap event multiple times for telemetry [#10854](https://github.com/mapbox/mapbox-gl-native/pull/10854) - Fix code generation [#10856](https://github.com/mapbox/mapbox-gl-native/pull/10856) - - Use the correct cancelable callback after posting cancel [#10871](https://github.com/mapbox/mapbox-gl-native/pull/10871) - + - Use the correct cancelable callback after posting cancel [#10871](https://github.com/mapbox/mapbox-gl-native/pull/10871) + ## 5.3.0 - December 20, 2017 - Add support for TinySDF [#10706](https://github.com/mapbox/mapbox-gl-native/pull/10706) - - Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746) + - Save restore MyLocationViewSettings [#10746](https://github.com/mapbox/mapbox-gl-native/pull/10746) - Post animation callback invocation [#10664](https://github.com/mapbox/mapbox-gl-native/pull/10664) - Allow configuring Http logging [#10681](https://github.com/mapbox/mapbox-gl-native/pull/10681) - Fix reverse scale gesture [#10688](https://github.com/mapbox/mapbox-gl-native/pull/10688) diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index d50f6cef08..b9744a61ef 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -9,7 +9,7 @@ ext { versions = [ mapboxServices : '3.1.0', - mapboxTelemetry: '3.1.0', + mapboxTelemetry: '3.1.1', mapboxGestures : '0.2.0', supportLib : '25.4.0', espresso : '3.0.1', -- cgit v1.2.1 From be6e40eb683082cb0f3c330179ed59c92a9398aa Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Fri, 18 May 2018 10:14:38 -0400 Subject: [ios] Bump podspect to 4.1.0-alpha.1. (#11937) --- platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec | 2 +- platform/ios/Mapbox-iOS-SDK-symbols.podspec | 2 +- platform/ios/Mapbox-iOS-SDK.podspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec index 77ff15ab42..f2d8cee025 100644 --- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec +++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.1' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK-nightly-dynamic' m.version = "#{version}-nightly" diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec index 7db87e1f08..7f4f27301e 100644 --- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec +++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.1' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK-symbols' m.version = "#{version}-symbols" diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec index 67e1665853..79229c5e8e 100644 --- a/platform/ios/Mapbox-iOS-SDK.podspec +++ b/platform/ios/Mapbox-iOS-SDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - version = '4.0.1' + version = '4.1.0-alpha.1' m.name = 'Mapbox-iOS-SDK' m.version = version -- cgit v1.2.1 From b2fabe5eefc81cc38866a4856d6db37f4471d6ae Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 17 May 2018 09:55:15 -0700 Subject: [core] Align match behavior with case/== Makes `["match", ["get", k], label, match, otherwise]` equivalent to `["case", ["==", ["get", k], label], match, otherwise]`. This changes the behavior of match expressions where the runtime type of the input does not match the type of the labels: previously such expressions produced a runtime type error and then fell back to the property default value; now they produce the fallback value from the match expression. --- mapbox-gl-js | 2 +- platform/darwin/test/MGLPredicateTests.mm | 10 +++++----- platform/node/test/ignores.json | 3 +++ src/mbgl/style/expression/match.cpp | 18 ++++++++++++++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/mapbox-gl-js b/mapbox-gl-js index 5b3af8e151..a5a8dfe5b9 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit 5b3af8e1515ee311967661b5aabdab65f1376e36 +Subproject commit a5a8dfe5b90737dbb295eaca7f41c667ae4060a8 diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm index a21774a8f3..41c2d56868 100644 --- a/platform/darwin/test/MGLPredicateTests.mm +++ b/platform/darwin/test/MGLPredicateTests.mm @@ -219,7 +219,7 @@ NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); @@ -228,7 +228,7 @@ NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); @@ -237,7 +237,7 @@ NSArray *expected = @[@"match", @[@"get", @"a"], @[@"b", @"c"], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), { 'b', 'c' }, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(a, { 'b', 'c' }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); @@ -255,7 +255,7 @@ NSArray *expected = @[@"match", @[@"get", @"x"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3 } CONTAINS x"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(x, { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); @@ -273,7 +273,7 @@ NSArray *expected = @[@"match", @[@"id"], @[@6, @5, @4, @3], @YES, @NO]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"]; XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected); - NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), { 3, 4, 5, 6 }, YES, NO) == YES"]; + NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($featureIdentifier, { 3, 4, 5, 6 }, YES, NO) == YES"]; auto forwardFilter = [NSPredicate predicateWithMGLJSONObject:expected].mgl_filter; NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter]; XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter); diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index 47cb07c85a..ac00235702 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -42,6 +42,7 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js", + "render-tests/icon-rotate/with-offset": "https://github.com/mapbox/mapbox-gl-native/issues/11872", "render-tests/line-gradient/gradient": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/line-gradient/translucent": "https://github.com/mapbox/mapbox-gl-native/issues/11718", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", @@ -57,6 +58,7 @@ "render-tests/regressions/mapbox-gl-js#5370": "skip - https://github.com/mapbox/mapbox-gl-native/pull/9439", "render-tests/regressions/mapbox-gl-js#5740": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/regressions/mapbox-gl-js#5982": "https://github.com/mapbox/mapbox-gl-native/issues/10619", + "render-tests/regressions/mapbox-gl-js#6655": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357", "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847", "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745", @@ -67,6 +69,7 @@ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732", "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732", + "render-tests/text-rotate/with-offset": "https://github.com/mapbox/mapbox-gl-native/issues/11872", "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601", "render-tests/background-color/colorSpace-hcl": "needs issue", "render-tests/combinations/background-opaque--heatmap-translucent": "https://github.com/mapbox/mapbox-gl-native/issues/10146", diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp index 3d41f0bdd3..59123c9812 100644 --- a/src/mbgl/style/expression/match.cpp +++ b/src/mbgl/style/expression/match.cpp @@ -83,6 +83,10 @@ template<> EvaluationResult Match::evaluate(const EvaluationContext return inputValue.error(); } + if (!inputValue->is()) { + return otherwise->evaluate(params); + } + auto it = branches.find(inputValue->get()); if (it != branches.end()) { return (*it).second->evaluate(params); @@ -96,7 +100,11 @@ template<> EvaluationResult Match::evaluate(const EvaluationContext& pa if (!inputValue) { return inputValue.error(); } - + + if (!inputValue->is()) { + return otherwise->evaluate(params); + } + const auto numeric = inputValue->get(); int64_t rounded = std::floor(numeric); if (numeric == rounded) { @@ -280,7 +288,7 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) { branches.push_back(std::make_pair(std::move(labels), std::move(*output))); } - auto input = ctx.parse(arrayMember(value, 1), 1, inputType); + auto input = ctx.parse(arrayMember(value, 1), 1, {type::Value}); if (!input) { return ParseResult(); } @@ -292,6 +300,12 @@ ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) { assert(inputType && outputType); + optional err; + if ((*input)->getType() != type::Value && (err = type::checkSubtype(*inputType, (*input)->getType()))) { + ctx.error(*err, 1); + return ParseResult(); + } + return inputType->match( [&](const type::NumberType&) { return create(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx); -- cgit v1.2.1 From a84ac4c8d79952fa3031f5414b10a560fdef2e1d Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Fri, 18 May 2018 15:02:26 -0700 Subject: [core] Align URL token replacement behavior with GL JS I.e. preserve unknown tokens in URLs rather than replacing them with an empty string. --- platform/android/CHANGELOG.md | 3 +++ platform/ios/CHANGELOG.md | 4 ++++ platform/macos/CHANGELOG.md | 4 ++++ src/mbgl/storage/resource.cpp | 8 ++++---- src/mbgl/util/token.hpp | 11 ++++++++++- src/mbgl/util/url.cpp | 5 +++-- test/util/token.test.cpp | 3 +++ 7 files changed, 31 insertions(+), 7 deletions(-) diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 7b8d2a82ef..3fbfc32a76 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -2,6 +2,9 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## master +- Unknown tokens in URLs are now preserved, rather than replaced with an empty string [#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787) + ## 6.1.2 - May 18, 2018 - Update telemetry to 3.1.1 [#11942](https://github.com/mapbox/mapbox-gl-native/pull/11942) diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 27ade8b4b0..ef89b20c43 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -12,6 +12,10 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) +### Other changes + +* Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) + ## 4.0.1 - May 14, 2018 ### Packaging diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 08093f2415..0b3db6becb 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -10,6 +10,10 @@ * Added support for aggregate expressions as input values to `MGL_MATCH` expressions. ([#11866](https://github.com/mapbox/mapbox-gl-native/pull/11866)) +### Other changes + +* Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) + ## 0.7.1 ### Style layers diff --git a/src/mbgl/storage/resource.cpp b/src/mbgl/storage/resource.cpp index 207dd2ee69..ba85f87dea 100644 --- a/src/mbgl/storage/resource.cpp +++ b/src/mbgl/storage/resource.cpp @@ -79,13 +79,13 @@ Resource Resource::spriteJSON(const std::string& base, float pixelRatio) { Resource Resource::glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair& glyphRange) { return Resource { Resource::Kind::Glyphs, - util::replaceTokens(urlTemplate, [&](const std::string& token) { + util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional { if (token == "fontstack") { return util::percentEncode(fontStackToString(fontStack)); } else if (token == "range") { return util::toString(glyphRange.first) + "-" + util::toString(glyphRange.second); } else { - return std::string(); + return {}; } }) }; @@ -104,7 +104,7 @@ Resource Resource::tile(const std::string& urlTemplate, } return Resource { Resource::Kind::Tile, - util::replaceTokens(urlTemplate, [&](const std::string& token) { + util::replaceTokens(urlTemplate, [&](const std::string& token) -> optional { if (token == "z") { return util::toString(z); } else if (token == "x") { @@ -123,7 +123,7 @@ Resource Resource::tile(const std::string& urlTemplate, } else if (token == "ratio") { return std::string(pixelRatio > 1.0 ? "@2x" : ""); } else { - return std::string(); + return {}; } }), Resource::TileData { diff --git a/src/mbgl/util/token.hpp b/src/mbgl/util/token.hpp index 149661e47e..dea12f9412 100644 --- a/src/mbgl/util/token.hpp +++ b/src/mbgl/util/token.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -25,7 +27,14 @@ std::string replaceTokens(const std::string &source, const Lookup &lookup) { if (pos != end) { for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++); if (brace != end && *brace == '}') { - result.append(lookup({ pos + 1, brace })); + std::string key { pos + 1, brace }; + if (optional replacement = lookup(key)) { + result.append(*replacement); + } else { + result.append("{"); + result.append(key); + result.append("}"); + } pos = brace + 1; } else { result.append(pos, brace); diff --git a/src/mbgl/util/url.cpp b/src/mbgl/util/url.cpp index 1f6dab9639..a4263502ef 100644 --- a/src/mbgl/util/url.cpp +++ b/src/mbgl/util/url.cpp @@ -130,7 +130,7 @@ Path::Path(const std::string& str, const size_t pos, const size_t count) } std::string transformURL(const std::string& tpl, const std::string& str, const URL& url) { - auto result = util::replaceTokens(tpl, [&](const std::string& token) -> std::string { + auto result = util::replaceTokens(tpl, [&](const std::string& token) -> optional { if (token == "path") { return str.substr(url.path.first, url.path.second); } else if (token == "domain") { @@ -146,8 +146,9 @@ std::string transformURL(const std::string& tpl, const std::string& str, const U } else if (token == "extension") { const Path path(str, url.path.first, url.path.second); return str.substr(path.extension.first, path.extension.second); + } else { + return {}; } - return ""; }); // Append the query string if it exists. diff --git a/test/util/token.test.cpp b/test/util/token.test.cpp index a2885dc8dd..d0cf1e31cf 100644 --- a/test/util/token.test.cpp +++ b/test/util/token.test.cpp @@ -47,4 +47,7 @@ TEST(Token, replaceTokens) { if (token == "HØYDE") return "150"; return ""; })); + EXPECT_EQ("{unknown}", mbgl::util::replaceTokens("{unknown}", [](const std::string&) -> optional { + return {}; + })); } -- cgit v1.2.1 From bef2a7ccb6e800d2ca6f2bac5c863b49fbba103d Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Mon, 21 May 2018 14:14:58 -0400 Subject: [ios,macos] Fix for infinite loop crash when setting shape to nil in MGLMapViewDelegate methods (#11614) --- platform/ios/CHANGELOG.md | 1 + .../Integration Tests/MGLCameraTransitionTests.mm | 398 +++++++++++++++++++++ .../Integration Tests/MGLMapViewIntegrationTest.h | 13 +- .../Integration Tests/MGLMapViewIntegrationTest.m | 12 +- .../ios/Integration Tests/MGLShapeSourceTests.m | 126 +++++++ platform/ios/ios.xcodeproj/project.pbxproj | 12 + platform/ios/src/MGLCameraChangeReason.h | 6 +- platform/ios/src/MGLMapView.mm | 34 +- platform/macos/CHANGELOG.md | 1 + src/mbgl/map/transform.cpp | 57 ++- src/mbgl/map/transform.hpp | 2 +- 11 files changed, 630 insertions(+), 32 deletions(-) create mode 100644 platform/ios/Integration Tests/MGLCameraTransitionTests.mm create mode 100644 platform/ios/Integration Tests/MGLShapeSourceTests.m diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ef89b20c43..609bf5f606 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -15,6 +15,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Other changes * Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) +* Adjusted when and how the camera transition update and finish callbacks are called, fixing recursion bugs. ([#11614](https://github.com/mapbox/mapbox-gl-native/pull/11614)) ## 4.0.1 - May 14, 2018 diff --git a/platform/ios/Integration Tests/MGLCameraTransitionTests.mm b/platform/ios/Integration Tests/MGLCameraTransitionTests.mm new file mode 100644 index 0000000000..d5c288fbb9 --- /dev/null +++ b/platform/ios/Integration Tests/MGLCameraTransitionTests.mm @@ -0,0 +1,398 @@ +#import "MGLMapViewIntegrationTest.h" +#import "../../darwin/src/MGLGeometry_Private.h" + +@interface MBCameraTransitionTests : MGLMapViewIntegrationTest +@end + +@implementation MBCameraTransitionTests + +- (void)testSetAndResetNorthWithDispatchAsyncInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + [expectation fulfill]; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + if (mapView.direction != 0.0) { + dispatch_async(dispatch_get_main_queue(), ^{ + [mapView resetNorth]; + }); + } + }; + + [self.mapView setDirection:90 animated:YES]; + + // loop, render, and wait + [self waitForExpectations:@[expectation] timeout:1.5]; +} + + +- (void)testSetAndResetNorthInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + [expectation fulfill]; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + if (mapView.direction != 0.0) { + NSLog(@"Reset to north"); + [mapView resetNorth]; + } + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; +} + +- (void)testInterruptingAndResetNorthOnlyOnceInIsChanging { + + // Reset to non-zero, prior to testing + [self.mapView setDirection:45 animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block BOOL startedReset = NO; + __block BOOL finishedReset = NO; + + self.regionIsChanging = ^(MGLMapView *mapView) { + MBCameraTransitionTests *strongSelf = weakself; + if (!strongSelf) return; + + if (!startedReset) { + NSLog(@"Reset to north, interrupting the previous transition"); + startedReset = YES; + [mapView resetNorth]; + finishedReset = YES; + } + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + MBCameraTransitionTests *strongSelf = weakself; + if (!strongSelf) return; + + MGLTestAssert(strongSelf, startedReset); + + if (finishedReset) { + MGLTestAssert(strongSelf, !(reason & MGLCameraChangeReasonTransitionCancelled)); + [expectation fulfill]; + } + else { + MGLTestAssert(strongSelf, reason & MGLCameraChangeReasonTransitionCancelled); + } + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +- (void)testSetCenterCancelsTransitions { + XCTestExpectation *cameraIsInDCExpectation = [self expectationWithDescription:@"camera reset to DC"]; + + CLLocationCoordinate2D dc = CLLocationCoordinate2DMake(38.894368, -77.036487); + CLLocationCoordinate2D dc_west = CLLocationCoordinate2DMake(38.894368, -77.076487); + + double zoomLevel = 15.0; + + [self.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO]; + [self.mapView setCenterCoordinate:dc_west zoomLevel:zoomLevel animated:YES]; + + __weak typeof(self) weakself = self; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.15 * NSEC_PER_SEC), + dispatch_get_main_queue(), + ^{ + MBCameraTransitionTests *strongSelf = weakself; + + [strongSelf.mapView setCenterCoordinate:dc zoomLevel:zoomLevel animated:NO]; + MGLTestAssertEqualWithAccuracy(strongSelf, + dc.latitude, + strongSelf.mapView.centerCoordinate.latitude, + 0.0005, + @"setting center coordinate should cancel transitions"); + MGLTestAssertEqualWithAccuracy(strongSelf, + dc.longitude, + strongSelf.mapView.centerCoordinate.longitude, + 0.0005, + @"setting center coordinate should cancel transitions"); + [cameraIsInDCExpectation fulfill]; + }); + + [self waitForExpectations:@[cameraIsInDCExpectation] timeout:10.0]; +} + +- (void)testSetCenterCoordinateInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + + CLLocationCoordinate2D target = CLLocationCoordinate2DMake(40.0, 40.0); + CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(-40.0, -40.0); + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + + CLLocationCoordinate2D center = mapView.centerCoordinate; + + switch(delegateCallCount) { + case 0: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target"); + + // Now set another coordinate. + // Should take MGLAnimationDuration seconds (0.3s) + [mapView setCenterCoordinate:target2 animated:YES]; + break; + } + + case 1: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target2"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target2"); + break; + + } + + default: + MGLTestFail(strongSelf); + break; + } + + delegateCallCount++; + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration seconds (0.3) + [self.mapView setCenterCoordinate:target zoomLevel:15.0 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; +} + +- (void)testFlyToCameraInDelegateMethod { + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + expectation.expectedFulfillmentCount = 3; + expectation.assertForOverFulfill = YES; + + CLLocationCoordinate2D target = CLLocationCoordinate2DMake(40.0, 40.0); + CLLocationCoordinate2D target2 = CLLocationCoordinate2DMake(30.0, 30.0); + + __block BOOL runloop = YES; + + NSTimeInterval stop0 = CACurrentMediaTime(); + __block NSTimeInterval stop1 = 0.0; + __block NSTimeInterval stop2 = 0.0; + + double zoomLevel = 5.0; + double altitude = MGLAltitudeForZoomLevel(zoomLevel, 0.0, target.latitude, self.mapView.frame.size); + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MBCameraTransitionTests *strongSelf = weakself; + + if (!strongSelf) return; + + MGLTestAssert(strongSelf, mapView.userTrackingMode != MGLUserTrackingModeFollowWithHeading); + + CLLocationCoordinate2D center = mapView.centerCoordinate; + + switch(delegateCallCount) { + case 0: + { + stop1 = CACurrentMediaTime(); + + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target"); + + // Now set another coordinate. + MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:target2 + fromDistance:altitude + pitch:0.0 + heading:0.0]; + + // flyToCamera can take a while... + [mapView flyToCamera:camera completionHandler:^{ + MGLTestAssert(strongSelf, !runloop, @"Completion block should be called after delegate method"); + [expectation fulfill]; + stop2 = CACurrentMediaTime(); + }]; + break; + } + + case 1: + { + // Our center coordinate should match our target (assuming we're not + // constrained by zoom level) + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.longitude, + center.longitude, + 0.0005, + @"center coordinate longitude should be at target2"); + + MGLTestAssertEqualWithAccuracy(strongSelf, + target2.latitude, + center.latitude, + 0.0005, + @"center coordinate latitude should be at target2"); + + runloop = NO; + break; + } + + default: + MGLTestFail(strongSelf); + break; + } + + delegateCallCount++; + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration + [self.mapView setCenterCoordinate:target zoomLevel:zoomLevel animated:YES]; + + // Run the loop, so the camera can fly to the new camera + while (runloop) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + [self waitForExpectations:@[expectation] timeout:0.5]; + + NSLog(@"setCenterCoordinate: %0.4fs", stop1 - stop0); + NSLog(@"flyToCamera: %0.4fs", stop2 - stop1); + + XCTAssert(delegateCallCount == 2, @"Expecting 2 regionDidChange callbacks, got %ld", delegateCallCount); // Once for the setDirection and once for the reset north +} + +#pragma mark - Pending tests + +- (void)disabled_testContinuallyResettingNorthInIsChangingPENDING { + + // See https://github.com/mapbox/mapbox-gl-native/pull/11614 + // This test currently fails, unsurprisingly, since we're continually + // setting the camera to the same parameters during its update. + // + // Possible solutions/expectations: + // - If you set camera parameters that match the *current* target parameters + // then the transition could be a no-op. We'd need to consider any completion + // block + // - Ideally we would detect this case and disallow it. + + // Reset to non-zero, prior to testing + [self.mapView setDirection:45 animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + self.regionIsChanging = ^(MGLMapView *mapView) { + [mapView resetNorth]; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + [expectation fulfill]; + }; + + [self.mapView setDirection:90 animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +- (void)disabled_testContinuallySettingCoordinateInIsChangingPENDING { + // See above comment in `-disabled_testContinuallyResettingNorthInIsChangingPENDING` + + // Reset to non-zero, prior to testing + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(0.0, 0.0) animated:NO]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 2; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + + self.regionIsChanging = ^(MGLMapView *mapView) { + [weakself.mapView setCenterCoordinate:CLLocationCoordinate2DMake(-40.0, -40.0) animated:YES]; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + [expectation fulfill]; + }; + + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(40.0, 40.0) animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.5]; + + XCTAssertEqualWithAccuracy(self.mapView.direction, 0.0, 0.001, @"Camera should have reset to north. %0.3f", self.mapView.direction); +} + +@end diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h index ab5d2cc46f..6c04ed9f84 100644 --- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h +++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h @@ -1,18 +1,23 @@ #import #import -#define TestFailWithSelf(myself, ...) \ +#define MGLTestFail(myself, ...) \ _XCTPrimitiveFail(myself, __VA_ARGS__) +#define MGLTestAssert(myself, expression, ...) \ + _XCTPrimitiveAssertTrue(myself, expression, @#expression, __VA_ARGS__) + +#define MGLTestAssertEqualWithAccuracy(myself, expression1, expression2, accuracy, ...) \ + _XCTPrimitiveAssertEqualWithAccuracy(myself, expression1, @#expression1, expression2, @#expression2, accuracy, @#accuracy, __VA_ARGS__) + @interface MGLMapViewIntegrationTest : XCTestCase @property (nonatomic) MGLMapView *mapView; @property (nonatomic) MGLStyle *style; @property (nonatomic) XCTestExpectation *styleLoadingExpectation; @property (nonatomic) XCTestExpectation *renderFinishedExpectation; -@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, BOOL animated); +@property (nonatomic) void (^regionWillChange)(MGLMapView *mapView, BOOL animated); @property (nonatomic) void (^regionIsChanging)(MGLMapView *mapView); - - +@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated); // Utility methods - (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout; diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m index fc3229c83b..c42b8eef89 100644 --- a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m +++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m @@ -45,9 +45,9 @@ self.renderFinishedExpectation = nil; } -- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated { - if (self.regionDidChange) { - self.regionDidChange(mapView, animated); +- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated { + if (self.regionWillChange) { + self.regionWillChange(mapView, animated); } } @@ -57,6 +57,12 @@ } } +- (void)mapView:(MGLMapView *)mapView regionDidChangeWithReason:(MGLCameraChangeReason)reason animated:(BOOL)animated { + if (self.regionDidChange) { + self.regionDidChange(mapView, reason, animated); + } +} + #pragma mark - Utilities - (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout { diff --git a/platform/ios/Integration Tests/MGLShapeSourceTests.m b/platform/ios/Integration Tests/MGLShapeSourceTests.m new file mode 100644 index 0000000000..088a9b011e --- /dev/null +++ b/platform/ios/Integration Tests/MGLShapeSourceTests.m @@ -0,0 +1,126 @@ +// +// MBShapeSourceTests.m +// integration +// +// Created by Julian Rex on 4/5/18. +// Copyright © 2018 Mapbox. All rights reserved. +// + +#import "MGLMapViewIntegrationTest.h" + +@interface MGLShapeSourceTests : MGLMapViewIntegrationTest +@end + +@implementation MGLShapeSourceTests + +- (void)testSettingShapeSourceToNilInRegionDidChange { + + NSMutableArray *features = [[NSMutableArray alloc] init]; + + for (NSUInteger i = 0; i <= 180; i+=5) { + CLLocationCoordinate2D coord[4] = { + CLLocationCoordinate2DMake(round(0), round(i)), + CLLocationCoordinate2DMake(round(20), round(i)), + CLLocationCoordinate2DMake(round(0), round(i / 2 )), + CLLocationCoordinate2DMake(round(20), round(i / 2))}; + + MGLPolygonFeature *feature = [MGLPolygonFeature polygonWithCoordinates:coord count:4]; + [features addObject:feature]; + } + + MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"source" features:features options:nil]; + [self.style addSource:shapeSource]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layer" source:shapeSource]; + layer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; + [self.style addLayer:layer]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __weak typeof(self) weakself = self; + __block NSInteger delegateCallCount = 0; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + MGLShapeSourceTests *strongSelf = weakself; + + if (!strongSelf) + return; + + delegateCallCount++; + + // Setting the shapeSource.shape = nil, was causing an infinite loop, so here + // we check for a runaway call. 10 here is arbitrary. We could argue that this + // should check that the call count is only 1, however in this case we particularly + // want to check for the infinite loop. + // See https://github.com/mapbox/mapbox-gl-native/issues/11180 + + if (delegateCallCount > 10) { + MGLTestFail(strongSelf); + } + else { + shapeSource.shape = nil; + } + + [expectation fulfill]; + }; + + // setCenterCoordinate is NOT animated here. + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0)]; + [self waitForExpectations:@[expectation] timeout:5.0]; +} + +- (void)testSettingShapeSourceToNilInRegionIsChanging { + + NSMutableArray *features = [[NSMutableArray alloc] init]; + + for (NSUInteger i = 0; i <= 180; i+=5) { + CLLocationCoordinate2D coord[4] = { + CLLocationCoordinate2DMake(round(0), round(i)), + CLLocationCoordinate2DMake(round(20), round(i)), + CLLocationCoordinate2DMake(round(0), round(i / 2 )), + CLLocationCoordinate2DMake(round(20), round(i / 2))}; + + MGLPolygonFeature *feature = [MGLPolygonFeature polygonWithCoordinates:coord count:4]; + [features addObject:feature]; + } + + MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"source" features:features options:nil]; + [self.style addSource:shapeSource]; + + MGLFillStyleLayer *layer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"layer" source:shapeSource]; + layer.fillOpacity = [NSExpression expressionForConstantValue:@0.5]; + [self.style addLayer:layer]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"regionDidChange expectation"]; + expectation.expectedFulfillmentCount = 1; + expectation.assertForOverFulfill = YES; + + __block NSInteger delegateCallCount = 0; + __weak typeof(self) weakself = self; + + self.regionIsChanging = ^(MGLMapView *mapView) { + // See https://github.com/mapbox/mapbox-gl-native/issues/11180 + shapeSource.shape = nil; + }; + + self.regionDidChange = ^(MGLMapView *mapView, MGLCameraChangeReason reason, BOOL animated) { + + delegateCallCount++; + + if (delegateCallCount > 1) { + MGLTestFail(weakself); + } + + [expectation fulfill]; + }; + + // Should take MGLAnimationDuration seconds (0.3) + [self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(10.0, 10.0) animated:YES]; + [self waitForExpectations:@[expectation] timeout:1.0]; +} + + +@end diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 78cfff965c..aef2552b0b 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -363,8 +363,10 @@ AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; }; AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; }; + CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */; }; CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */; }; CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */; }; + CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */; }; CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; }; CAA69DA4206DCD0E007279CD /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4A26961CB6E795000B7809 /* Mapbox.framework */; }; @@ -996,9 +998,11 @@ 96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = ""; }; AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = ""; }; AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = ""; }; + CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLShapeSourceTests.m; sourceTree = ""; }; CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = ""; }; CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapViewIntegrationTest.h; sourceTree = ""; }; CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLStyleLayerIntegrationTests.m; sourceTree = ""; }; + CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCameraTransitionTests.mm; sourceTree = ""; }; CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = ""; }; DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = ""; }; DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = ""; }; @@ -1353,6 +1357,8 @@ children = ( 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */, 16376B0B1FFD9DAF0000563E /* Info.plist */, + CA34C9C2207FD272005C1A06 /* MGLCameraTransitionTests.mm */, + CA0C27912076C804001CE5B7 /* MGLShapeSourceTests.m */, CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */, CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */, CA4EB8C620863487006AB465 /* MGLStyleLayerIntegrationTests.m */, @@ -2809,8 +2815,10 @@ buildActionMask = 2147483647; files = ( CA4EB8C720863487006AB465 /* MGLStyleLayerIntegrationTests.m in Sources */, + CA34C9C3207FD272005C1A06 /* MGLCameraTransitionTests.mm in Sources */, 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */, CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */, + CA0C27922076C804001CE5B7 /* MGLShapeSourceTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3379,6 +3387,7 @@ /* Begin XCBuildConfiguration section */ 16376B0E1FFD9DAF0000563E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3393,6 +3402,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = "Integration Tests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; @@ -3404,6 +3414,7 @@ }; 16376B0F1FFD9DAF0000563E /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 55D8C9941D0F133500F42F10 /* config.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -3418,6 +3429,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = gnu11; + HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)"; INFOPLIST_FILE = "Integration Tests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.mapbox.integration-tests"; diff --git a/platform/ios/src/MGLCameraChangeReason.h b/platform/ios/src/MGLCameraChangeReason.h index 6c6b3636ba..f439d3e7ea 100644 --- a/platform/ios/src/MGLCameraChangeReason.h +++ b/platform/ios/src/MGLCameraChangeReason.h @@ -57,5 +57,9 @@ typedef NS_OPTIONS(NSUInteger, MGLCameraChangeReason) MGLCameraChangeReasonGestureOneFingerZoom = 1 << 7, // :nodoc: The user panned with two fingers to tilt the map (two finger drag). - MGLCameraChangeReasonGestureTilt = 1 << 8 + MGLCameraChangeReasonGestureTilt = 1 << 8, + + // :nodoc: Cancelled + MGLCameraChangeReasonTransitionCancelled = 1 << 16 + }; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 26b23abb4e..e16411f2c5 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -1271,7 +1271,7 @@ public: { [self setUserTrackingMode:MGLUserTrackingModeNone animated:NO]; } - _mbglMap->cancelTransitions(); + [self cancelTransitions]; } - (void)notifyGestureDidBegin { @@ -1321,7 +1321,7 @@ public: { if ( ! self.isScrollEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; MGLMapCamera *oldCamera = self.camera; @@ -1390,7 +1390,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGPoint centerPoint = [self anchorPointForGesture:pinch]; MGLMapCamera *oldCamera = self.camera; @@ -1491,7 +1491,7 @@ public: { if ( ! self.isRotateEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGPoint centerPoint = [self anchorPointForGesture:rotate]; MGLMapCamera *oldCamera = self.camera; @@ -1696,7 +1696,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; if (doubleTap.state == UIGestureRecognizerStateEnded) { @@ -1737,7 +1737,7 @@ public: if (_mbglMap->getZoom() == _mbglMap->getMinZoom()) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureZoomOut; @@ -1776,7 +1776,7 @@ public: { if ( ! self.isZoomEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureOneFingerZoom; @@ -1821,7 +1821,7 @@ public: { if ( ! self.isPitchEnabled) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonGestureTilt; @@ -2981,7 +2981,7 @@ public: return; } - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3006,7 +3006,7 @@ public: - (void)setZoomLevel:(double)zoomLevel animated:(BOOL)animated { if (zoomLevel == self.zoomLevel) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3149,7 +3149,7 @@ public: } [self willChangeValueForKey:@"visibleCoordinateBounds"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3182,7 +3182,7 @@ public: - (void)_setDirection:(CLLocationDirection)direction animated:(BOOL)animated { if (direction == self.direction) return; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; CGFloat duration = animated ? MGLAnimationDuration : 0; @@ -3271,7 +3271,7 @@ public: } [self willChangeValueForKey:@"camera"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3330,7 +3330,7 @@ public: } [self willChangeValueForKey:@"camera"]; - _mbglMap->cancelTransitions(); + [self cancelTransitions]; self.cameraChangeReasonBitmask |= MGLCameraChangeReasonProgrammatic; @@ -3339,6 +3339,12 @@ public: [self didChangeValueForKey:@"camera"]; } +- (void)cancelTransitions { + self.cameraChangeReasonBitmask |= MGLCameraChangeReasonTransitionCancelled; + _mbglMap->cancelTransitions(); + self.cameraChangeReasonBitmask &= ~MGLCameraChangeReasonTransitionCancelled; +} + - (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds { return [self cameraThatFitsCoordinateBounds:bounds edgePadding:UIEdgeInsetsZero]; diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md index 0b3db6becb..c711f6f352 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -13,6 +13,7 @@ ### Other changes * Unknown tokens in URLs are now preserved, rather than replaced with an empty string. ([#11787](https://github.com/mapbox/mapbox-gl-native/issues/11787)) +* Adjusted when and how the camera transition update and finish callbacks are called, fixing recursion bugs. ([#11614](https://github.com/mapbox/mapbox-gl-native/pull/11614)) ## 0.7.1 diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 105adf0400..da8e243d91 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -594,13 +594,10 @@ void Transform::startTransition(const CameraOptions& camera, animation.transitionFrameFn(t); } observer.onCameraIsChanging(); + return false; } else { - transitionFinishFn(); - transitionFinishFn = nullptr; - - // This callback gets destroyed here, - // we can only return after this point. - transitionFrameFn = nullptr; + // Indicate that we need to terminate this transition + return true; } }; @@ -615,7 +612,14 @@ void Transform::startTransition(const CameraOptions& camera, }; if (!isAnimated) { - transitionFrameFn(Clock::now()); + auto update = std::move(transitionFrameFn); + auto finish = std::move(transitionFinishFn); + + transitionFrameFn = nullptr; + transitionFinishFn = nullptr; + + update(Clock::now()); + finish(); } } @@ -624,8 +628,43 @@ bool Transform::inTransition() const { } void Transform::updateTransitions(const TimePoint& now) { - if (transitionFrameFn) { - transitionFrameFn(now); + + // Use a temporary function to ensure that the transitionFrameFn lambda is + // called only once per update. + + // This addresses the symptoms of https://github.com/mapbox/mapbox-gl-native/issues/11180 + // where setting a shape source to nil (or similar) in the `onCameraIsChanging` + // observer function causes `Map::Impl::onUpdate()` to be called which + // in turn calls this function (before the current iteration has completed), + // leading to an infinite loop. See https://github.com/mapbox/mapbox-gl-native/issues/5833 + // for a similar, related, issue. + // + // By temporarily nulling the `transitionFrameFn` (and then restoring it + // after the temporary has been called) we stop this recursion. + // + // It's important to note that the scope of this change is stop the above + // crashes. It doesn't address any potential deeper issue (for example + // user error, how often and when transition callbacks are called). + + auto transition = std::move(transitionFrameFn); + transitionFrameFn = nullptr; + + if (transition && transition(now)) { + // If the transition indicates that it is complete, then we should call + // the finish lambda (going via a temporary as above) + auto finish = std::move(transitionFinishFn); + + transitionFinishFn = nullptr; + transitionFrameFn = nullptr; + + if (finish) { + finish(); + } + } else if (!transitionFrameFn) { + // We have to check `transitionFrameFn` is nil here, since a new transition + // may have been triggered in a user callback (from the transition call + // above) + transitionFrameFn = std::move(transition); } } diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index d429c57661..bff44a2dcd 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -165,7 +165,7 @@ private: TimePoint transitionStart; Duration transitionDuration; - std::function transitionFrameFn; + std::function transitionFrameFn; std::function transitionFinishFn; }; -- cgit v1.2.1 From 78fdf5c7716ee7c79800b51713f30f1772514dc6 Mon Sep 17 00:00:00 2001 From: Asheem Mamoowala Date: Mon, 21 May 2018 14:59:54 -0700 Subject: Clamp TileJSON.bounds latitudes to [-90, 90] (#11964) Fixes #11963. --- src/mbgl/style/conversion/tileset.cpp | 17 +++++++++-------- test/style/conversion/tileset.test.cpp | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp index a2c4aa80b3..15ed10a90f 100644 --- a/src/mbgl/style/conversion/tileset.cpp +++ b/src/mbgl/style/conversion/tileset.cpp @@ -1,14 +1,11 @@ #include #include +#include namespace mbgl { namespace style { namespace conversion { -bool validateLatitude(const double lat) { - return lat <= 90 && lat >= -90; -} - optional Converter::operator()(const Convertible& value, Error& error) const { Tileset result; @@ -95,16 +92,20 @@ optional Converter::operator()(const Convertible& value, Error error = { "bounds array must contain numeric longitude and latitude values" }; return {}; } - if (!validateLatitude(*bottom) || !validateLatitude(*top) || top <= bottom){ - error = { "bounds latitude values must be between -90 and 90 with bottom less than top" }; + + bottom = util::clamp(*bottom, -90.0, 90.0); + top = util::clamp(*top, -90.0, 90.0); + if (top <= bottom){ + error = { "bounds bottom latitude must be between smaller than top latitude" }; return {}; } + if(*left >= *right) { error = { "bounds left longitude should be less than right longitude" }; return {}; } - *left = util::max(-180.0, *left); - *right = util::min(180.0, *right); + left = util::max(-180.0, *left); + right = util::min(180.0, *right); result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right }); } diff --git a/test/style/conversion/tileset.test.cpp b/test/style/conversion/tileset.test.cpp index f10aa0e318..f405fb1361 100644 --- a/test/style/conversion/tileset.test.cpp +++ b/test/style/conversion/tileset.test.cpp @@ -66,7 +66,7 @@ TEST(Tileset, BoundsAreClamped) { Error error; mbgl::optional converted = convertJSON(R"JSON({ "tiles": ["http://mytiles"], - "bounds": [-181.0000005,-90,180.00000000000006,90] + "bounds": [-181.0000005,-90.000000006,180.00000000000006,91] })JSON", error); EXPECT_TRUE((bool) converted); EXPECT_EQ(converted->bounds, LatLngBounds::hull({90, -180}, {-90, 180})); -- cgit v1.2.1 From d5aff7182d6caca9c69514df5fa90bfc203cb5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 18 May 2018 17:44:59 +0200 Subject: [node] prevent race condition for renderFinished --- platform/node/src/node_map.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 9b76f0f542..521cca1a51 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -447,6 +447,12 @@ void NodeMap::startRender(NodeMap::RenderOptions options) { } void NodeMap::renderFinished() { + if (!callback) { + // In some situations, the render finishes at the same time as we call cancel. Make sure + // we are only finishing a render once. + return; + } + Nan::HandleScope scope; // We're done with this render call, so we're unrefing so that the loop could close. -- cgit v1.2.1 From 973aa963f917df2c20a05dfea1b3af7314677e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 20 Apr 2018 16:40:17 +0300 Subject: [node] move to Nan 2.10.0 --- package.json | 2 +- platform/node/src/node_expression.cpp | 2 +- platform/node/src/node_map.cpp | 38 +++++++++++++++++++++++++---------- platform/node/src/node_map.hpp | 4 +++- platform/node/src/node_request.cpp | 3 ++- 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 977cd2b09c..c3f0d7b8ae 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "~2.8", + "nan": "~2.10", "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp index 27866ccbed..9faa41d8b8 100644 --- a/platform/node/src/node_expression.cpp +++ b/platform/node/src/node_expression.cpp @@ -52,7 +52,7 @@ type::Type parseType(v8::Local type) { v8::Local Nkey = Nan::New("N").ToLocalChecked(); if (Nan::Has(type, Nkey).FromMaybe(false)) { - N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value(); + N = Nan::To(Nan::Get(type, Nkey).ToLocalChecked()).ToLocalChecked()->Value(); } return type::Array(itemType, N); } diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 521cca1a51..bc40d56f44 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -353,6 +353,18 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local obj) { return options; } +class RenderRequest : public Nan::AsyncResource { +public: + RenderRequest(v8::Local callback_) : AsyncResource("mbgl:RenderRequest") { + callback.Reset(callback_); + } + ~RenderRequest() { + callback.Reset(); + } + + Nan::Persistent callback; +}; + /** * Render an image from the currently-loaded style * @@ -385,15 +397,16 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo& info) { return Nan::ThrowTypeError("Style is not loaded"); } - if (nodeMap->callback) { + if (nodeMap->req) { return Nan::ThrowError("Map is currently rendering an image"); } try { auto options = ParseOptions(Nan::To(info[0]).ToLocalChecked()); - assert(!nodeMap->callback); + assert(!nodeMap->req); assert(!nodeMap->image.data); - nodeMap->callback = std::make_unique(info[1].As()); + nodeMap->req = std::make_unique(Nan::To(info[1]).ToLocalChecked()); + nodeMap->startRender(std::move(options)); } catch (mbgl::style::conversion::Error& err) { return Nan::ThrowTypeError(err.message.c_str()); @@ -463,14 +476,17 @@ void NodeMap::renderFinished() { Unref(); // Move the callback and image out of the way so that the callback can start a new render call. - auto cb = std::move(callback); + auto request = std::move(req); auto img = std::move(image); - assert(cb); + assert(request); // These have to be empty to be prepared for the next render call. - assert(!callback); + assert(!req); assert(!image.data); + v8::Local callback = Nan::New(request->callback); + v8::Local target = Nan::New(); + if (error) { std::string errorMessage; @@ -488,7 +504,7 @@ void NodeMap::renderFinished() { error = nullptr; assert(!error); - cb->Call(1, argv); + request->runInAsyncScope(target, callback, 1, argv); } else if (img.data) { v8::Local pixels = Nan::NewBuffer( reinterpret_cast(img.data.get()), img.bytes(), @@ -504,12 +520,12 @@ void NodeMap::renderFinished() { Nan::Null(), pixels }; - cb->Call(2, argv); + request->runInAsyncScope(target, callback, 2, argv); } else { v8::Local argv[] = { Nan::Error("Didn't get an image") }; - cb->Call(1, argv); + request->runInAsyncScope(target, callback, 1, argv); } } @@ -552,7 +568,7 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo& info) { auto nodeMap = Nan::ObjectWrap::Unwrap(info.Holder()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); - if (!nodeMap->callback) return Nan::ThrowError("No render in progress"); + if (!nodeMap->req) return Nan::ThrowError("No render in progress"); try { nodeMap->cancel(); @@ -1198,7 +1214,7 @@ std::unique_ptr NodeMap::request(const mbgl::Resource& resou Nan::New(&callback_) }; - auto instance = Nan::New(NodeRequest::constructor)->NewInstance(2, argv); + auto instance = Nan::NewInstance(Nan::New(NodeRequest::constructor), 2, argv).ToLocalChecked(); Nan::Set(instance, Nan::New("url").ToLocalChecked(), Nan::New(resource.url).ToLocalChecked()); Nan::Set(instance, Nan::New("kind").ToLocalChecked(), Nan::New(resource.kind)); diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 7fe23ad86a..19df095481 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -25,6 +25,8 @@ class NodeMapObserver : public mbgl::MapObserver { void onDidFailLoadingMap(std::exception_ptr) override; }; +class RenderRequest; + class NodeMap : public Nan::ObjectWrap, public mbgl::FileSource { public: @@ -84,7 +86,7 @@ public: std::exception_ptr error; mbgl::PremultipliedImage image; - std::unique_ptr callback; + std::unique_ptr req; // Async for delivering the notifications of render completion. uv_async_t *async; diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp index de16710f78..8c26d44583 100644 --- a/platform/node/src/node_request.cpp +++ b/platform/node/src/node_request.cpp @@ -124,7 +124,8 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo& inf void NodeRequest::Execute() { v8::Local argv[] = { handle() }; - Nan::MakeCallback(Nan::To(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); + Nan::AsyncResource res("mbgl:execute"); + res.runInAsyncScope(Nan::To(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); } NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) { -- cgit v1.2.1 From d1a266b3e68d052d1478382795598bf2ff28da6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Fri, 20 Apr 2018 16:40:17 +0300 Subject: [node] allow building all ABIs at once --- Makefile | 16 +- cmake/NodeJS.cmake | 600 -------------------------------------------- cmake/loop-uv.cmake | 14 ++ cmake/mbgl.cmake | 31 ++- cmake/node.cmake | 121 ++++----- package.json | 2 +- platform/macos/config.cmake | 4 +- platform/node/index.js | 3 +- 8 files changed, 122 insertions(+), 669 deletions(-) delete mode 100644 cmake/NodeJS.cmake create mode 100644 cmake/loop-uv.cmake diff --git a/Makefile b/Makefile index 576312cabf..e54fe6313b 100644 --- a/Makefile +++ b/Makefile @@ -111,8 +111,7 @@ run-benchmark-%: benchmark $(MACOS_OUTPUT_PATH)/$(BUILDTYPE)/mbgl-benchmark --benchmark_filter=$* ${BENCHMARK_ARGS} .PHONY: node-benchmark -node-benchmark: $(MACOS_PROJ_PATH) - set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'node-benchmark' build $(XCPRETTY) +node-benchmark: node .PHONY: run-node-benchmark run-node-benchmark: node-benchmark @@ -136,7 +135,11 @@ offline: $(MACOS_PROJ_PATH) .PHONY: node node: $(MACOS_PROJ_PATH) - set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node' build $(XCPRETTY) + set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node (Active ABI)' build $(XCPRETTY) + +.PHONY: node-all +node-all: $(MACOS_PROJ_PATH) + set -o pipefail && $(MACOS_XCODEBUILD) -scheme 'mbgl-node (All ABIs)' build $(XCPRETTY) .PHONY: macos-test macos-test: $(MACOS_PROJ_PATH) @@ -362,7 +365,11 @@ run-glfw-app: glfw-app .PHONY: node node: $(LINUX_BUILD) - $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node + $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.active + +.PHONY: node-all +node-all: $(LINUX_BUILD) + $(NINJA) $(NINJA_ARGS) -j$(JOBS) -C $(LINUX_OUTPUT_PATH) mbgl-node.all .PHONY: compdb compdb: $(LINUX_BUILD) @@ -706,6 +713,7 @@ codestyle: .PHONY: clean clean: -rm -rf ./build \ + ./lib/*.node \ ./platform/android/gradle/configuration.gradle \ ./platform/android/MapboxGLAndroidSDK/build \ ./platform/android/MapboxGLAndroidSDK/.externalNativeBuild \ diff --git a/cmake/NodeJS.cmake b/cmake/NodeJS.cmake deleted file mode 100644 index 8e0ec56982..0000000000 --- a/cmake/NodeJS.cmake +++ /dev/null @@ -1,600 +0,0 @@ -# NOTE: We're using a patched version of the original https://github.com/cjntaylor/node-cmake - -# Our version is in https://github.com/mapbox/node-cmake/blob/mapbox-gl-native/NodeJS.cmake and -# contains these patches: -# - https://github.com/cjntaylor/node-cmake/pull/20 -# - https://github.com/cjntaylor/node-cmake/pull/22 -# - https://github.com/cjntaylor/node-cmake/pull/23 - -# Defaults for standard Node.js builds -set(NODEJS_DEFAULT_URL https://nodejs.org/download/release) -set(NODEJS_DEFAULT_VERSION installed) -set(NODEJS_VERSION_FALLBACK latest) -set(NODEJS_DEFAULT_NAME node) -set(NODEJS_DEFAULT_CHECKSUM SHASUMS256.txt) -set(NODEJS_DEFAULT_CHECKTYPE SHA256) - -include(CMakeParseArguments) - -# Find a path by walking upward from a base directory until the path is -# found. Sets the variable ${PATH} to False if the path can't -# be determined -function(find_path_parent NAME BASE PATH) - set(ROOT ${BASE}) - set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) - set(DRIVE "^[A-Za-z]?:?/$") - while(NOT ROOT MATCHES ${DRIVE} AND NOT EXISTS ${ROOT}/${NAME}) - get_filename_component(ROOT ${ROOT} DIRECTORY) - set(${PATH} ${ROOT}/${NAME} PARENT_SCOPE) - endwhile() - if(ROOT MATCHES ${DRIVE}) - set(${PATH} False PARENT_SCOPE) - endif() -endfunction() - -# Shortcut for finding standard node module locations -macro(find_nodejs_module NAME BASE PATH) - find_path_parent(node_modules/${NAME} ${BASE} ${PATH}) -endmacro() - -# Download with a bit of nice output (without spewing progress) -function(download_file DESCRIPTION URL FILE) - message(STATUS "Downloading: ${URL}") - file(DOWNLOAD - ${URL} - ${FILE}.tmp - ${ARGN} - STATUS RESULT - ) - list(GET RESULT 0 STATUS) - if(STATUS) - list(GET result 1 MESSAGE) - message(FATAL_ERROR "Unable to download ${DESCRIPTION} from ${URL}: ${MESSAGE}") - else() - file(RENAME ${FILE}.tmp ${FILE}) - endif() -endfunction() - -# Embedded win_delay_load_hook file so that this file can be copied -# into projects directly (recommended practice) -function(nodejs_generate_delayload_hook OUTPUT) - file(WRITE ${OUTPUT} "") - file(APPEND ${OUTPUT} "/*\n") - file(APPEND ${OUTPUT} " * When this file is linked to a DLL, it sets up a delay-load hook that\n") - file(APPEND ${OUTPUT} " * intervenes when the DLL is trying to load 'node.exe' or 'iojs.exe'\n") - file(APPEND ${OUTPUT} " * dynamically. Instead of trying to locate the .exe file it'll just return\n") - file(APPEND ${OUTPUT} " * a handle to the process image.\n") - file(APPEND ${OUTPUT} " *\n") - file(APPEND ${OUTPUT} " * This allows compiled addons to work when node.exe or iojs.exe is renamed.\n") - file(APPEND ${OUTPUT} " */\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifdef _MSC_VER\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifndef DELAYIMP_INSECURE_WRITABLE_HOOKS\n") - file(APPEND ${OUTPUT} "#define DELAYIMP_INSECURE_WRITABLE_HOOKS\n") - file(APPEND ${OUTPUT} "#endif\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#ifndef WIN32_LEAN_AND_MEAN\n") - file(APPEND ${OUTPUT} "#define WIN32_LEAN_AND_MEAN\n") - file(APPEND ${OUTPUT} "#endif\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#include \n") - file(APPEND ${OUTPUT} "#include \n") - file(APPEND ${OUTPUT} "#include \n") - file(APPEND ${OUTPUT} "#include \n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) {\n") - file(APPEND ${OUTPUT} " if (event != dliNotePreLoadLibrary) return NULL;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " if (_stricmp(info->szDll, \"iojs.exe\") != 0 &&\n") - file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.exe\") != 0 &&\n") - file(APPEND ${OUTPUT} " _stricmp(info->szDll, \"node.dll\") != 0)\n") - file(APPEND ${OUTPUT} " return NULL;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get a handle to the current process executable.\n") - file(APPEND ${OUTPUT} " HMODULE processModule = GetModuleHandle(NULL);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get the path to the executable.\n") - file(APPEND ${OUTPUT} " TCHAR processPath[_MAX_PATH];\n") - file(APPEND ${OUTPUT} " GetModuleFileName(processModule, processPath, _MAX_PATH);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Get the name of the current executable.\n") - file(APPEND ${OUTPUT} " LPSTR processName = PathFindFileName(processPath);\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // If the current process is node or iojs, then just return the proccess \n") - file(APPEND ${OUTPUT} " // module.\n") - file(APPEND ${OUTPUT} " if (_stricmp(processName, \"node.exe\") == 0 ||\n") - file(APPEND ${OUTPUT} " _stricmp(processName, \"iojs.exe\") == 0) {\n") - file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") - file(APPEND ${OUTPUT} " }\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // If it is another process, attempt to load 'node.dll' from the same \n") - file(APPEND ${OUTPUT} " // directory.\n") - file(APPEND ${OUTPUT} " PathRemoveFileSpec(processPath);\n") - file(APPEND ${OUTPUT} " PathAppend(processPath, \"node.dll\");\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " HMODULE nodeDllModule = GetModuleHandle(processPath);\n") - file(APPEND ${OUTPUT} " if(nodeDllModule != NULL) {\n") - file(APPEND ${OUTPUT} " // This application has a node.dll in the same directory as the executable,\n") - file(APPEND ${OUTPUT} " // use that.\n") - file(APPEND ${OUTPUT} " return (FARPROC) nodeDllModule;\n") - file(APPEND ${OUTPUT} " }\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} " // Fallback to the current executable, which must statically link to \n") - file(APPEND ${OUTPUT} " // node.lib\n") - file(APPEND ${OUTPUT} " return (FARPROC) processModule;\n") - file(APPEND ${OUTPUT} "}\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "PfnDliHook __pfnDliNotifyHook2 = load_exe_hook;\n") - file(APPEND ${OUTPUT} "\n") - file(APPEND ${OUTPUT} "#endif\n") -endfunction() - -# Sets up a project to build Node.js native modules -# - Downloads required dependencies and unpacks them to the build directory. -# Internet access is required the first invocation but not after ( -# provided the download is successful) -# - Sets up several variables for building against the downloaded -# dependencies -# - Guarded to prevent multiple executions, so a single project hierarchy -# will only call this once -function(nodejs_init) - # Prevents this function from executing more than once - if(NODEJS_INIT) - return() - endif() - - # Regex patterns used by the init function for component extraction - set(HEADERS_MATCH "^([A-Fa-f0-9]+)[ \t]+([^-]+)-(headers|v?[0-9.]+)-(headers|v?[0-9.]+)([.]tar[.]gz)$") - set(LIB32_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-x86)?(/)?([^/]*)(.lib)$") - set(LIB64_MATCH "(^[0-9A-Fa-f]+)[\t ]+(win-)?(x64/)(.*)(.lib)$") - - # Parse function arguments - cmake_parse_arguments(nodejs_init - "" "URL;NAME;VERSION;CHECKSUM;CHECKTYPE" "" ${ARGN} - ) - - # Allow the download URL to be overridden by command line argument - # NODEJS_URL - if(NODEJS_URL) - set(URL ${NODEJS_URL}) - else() - # Use the argument if specified, falling back to the default - set(URL ${NODEJS_DEFAULT_URL}) - if(nodejs_init_URL) - set(URL ${nodejs_init_URL}) - endif() - endif() - - # Allow name to be overridden by command line argument NODEJS_NAME - if(NODEJS_NAME) - set(NAME ${NODEJS_NAME}) - else() - # Use the argument if specified, falling back to the default - set(NAME ${NODEJS_DEFAULT_NAME}) - if(nodejs_init_NAME) - set(NAME ${nodejs_init_NAME}) - endif() - endif() - - # Allow the checksum file to be overridden by command line argument - # NODEJS_CHECKSUM - if(NODEJS_CHECKSUM) - set(CHECKSUM ${NODEJS_CHECKSUM}) - else() - # Use the argument if specified, falling back to the default - set(CHECKSUM ${NODEJS_DEFAULT_CHECKSUM}) - if(nodejs_init_CHECKSUM) - set(CHECKSUM ${nodejs_init_CHECKSUM}) - endif() - endif() - - # Allow the checksum type to be overriden by the command line argument - # NODEJS_CHECKTYPE - if(NODEJS_CHECKTYPE) - set(CHECKTYPE ${NODEJS_CHECKTYPE}) - else() - # Use the argument if specified, falling back to the default - set(CHECKTYPE ${NODEJS_DEFAULT_CHECKTYPE}) - if(nodejs_init_CHECKTYPE) - set(CHECKTYPE ${nodejs_init_CHECKTYPE}) - endif() - endif() - - # Allow the version to be overridden by the command line argument - # NODEJS_VERSION - if(NODEJS_VERSION) - set(VERSION ${NODEJS_VERSION}) - else() - # Use the argument if specified, falling back to the default - set(VERSION ${NODEJS_DEFAULT_VERSION}) - if(nodejs_init_VERSION) - set(VERSION ${nodejs_init_VERSION}) - endif() - endif() - - # "installed" is a special version that tries to use the currently - # installed version (determined by running node) - set(NODEJS_INSTALLED False CACHE BOOL "Node.js install status" FORCE) - if(VERSION STREQUAL "installed") - if(NOT NAME STREQUAL ${NODEJS_DEFAULT_NAME}) - message(FATAL_ERROR - "'Installed' version identifier can only be used with" - "the core Node.js library" - ) - endif() - # Fall back to the "latest" version if node isn't installed - set(VERSION ${NODEJS_VERSION_FALLBACK}) - find_program(NODEJS_BINARY NAMES nodejs node) - if(NODEJS_BINARY) - execute_process( - COMMAND ${NODEJS_BINARY} --version - RESULT_VARIABLE INSTALLED_VERSION_RESULT - OUTPUT_VARIABLE INSTALLED_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(INSTALLED_VERSION_RESULT STREQUAL "0") - set(NODEJS_INSTALLED True CACHE BOOL - "Node.js install status" FORCE - ) - set(VERSION ${INSTALLED_VERSION}) - endif() - endif() - endif() - - # Create a temporary download directory - set(TEMP ${CMAKE_CURRENT_BINARY_DIR}/temp) - file(MAKE_DIRECTORY ${TEMP}) - - # Unless the target is special version "latest", the parameters - # necessary to construct the root path are known - if(NOT VERSION STREQUAL "latest") - set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) - # Extract checksums from the existing checksum file - set(CHECKSUM_TARGET ${ROOT}/CHECKSUM) - endif() - - # If we're trying to determine the version or we haven't saved the - # checksum file for this version, download it from the specified server - if(VERSION STREQUAL "latest" OR - (DEFINED ROOT AND NOT EXISTS ${ROOT}/CHECKSUM)) - if(DEFINED ROOT) - # Clear away the old checksum in case the new one is different - # and/or it fails to download - file(REMOVE ${ROOT}/CHECKSUM) - endif() - file(REMOVE ${TEMP}/CHECKSUM) - download_file( - "checksum file" - ${URL}/${VERSION}/${CHECKSUM} - ${TEMP}/CHECKSUM - INACTIVITY_TIMEOUT 10 - ) - # Extract checksums from the temporary file - set(CHECKSUM_TARGET ${TEMP}/CHECKSUM) - endif() - - # Extract the version, name, header archive and archive checksum - # from the file. This first extract is what defines / specifies the - # actual version number and name. - file(STRINGS - ${CHECKSUM_TARGET} HEADERS_CHECKSUM - REGEX ${HEADERS_MATCH} - LIMIT_COUNT 1 - ) - if(NOT HEADERS_CHECKSUM) - file(REMOVE ${TEMP}/CHECKSUM) - if(DEFINED ROOT) - file(REMOVE ${ROOT}/CHECKSUM) - endif() - message(FATAL_ERROR "Unable to extract header archive checksum") - endif() - string(REGEX MATCH ${HEADERS_MATCH} HEADERS_CHECKSUM ${HEADERS_CHECKSUM}) - set(HEADERS_CHECKSUM ${CMAKE_MATCH_1}) - set(NAME ${CMAKE_MATCH_2}) - if(CMAKE_MATCH_3 STREQUAL "headers") - set(VERSION ${CMAKE_MATCH_4}) - else() - set(VERSION ${CMAKE_MATCH_3}) - endif() - set(HEADERS_ARCHIVE - ${CMAKE_MATCH_2}-${CMAKE_MATCH_3}-${CMAKE_MATCH_4}${CMAKE_MATCH_5} - ) - # Make sure that the root directory exists, and that the checksum - # file has been moved over from temp - if(DEFINED ROOT) - set(OLD_ROOT ${ROOT}) - endif() - set(ROOT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}/${VERSION}) - if(DEFINED OLD_ROOT AND NOT ROOT STREQUAL "${OLD_ROOT}") - file(REMOVE ${TEMP}/CHECKSUM) - file(REMOVE ${ROOT}/CHECKSUM) - message(FATAL_ERROR "Version/Name mismatch") - endif() - file(MAKE_DIRECTORY ${ROOT}) - if(EXISTS ${TEMP}/CHECKSUM) - file(REMOVE ${ROOT}/CHECKSUM) - file(RENAME ${TEMP}/CHECKSUM ${ROOT}/CHECKSUM) - endif() - - # Now that its fully resolved, report the name and version of Node.js being - # used - message(STATUS "NodeJS: Using ${NAME}, version ${VERSION}") - - # Download the headers for the version being used - # Theoretically, these could be found by searching the installed - # system, but in practice, this can be error prone. They're provided - # on the download servers, so just use the ones there. - if(NOT EXISTS ${ROOT}/include) - file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) - download_file( - "Node.js headers" - ${URL}/${VERSION}/${HEADERS_ARCHIVE} - ${TEMP}/${HEADERS_ARCHIVE} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${HEADERS_CHECKSUM} - ) - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xfz ${TEMP}/${HEADERS_ARCHIVE} - WORKING_DIRECTORY ${TEMP} - ) - - # This adapts the header extraction to support a number of different - # header archive contents in addition to the one used by the - # default Node.js library - unset(NODEJS_HEADERS_PATH CACHE) - find_path(NODEJS_HEADERS_PATH - NAMES src include - PATHS - ${TEMP}/${NAME}-${VERSION}-headers - ${TEMP}/${NAME}-${VERSION} - ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION}-headers - ${TEMP}/${NODEJS_DEFAULT_NAME}-${VERSION} - ${TEMP}/${NODEJS_DEFAULT_NAME} - ${TEMP} - NO_DEFAULT_PATH - ) - if(NOT NODEJS_HEADERS_PATH) - message(FATAL_ERROR "Unable to find extracted headers folder") - endif() - - # Move the headers into a standard location with a standard layout - file(REMOVE ${TEMP}/${HEADERS_ARCHIVE}) - file(REMOVE_RECURSE ${ROOT}/include) - if(EXISTS ${NODEJS_HEADERS_PATH}/include/node) - file(RENAME ${NODEJS_HEADERS_PATH}/include/node ${ROOT}/include) - elseif(EXISTS ${NODEJS_HEADERS_PATH}/src) - file(MAKE_DIRECTORY ${ROOT}/include) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/src) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find core headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/src/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/uv/include) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find libuv headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/uv/include/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/v8/include) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find v8 headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/v8/include/ - DESTINATION ${ROOT}/include - ) - if(NOT EXISTS ${NODEJS_HEADERS_PATH}/deps/zlib) - file(REMOVE_RECURSE ${ROOT}/include) - message(FATAL_ERROR "Unable to find zlib headers") - endif() - file(COPY ${NODEJS_HEADERS_PATH}/deps/zlib/ - DESTINATION ${ROOT}/include - ) - endif() - file(REMOVE_RECURSE ${NODEJS_HEADERS_PATH}) - unset(NODEJS_HEADERS_PATH CACHE) - endif() - - # Only download the libraries on windows, since its the only place - # its necessary. Note, this requires rerunning CMake if moving - # a module from one platform to another (should happen automatically - # with most generators) - if(WIN32) - # Download the win32 library for linking - file(STRINGS - ${ROOT}/CHECKSUM LIB32_CHECKSUM - LIMIT_COUNT 1 - REGEX ${LIB32_MATCH} - ) - if(NOT LIB32_CHECKSUM) - message(FATAL_ERROR "Unable to extract x86 library checksum") - endif() - string(REGEX MATCH ${LIB32_MATCH} LIB32_CHECKSUM ${LIB32_CHECKSUM}) - set(LIB32_CHECKSUM ${CMAKE_MATCH_1}) - set(LIB32_PATH win-x86) - set(LIB32_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) - set(LIB32_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB32_NAME}) - if(NOT EXISTS ${ROOT}/${LIB32_PATH}) - file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) - download_file( - "Node.js windows library (32-bit)" - ${URL}/${VERSION}/${LIB32_TARGET} - ${TEMP}/${LIB32_PATH}/${LIB32_NAME} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${LIB32_CHECKSUM} - ) - file(REMOVE_RECURSE ${ROOT}/${LIB32_PATH}) - file(MAKE_DIRECTORY ${ROOT}/${LIB32_PATH}) - file(RENAME - ${TEMP}/${LIB32_PATH}/${LIB32_NAME} - ${ROOT}/${LIB32_PATH}/${LIB32_NAME} - ) - file(REMOVE_RECURSE ${TEMP}/${LIB32_PATH}) - endif() - - # Download the win64 library for linking - file(STRINGS - ${ROOT}/CHECKSUM LIB64_CHECKSUM - LIMIT_COUNT 1 - REGEX ${LIB64_MATCH} - ) - if(NOT LIB64_CHECKSUM) - message(FATAL_ERROR "Unable to extract x64 library checksum") - endif() - string(REGEX MATCH ${LIB64_MATCH} LIB64_CHECKSUM ${LIB64_CHECKSUM}) - set(LIB64_CHECKSUM ${CMAKE_MATCH_1}) - set(LIB64_PATH win-x64) - set(LIB64_NAME ${CMAKE_MATCH_4}${CMAKE_MATCH_5}) - set(LIB64_TARGET ${CMAKE_MATCH_2}${CMAKE_MATCH_3}${LIB64_NAME}) - if(NOT EXISTS ${ROOT}/${LIB64_PATH}) - file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) - download_file( - "Node.js windows library (64-bit)" - ${URL}/${VERSION}/${LIB64_TARGET} - ${TEMP}/${LIB64_PATH}/${LIB64_NAME} - INACTIVITY_TIMEOUT 10 - EXPECTED_HASH ${CHECKTYPE}=${LIB64_CHECKSUM} - ) - file(REMOVE_RECURSE ${ROOT}/${LIB64_PATH}) - file(MAKE_DIRECTORY ${ROOT}/${LIB64_PATH}) - file(RENAME - ${TEMP}/${LIB64_PATH}/${LIB64_NAME} - ${ROOT}/${LIB64_PATH}/${LIB64_NAME} - ) - file(REMOVE_RECURSE ${TEMP}/${LIB64_PATH}) - endif() - endif() - - # The downloaded headers should always be set for inclusion - list(APPEND INCLUDE_DIRS ${ROOT}/include) - - # Look for the NAN module, and add it to the includes - find_nodejs_module( - nan - ${CMAKE_CURRENT_SOURCE_DIR} - NODEJS_NAN_DIR - ) - if(NODEJS_NAN_DIR) - list(APPEND INCLUDE_DIRS ${NODEJS_NAN_DIR}) - endif() - - # Under windows, we need a bunch of libraries (due to the way - # dynamic linking works) - if(WIN32) - # Generate and use a delay load hook to allow the node binary - # name to be changed while still loading native modules - set(DELAY_LOAD_HOOK ${CMAKE_CURRENT_BINARY_DIR}/win_delay_load_hook.c) - nodejs_generate_delayload_hook(${DELAY_LOAD_HOOK}) - set(SOURCES ${DELAY_LOAD_HOOK}) - - # Necessary flags to get delayload working correctly - list(APPEND LINK_FLAGS - "-IGNORE:4199" - "-DELAYLOAD:iojs.exe" - "-DELAYLOAD:node.exe" - "-DELAYLOAD:node.dll" - ) - - # Core system libraries used by node - list(APPEND LIBRARIES - kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib - advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib - odbc32.lib Shlwapi.lib DelayImp.lib - ) - - # Also link to the node stub itself (downloaded above) - if(CMAKE_CL_64) - list(APPEND LIBRARIES ${ROOT}/${LIB64_PATH}/${LIB64_NAME}) - else() - list(APPEND LIBRARIES ${ROOT}/${LIB32_PATH}/${LIB32_NAME}) - endif() - else() - # Non-windows platforms should use these flags - list(APPEND DEFINITIONS _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64) - endif() - - # Special handling for OSX / clang to allow undefined symbols - # Define is required by node on OSX - if(APPLE) - list(APPEND LINK_FLAGS "-undefined dynamic_lookup") - list(APPEND DEFINITIONS _DARWIN_USE_64_BIT_INODE=1) - endif() - - # Export all settings for use as arguments in the rest of the build - set(NODEJS_VERSION ${VERSION} PARENT_SCOPE) - set(NODEJS_SOURCES ${SOURCES} PARENT_SCOPE) - set(NODEJS_INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) - set(NODEJS_LIBRARIES ${LIBRARIES} PARENT_SCOPE) - set(NODEJS_LINK_FLAGS ${LINK_FLAGS} PARENT_SCOPE) - set(NODEJS_DEFINITIONS ${DEFINITIONS} PARENT_SCOPE) - - # Prevents this function from executing more than once - set(NODEJS_INIT TRUE PARENT_SCOPE) -endfunction() - -# Helper function for defining a node module -# After nodejs_init, all of the settings and dependencies necessary to do -# this yourself are defined, but this helps make sure everything is configured -# correctly. Feel free to use it as a model to do this by hand (or to -# tweak this configuration if you need something custom). -function(add_nodejs_module NAME) - # Make sure node is initialized (variables set) before defining the module - if(NOT NODEJS_INIT) - message(FATAL_ERROR - "Node.js has not been initialized. " - "Call nodejs_init before adding any modules" - ) - endif() - # In order to match node-gyp, we need to build into type specific folders - # ncmake takes care of this, but be sure to set CMAKE_BUILD_TYPE yourself - # if invoking CMake directly - if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) - message(FATAL_ERROR - "Configuration type must be specified. " - "Set CMAKE_BUILD_TYPE or use a different generator" - ) - endif() - - # A node module is a shared library - add_library(${NAME} SHARED ${NODEJS_SOURCES} ${ARGN}) - # Add compiler defines for the module - # Two helpful ones: - # MODULE_NAME must match the name of the build library, define that here - target_compile_definitions(${NAME} - PRIVATE MODULE_NAME=${NAME} - PUBLIC ${NODEJS_DEFINITIONS} - ) - # This properly defines includes for the module - target_include_directories(${NAME} PUBLIC ${NODEJS_INCLUDE_DIRS}) - - # Add link flags to the module - target_link_libraries(${NAME} ${NODEJS_LIBRARIES}) - - # Set required properties for the module to build properly - # Correct naming, symbol visiblity and C++ standard - set_target_properties(${NAME} PROPERTIES - OUTPUT_NAME ${NAME} - PREFIX "" - SUFFIX ".node" - MACOSX_RPATH ON - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - POSITION_INDEPENDENT_CODE TRUE - CMAKE_CXX_STANDARD_REQUIRED TRUE - CXX_STANDARD 11 - LINK_FLAGS "${NODEJS_LINK_FLAGS}" - ) - - # Make sure we're buiilding in a build specific output directory - # Only necessary on single-target generators (Make, Ninja) - # Multi-target generators do this automatically - # This (luckily) mirrors node-gyp conventions - if(NOT CMAKE_CONFIGURATION_TYPES) - set_property(TARGET ${NAME} PROPERTY - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BUILD_TYPE} - ) - endif() -endfunction() diff --git a/cmake/loop-uv.cmake b/cmake/loop-uv.cmake new file mode 100644 index 0000000000..e1d3166b63 --- /dev/null +++ b/cmake/loop-uv.cmake @@ -0,0 +1,14 @@ +add_library(mbgl-loop-uv INTERFACE) + +target_sources(mbgl-loop-uv INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/async_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/run_loop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/timer.cpp +) + +target_include_directories(mbgl-loop-uv INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src +) + +create_source_groups(mbgl-loop-uv) diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake index f087c32511..88e24e3536 100644 --- a/cmake/mbgl.cmake +++ b/cmake/mbgl.cmake @@ -17,12 +17,32 @@ if(WITH_NODEJS) message(FATAL_ERROR "Could not find npm") endif() + execute_process( + COMMAND "${NodeJS_EXECUTABLE}" -e "process.stdout.write(process.versions.node)" + RESULT_VARIABLE _STATUS_CODE + OUTPUT_VARIABLE NodeJS_VERSION + ERROR_VARIABLE _STATUS_MESSAGE + ) + if(NOT _STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Could not detect Node.js version: ${_STATUS_MESSAGE}") + endif() + + execute_process( + COMMAND "${NodeJS_EXECUTABLE}" -e "process.stdout.write(process.versions.modules)" + RESULT_VARIABLE _STATUS_CODE + OUTPUT_VARIABLE NodeJS_ABI + ERROR_VARIABLE _STATUS_MESSAGE + ) + if(NOT _STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Could not detect Node.js ABI version: ${_STATUS_MESSAGE}") + endif() + function(_npm_install DIRECTORY NAME ADDITIONAL_DEPS) SET(NPM_INSTALL_FAILED FALSE) if("${DIRECTORY}/package.json" IS_NEWER_THAN "${DIRECTORY}/node_modules/.${NAME}.stamp") message(STATUS "Running 'npm install' for ${NAME}...") execute_process( - COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts + COMMAND "${NodeJS_EXECUTABLE}" "${npm_EXECUTABLE}" install --ignore-scripts WORKING_DIRECTORY "${DIRECTORY}" RESULT_VARIABLE NPM_INSTALL_FAILED) if(NOT NPM_INSTALL_FAILED) @@ -32,7 +52,7 @@ if(WITH_NODEJS) add_custom_command( OUTPUT "${DIRECTORY}/node_modules/.${NAME}.stamp" - COMMAND ${NodeJS_EXECUTABLE} ${npm_EXECUTABLE} install --ignore-scripts + COMMAND "${NodeJS_EXECUTABLE}" "${npm_EXECUTABLE}" install --ignore-scripts COMMAND ${CMAKE_COMMAND} -E touch "${DIRECTORY}/node_modules/.${NAME}.stamp" WORKING_DIRECTORY "${DIRECTORY}" DEPENDS ${ADDITIONAL_DEPS} "${DIRECTORY}/package.json" @@ -72,7 +92,12 @@ endif() # Generate source groups so the files are properly sorted in IDEs like Xcode. function(create_source_groups target) - get_target_property(sources ${target} SOURCES) + get_target_property(type ${target} TYPE) + if(type AND type STREQUAL "INTERFACE_LIBRARY") + get_target_property(sources ${target} INTERFACE_SOURCES) + else() + get_target_property(sources ${target} SOURCES) + endif() foreach(file ${sources}) get_filename_component(file "${file}" ABSOLUTE) string(REGEX REPLACE "^${CMAKE_SOURCE_DIR}/" "" group "${file}") diff --git a/cmake/node.cmake b/cmake/node.cmake index 90bec6575c..b6f7bb9dc0 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -1,87 +1,96 @@ # Load Node.js -include(cmake/NodeJS.cmake) -nodejs_init() +include(node_modules/@mapbox/cmake-node-module/module.cmake) -add_library(mbgl-loop-node STATIC - platform/default/async_task.cpp - platform/default/run_loop.cpp - platform/default/timer.cpp -) +add_library(mbgl-loop-node INTERFACE) -target_include_directories(mbgl-loop-node - PRIVATE include - PRIVATE src +target_sources(mbgl-loop-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/async_task.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/run_loop.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default/timer.cpp ) -target_include_directories(mbgl-loop-node PUBLIC ${NODEJS_INCLUDE_DIRS}) +target_include_directories(mbgl-loop-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src +) create_source_groups(mbgl-loop-node) -xcode_create_scheme(TARGET mbgl-loop-node) -add_nodejs_module(mbgl-node - platform/node/src/node_mapbox_gl_native.cpp + +add_node_module(mbgl-node + INSTALL_DIR "lib" + NAN_VERSION "2.10.0" + EXCLUDE_NODE_ABIS 47 51 59 # Don't build old beta ABIs 5.x, 7.x, and 9.x ) -# NodeJS.cmake forces C++11. -# https://github.com/cjntaylor/node-cmake/issues/18 -set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14) - -target_sources(mbgl-node - PRIVATE platform/node/src/node_logging.hpp - PRIVATE platform/node/src/node_logging.cpp - PRIVATE platform/node/src/node_conversion.hpp - PRIVATE platform/node/src/node_map.hpp - PRIVATE platform/node/src/node_map.cpp - PRIVATE platform/node/src/node_request.hpp - PRIVATE platform/node/src/node_request.cpp - PRIVATE platform/node/src/node_feature.hpp - PRIVATE platform/node/src/node_feature.cpp - PRIVATE platform/node/src/node_thread_pool.hpp - PRIVATE platform/node/src/node_thread_pool.cpp - PRIVATE platform/node/src/node_expression.hpp - PRIVATE platform/node/src/node_expression.cpp - PRIVATE platform/node/src/util/async_queue.hpp +target_sources(mbgl-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_mapbox_gl_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_mapbox_gl_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_logging.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_conversion.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_map.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_request.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_request.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_feature.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_feature.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_thread_pool.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_thread_pool.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_expression.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/node_expression.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/node/src/util/async_queue.hpp ) -target_include_directories(mbgl-node - PRIVATE platform/default +target_include_directories(mbgl-node INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/platform/default ) -target_link_libraries(mbgl-node - PRIVATE mbgl-core - PRIVATE mbgl-loop-node +target_link_libraries(mbgl-node INTERFACE + mbgl-core + mbgl-loop-node ) -target_add_mason_package(mbgl-node PRIVATE geojson) +target_add_mason_package(mbgl-node INTERFACE geojson) -add_custom_command( - TARGET mbgl-node - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/lib/mapbox_gl_native.node -) +add_custom_target(mbgl-node.active DEPENDS mbgl-node.abi-${NodeJS_ABI}) mbgl_platform_node() create_source_groups(mbgl-node) -initialize_xcode_cxx_build_settings(mbgl-node) +foreach(ABI IN LISTS mbgl-node::abis) + initialize_xcode_cxx_build_settings(mbgl-node.abi-${ABI}) + xcode_create_scheme( + TARGET mbgl-node.abi-${ABI} + NAME "mbgl-node (ABI ${ABI})" + ) +endforeach() + +xcode_create_scheme( + TARGET mbgl-node.active + TYPE library + NAME "mbgl-node (Active ABI)" +) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.all + TYPE library + NAME "mbgl-node (All ABIs)" ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node tests" + NAME "node tests (Active ABI)" ARGS "node_modules/.bin/tape platform/node/test/js/**/*.test.js" ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node render tests" + NAME "node render tests (Active ABI)" ARGS "platform/node/test/render.test.js" OPTIONAL_ARGS @@ -90,9 +99,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node query tests" + NAME "node query tests (Active ABI)" ARGS "platform/node/test/query.test.js" OPTIONAL_ARGS @@ -101,9 +110,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node expression tests" + NAME "node expression tests (Active ABI)" ARGS "platform/node/test/expression.test.js" OPTIONAL_ARGS @@ -112,9 +121,9 @@ xcode_create_scheme( ) xcode_create_scheme( - TARGET mbgl-node + TARGET mbgl-node.active TYPE node - NAME "node-benchmark" + NAME "node-benchmark (Active ABI)" ARGS "platform/node/test/benchmark.js" OPTIONAL_ARGS diff --git a/package.json b/package.json index c3f0d7b8ae..39f64a3967 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "license": "BSD-2-Clause", "dependencies": { - "nan": "~2.10", + "@mapbox/cmake-node-module": "^1.0.0", "node-pre-gyp": "^0.6.37", "npm-run-all": "^4.0.2" }, diff --git a/platform/macos/config.cmake b/platform/macos/config.cmake index 28573258d9..aa3f17eea1 100644 --- a/platform/macos/config.cmake +++ b/platform/macos/config.cmake @@ -154,7 +154,5 @@ macro(mbgl_platform_benchmark) endmacro() macro(mbgl_platform_node) - target_link_libraries(mbgl-node - PRIVATE "-Wl,-bind_at_load" - ) + # Define macro to enable this target on this platform endmacro() diff --git a/platform/node/index.js b/platform/node/index.js index 5944a0a27d..6f6b33058a 100644 --- a/platform/node/index.js +++ b/platform/node/index.js @@ -2,9 +2,8 @@ // Shim to wrap req.respond while preserving callback-passing API -var mbgl = require('../../lib/mapbox_gl_native.node'); +var mbgl = require('../../lib/mbgl-node.abi-' + process.versions.modules); var constructor = mbgl.Map.prototype.constructor; -var process = require('process'); var Map = function(options) { if (!(options instanceof Object)) { -- cgit v1.2.1 From 162ee6f4e888050f36f23997eaa5fc215b80e75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 22 May 2018 12:19:40 +0200 Subject: [node] fixup bad merge --- platform/node/src/node_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index bc40d56f44..4d89077d64 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -460,7 +460,7 @@ void NodeMap::startRender(NodeMap::RenderOptions options) { } void NodeMap::renderFinished() { - if (!callback) { + if (!req) { // In some situations, the render finishes at the same time as we call cancel. Make sure // we are only finishing a render once. return; -- cgit v1.2.1 From 94ec9e15d5c418ff767dbf92c5d49914e5a55dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 22 May 2018 11:23:29 +0200 Subject: [core] fix hang when parsing very large angles for hue in hsl colors --- src/csscolorparser/csscolorparser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/csscolorparser/csscolorparser.cpp b/src/csscolorparser/csscolorparser.cpp index 4d1c6a3d65..106dae6cef 100644 --- a/src/csscolorparser/csscolorparser.cpp +++ b/src/csscolorparser/csscolorparser.cpp @@ -185,7 +185,6 @@ optional parse(const std::string& css_str) { // Convert to lowercase. std::transform(str.begin(), str.end(), str.begin(), ::tolower); - for (const auto& namedColor : namedColors) { if (str == namedColor.name) { return { namedColor.color }; @@ -262,8 +261,9 @@ optional parse(const std::string& css_str) { } float h = parseFloat(params[0]) / 360.0f; - while (h < 0.0f) h++; - while (h > 1.0f) h--; + float i; + // Normalize the hue to [0..1[ + h = std::modf(h, &i); // NOTE(deanm): According to the CSS spec s/l should only be // percentages, but we don't bother and let float or percentage. -- cgit v1.2.1 From 23a9d3b62362905b926405f74d6932f420666d2f Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Tue, 22 May 2018 14:33:19 +0300 Subject: [core] Use std::ios::binary format in std::ifstream --- src/mbgl/util/io.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/util/io.cpp b/src/mbgl/util/io.cpp index 6a6ed7b250..058cd0d202 100644 --- a/src/mbgl/util/io.cpp +++ b/src/mbgl/util/io.cpp @@ -20,7 +20,7 @@ void write_file(const std::string &filename, const std::string &data) { } std::string read_file(const std::string &filename) { - std::ifstream file(filename); + std::ifstream file(filename, std::ios::binary); if (file.good()) { std::stringstream data; data << file.rdbuf(); @@ -31,7 +31,7 @@ std::string read_file(const std::string &filename) { } optional readFile(const std::string &filename) { - std::ifstream file(filename); + std::ifstream file(filename, std::ios::binary); if (file.good()) { std::stringstream data; data << file.rdbuf(); -- cgit v1.2.1 From cce72e2d36c5efe53bb026a0d98f835d16440ffa Mon Sep 17 00:00:00 2001 From: Tobrun Date: Tue, 22 May 2018 11:50:44 +0200 Subject: [android] - change MapView#initialize modifier to allow subclasses to override this method to provide alternate configurations to MapboxMapOptions --- .../src/main/java/com/mapbox/mapboxsdk/maps/MapView.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 23fb3c22dd..0f19965224 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -98,28 +98,30 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { @UiThread public MapView(@NonNull Context context) { super(context); - initialise(context, MapboxMapOptions.createFromAttributes(context, null)); + initialize(context, MapboxMapOptions.createFromAttributes(context, null)); } @UiThread public MapView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); - initialise(context, MapboxMapOptions.createFromAttributes(context, attrs)); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); } @UiThread public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - initialise(context, MapboxMapOptions.createFromAttributes(context, attrs)); + initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); } @UiThread public MapView(@NonNull Context context, @Nullable MapboxMapOptions options) { super(context); - initialise(context, options == null ? MapboxMapOptions.createFromAttributes(context, null) : options); + initialize(context, options == null ? MapboxMapOptions.createFromAttributes(context, null) : options); } - private void initialise(@NonNull final Context context, @NonNull final MapboxMapOptions options) { + @CallSuper + @UiThread + protected void initialize(@NonNull final Context context, @NonNull final MapboxMapOptions options) { if (isInEditMode()) { // in IDE layout editor, just return return; -- cgit v1.2.1 From 60505b03174b5ec02ae723beafa7683f6ed54a62 Mon Sep 17 00:00:00 2001 From: Tobrun Date: Thu, 15 Mar 2018 13:07:46 +0100 Subject: [android] - remove mips and armeabi as supported ABIs [android] - bump CI image to NDK 17 compatible [core] - remove setting edgeDistance to 0, comparison 'const short' > 32767 is always false [android] - remove throwing in desructor, undefined behaviour [android] - bump dependency versions of project --- Makefile | 6 ++---- circle.yml | 4 ++-- cmake/mason.cmake | 8 +------- platform/android/MapboxGLAndroidSDK/build.gradle | 12 ++++++++---- platform/android/build.gradle | 2 +- platform/android/config.cmake | 2 +- platform/android/gradle/dependencies.gradle | 22 ++++++++++------------ .../gradle/wrapper/gradle-wrapper.properties | 3 ++- platform/android/src/bitmap.cpp | 3 ++- platform/android/src/run_loop.cpp | 6 ++++-- src/mbgl/util/http_header.cpp | 1 + 11 files changed, 34 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index e54fe6313b..c2daa617b4 100644 --- a/Makefile +++ b/Makefile @@ -480,12 +480,10 @@ test-node-recycle-map: node #### Android targets ########################################################### -MBGL_ANDROID_ABIS = arm-v5;armeabi -MBGL_ANDROID_ABIS += arm-v7;armeabi-v7a +MBGL_ANDROID_ABIS = arm-v7;armeabi-v7a MBGL_ANDROID_ABIS += arm-v8;arm64-v8a MBGL_ANDROID_ABIS += x86;x86 MBGL_ANDROID_ABIS += x86-64;x86_64 -MBGL_ANDROID_ABIS += mips;mips MBGL_ANDROID_LOCAL_WORK_DIR = /data/local/tmp/core-tests MBGL_ANDROID_LIBDIR = lib$(if $(filter arm-v8 x86-64,$1),64) @@ -626,7 +624,7 @@ run-android-unit-test-%: platform/android/gradle/configuration.gradle # Builds a release package of the Android SDK .PHONY: apackage apackage: platform/android/gradle/configuration.gradle - make android-lib-arm-v5 && make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 && make android-lib-mips + make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE) # Build test app instrumentation tests apk and test app apk for all abi's diff --git a/circle.yml b/circle.yml index ca9ceb6636..53cc6e9c7b 100644 --- a/circle.yml +++ b/circle.yml @@ -305,7 +305,7 @@ jobs: # ------------------------------------------------------------------------------ android-debug-arm-v7: docker: - - image: mbgl/7d2403f42e:android-ndk-r16b + - image: mbgl/feb0443038:android-ndk-r17 resource_class: large working_directory: /src environment: @@ -378,7 +378,7 @@ jobs: # ------------------------------------------------------------------------------ android-release-all: docker: - - image: mbgl/7d2403f42e:android-ndk-r16b + - image: mbgl/feb0443038:android-ndk-r17 resource_class: large working_directory: /src environment: diff --git a/cmake/mason.cmake b/cmake/mason.cmake index bc31feeb5f..6116067080 100644 --- a/cmake/mason.cmake +++ b/cmake/mason.cmake @@ -23,9 +23,7 @@ function(mason_detect_platform) if(NOT MASON_PLATFORM_VERSION) # Android Studio only passes ANDROID_ABI, but we need to adjust that to the Mason if(MASON_PLATFORM STREQUAL "android" AND NOT MASON_PLATFORM_VERSION) - if (ANDROID_ABI STREQUAL "armeabi") - set(MASON_PLATFORM_VERSION "arm-v5-9" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "armeabi-v7a") + if (ANDROID_ABI STREQUAL "armeabi-v7a") set(MASON_PLATFORM_VERSION "arm-v7-9" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "arm64-v8a") set(MASON_PLATFORM_VERSION "arm-v8-21" PARENT_SCOPE) @@ -33,10 +31,6 @@ function(mason_detect_platform) set(MASON_PLATFORM_VERSION "x86-9" PARENT_SCOPE) elseif (ANDROID_ABI STREQUAL "x86_64") set(MASON_PLATFORM_VERSION "x86-64-21" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "mips") - set(MASON_PLATFORM_VERSION "mips-9" PARENT_SCOPE) - elseif (ANDROID_ABI STREQUAL "mips64") - set(MASON_PLATFORM_VERSION "mips-64-9" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown ANDROID_ABI '${ANDROID_ABI}'.") endif() diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle index c43bc9112b..21ed25e2c2 100644 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ b/platform/android/MapboxGLAndroidSDK/build.gradle @@ -1,9 +1,13 @@ apply plugin: 'com.android.library' dependencies { - api dependenciesList.mapboxAndroidTelemetry + api (dependenciesList.mapboxAndroidTelemetry) { + exclude group: 'com.android.support', module: 'appcompat-v7' + } api dependenciesList.mapboxJavaGeoJSON - api dependenciesList.mapboxAndroidGestures + api (dependenciesList.mapboxAndroidGestures) { + exclude group: 'com.android.support', module: 'appcompat-v7' + } implementation dependenciesList.supportAnnotations implementation dependenciesList.supportFragmentV4 implementation dependenciesList.timber @@ -87,7 +91,7 @@ android { if (abi != 'all') { abiFilters abi.split(' ') } else { - abiFilters "armeabi", "armeabi-v7a", "mips", "x86", "arm64-v8a", "x86_64" + abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } } } @@ -111,7 +115,7 @@ android { } lintOptions { - disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission' + disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural' checkAllWarnings true warningsAsErrors false } diff --git a/platform/android/build.gradle b/platform/android/build.gradle index 16238f41c1..45cddd9688 100644 --- a/platform/android/build.gradle +++ b/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.2' } } diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 77074dc82c..c25e48de05 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -10,7 +10,7 @@ set(CMAKE_C_ARCHIVE_APPEND " ruT ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") -if ((ANDROID_ABI STREQUAL "armeabi") OR (ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR +if ((ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR (ANDROID_ABI STREQUAL "x86") OR (ANDROID_ABI STREQUAL "x86_64")) # Use Identical Code Folding on platforms that support the gold linker. set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_EXE_LINKER_FLAGS}") diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle index b9744a61ef..ff61b1d074 100644 --- a/platform/android/gradle/dependencies.gradle +++ b/platform/android/gradle/dependencies.gradle @@ -2,24 +2,23 @@ ext { androidVersions = [ minSdkVersion : 14, - targetSdkVersion : 25, - compileSdkVersion: 25, - buildToolsVersion: '26.0.3' + targetSdkVersion : 27, + compileSdkVersion: 27, + buildToolsVersion: '27.0.3' ] versions = [ mapboxServices : '3.1.0', mapboxTelemetry: '3.1.1', mapboxGestures : '0.2.0', - supportLib : '25.4.0', - espresso : '3.0.1', - testRunner : '1.0.1', - leakCanary : '1.5.1', - lost : '3.0.4', + supportLib : '27.1.1', + espresso : '3.0.2', + testRunner : '1.0.2', + leakCanary : '1.5.4', junit : '4.12', - mockito : '2.10.0', - robolectric : '3.5.1', - timber : '4.5.1', + mockito : '2.18.3', + robolectric : '3.8', + timber : '4.7.0', okhttp : '3.10.0' ] @@ -48,7 +47,6 @@ ext { supportDesign : "com.android.support:design:${versions.supportLib}", supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}", - lost : "com.mapzen.android:lost:${versions.lost}", gmsLocation : 'com.google.android.gms:play-services-location:11.0.4', timber : "com.jakewharton.timber:timber:${versions.timber}", okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", diff --git a/platform/android/gradle/wrapper/gradle-wrapper.properties b/platform/android/gradle/wrapper/gradle-wrapper.properties index bf1b63c346..84af82d181 100644 --- a/platform/android/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon May 14 12:12:39 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp index 46e7253050..0d3670b666 100644 --- a/platform/android/src/bitmap.cpp +++ b/platform/android/src/bitmap.cpp @@ -1,6 +1,7 @@ #include "bitmap.hpp" #include +#include namespace mbgl { namespace android { @@ -17,7 +18,7 @@ public: ~PixelGuard() { const int result = AndroidBitmap_unlockPixels(&env, jni::Unwrap(*bitmap)); if (result != ANDROID_BITMAP_RESULT_SUCCESS) { - throw std::runtime_error("bitmap decoding: could not unlock pixels"); + Log::Warning(mbgl::Event::General, "Bitmap decoding: could not unlock pixels"); } } diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 34366d836a..f655f13ea8 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -17,6 +18,7 @@ #include #include +#include #define PIPE_OUT 0 #define PIPE_IN 1 @@ -119,11 +121,11 @@ RunLoop::Impl::~Impl() { alarm.reset(); if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) { - throw std::runtime_error("Failed to remove file descriptor from Looper."); + Log::Error(mbgl::Event::General, "Failed to remove file descriptor from Looper"); } if (close(fds[PIPE_IN]) || close(fds[PIPE_OUT])) { - throw std::runtime_error("Failed to close file descriptor."); + Log::Error(mbgl::Event::General, "Failed to close file descriptor."); } ALooper_release(loop); diff --git a/src/mbgl/util/http_header.cpp b/src/mbgl/util/http_header.cpp index 5921edfb14..8048dfe84a 100644 --- a/src/mbgl/util/http_header.cpp +++ b/src/mbgl/util/http_header.cpp @@ -8,6 +8,7 @@ #pragma GCC diagnostic ignored "-Wshadow" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshorten-64-to-32" +#pragma clang diagnostic ignored "-Wtautological-constant-compare" #include #include #include -- cgit v1.2.1