From 33a70100b839793311ecc73c873c063c7cf65e31 Mon Sep 17 00:00:00 2001 From: Julian Rex Date: Fri, 4 Oct 2019 13:51:40 -0400 Subject: [ios] Adds pointForCoordinate/coordinateForPoint to MGLMapSnapshotOverlay (#15746) * [ios] Adds `pointForCoordinate:`/`coordinateForPoint:` to MGLMapSnapshotOverlay * [ios] Adds PR # * [ios, macos] Update file lists & mac contexts * [macos] Fix for image scale in overlay. * [ios] Updated change log based on PR feedback. --- platform/darwin/src/MGLMapSnapshotter.h | 25 ++++++++ platform/darwin/src/MGLMapSnapshotter.mm | 66 +++++++++++++++++--- platform/darwin/src/MGLMapSnapshotter_Private.h | 14 +++++ platform/ios/CHANGELOG.md | 1 + .../Snapshotter Tests/MGLMapSnapshotterTest.m | 72 ++++++++++++++++++++++ platform/ios/ios.xcodeproj/project.pbxproj | 10 ++- platform/ios/sdk-files.json | 1 + 7 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 platform/darwin/src/MGLMapSnapshotter_Private.h diff --git a/platform/darwin/src/MGLMapSnapshotter.h b/platform/darwin/src/MGLMapSnapshotter.h index 33febe0d0c..98c8e8a375 100644 --- a/platform/darwin/src/MGLMapSnapshotter.h +++ b/platform/darwin/src/MGLMapSnapshotter.h @@ -19,6 +19,31 @@ MGL_EXPORT */ @property (nonatomic, readonly) CGContextRef context; +#if TARGET_OS_IPHONE +/** + Converts the specified map coordinate to a point in the coordinate space of the + context. + */ +- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + Converts the specified context point to a map coordinate. + */ +- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point; + +#else +/** + Converts the specified map coordinate to a point in the coordinate space of the + context. + */ +- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate; + +/** + Converts the specified context point to a map coordinate. + */ +- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point; +#endif + @end /** diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm index 85619a780b..0dde94292c 100644 --- a/platform/darwin/src/MGLMapSnapshotter.mm +++ b/platform/darwin/src/MGLMapSnapshotter.mm @@ -17,6 +17,8 @@ #import "MGLAttributionInfo_Private.h" #import "MGLLoggingConfiguration_Private.h" #import "MGLRendererConfiguration.h" +#import "MGLMapSnapshotter_Private.h" + #if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR #import "MGLMapboxEvents.h" #endif @@ -33,23 +35,61 @@ const CGPoint MGLLogoImagePosition = CGPointMake(8, 8); const CGFloat MGLSnapshotterMinimumPixelSize = 64; -@interface MGLMapSnapshotOverlay() - -- (instancetype)initWithContext:(CGContextRef)context; +@interface MGLMapSnapshotOverlay() +@property (nonatomic, assign) CGFloat scale; +- (instancetype)initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn; @end -@implementation MGLMapSnapshotOverlay +@implementation MGLMapSnapshotOverlay { + mbgl::MapSnapshotter::PointForFn _pointForFn; + mbgl::MapSnapshotter::LatLngForFn _latLngForFn; +} -- (instancetype) initWithContext:(CGContextRef)context { +- (instancetype) initWithContext:(CGContextRef)context scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn { self = [super init]; if (self) { _context = context; + _scale = scale; + _pointForFn = pointForFn; + _latLngForFn = latLngForFn; } return self; } +#if TARGET_OS_IPHONE + +- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate +{ + mbgl::ScreenCoordinate sc = _pointForFn(MGLLatLngFromLocationCoordinate2D(coordinate)); + return CGPointMake(sc.x, sc.y); +} + +- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point +{ + mbgl::LatLng latLng = _latLngForFn(mbgl::ScreenCoordinate(point.x, point.y)); + return MGLLocationCoordinate2DFromLatLng(latLng); +} + +#else + +- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate +{ + mbgl::ScreenCoordinate sc = _pointForFn(MGLLatLngFromLocationCoordinate2D(coordinate)); + CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale; + return NSMakePoint(sc.x, height - sc.y); +} + +- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point +{ + CGFloat height = ((CGFloat)CGBitmapContextGetHeight(self.context))/self.scale; + auto screenCoord = mbgl::ScreenCoordinate(point.x, height - point.y); + mbgl::LatLng latLng = _latLngForFn(screenCoord); + return MGLLocationCoordinate2DFromLatLng(latLng); +} + +#endif @end @implementation MGLMapSnapshotOptions @@ -78,7 +118,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; @end -@interface MGLMapSnapshot() +@interface MGLMapSnapshot() - (instancetype)initWithImage:(nullable MGLImage *)image scale:(CGFloat)scale pointForFn:(mbgl::MapSnapshotter::PointForFn)pointForFn latLngForFn:(mbgl::MapSnapshotter::LatLngForFn)latLngForFn; @property (nonatomic) CGFloat scale; @@ -93,8 +133,8 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; { self = [super init]; if (self) { - _pointForFn = std::move(pointForFn); - _latLngForFn = std::move(latLngForFn); + _pointForFn = pointForFn; + _latLngForFn = latLngForFn; _scale = scale; _image = image; } @@ -328,7 +368,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; CGContextRef currentContext = UIGraphicsGetCurrentContext(); if (currentContext && overlayHandler) { - MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext]; + MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext + scale:scale + pointForFn:pointForFn + latLngForFn:latLngForFn]; CGContextSaveGState(snapshotOverlay.context); overlayHandler(snapshotOverlay); CGContextRestoreGState(snapshotOverlay.context); @@ -409,7 +452,10 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64; NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; if (currentContext && overlayHandler) { - MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext.CGContext]; + MGLMapSnapshotOverlay *snapshotOverlay = [[MGLMapSnapshotOverlay alloc] initWithContext:currentContext.CGContext + scale:scale + pointForFn:pointForFn + latLngForFn:latLngForFn]; [currentContext saveGraphicsState]; overlayHandler(snapshotOverlay); [currentContext restoreGraphicsState]; diff --git a/platform/darwin/src/MGLMapSnapshotter_Private.h b/platform/darwin/src/MGLMapSnapshotter_Private.h new file mode 100644 index 0000000000..205e51a6de --- /dev/null +++ b/platform/darwin/src/MGLMapSnapshotter_Private.h @@ -0,0 +1,14 @@ +#import "MGLMapSnapshotter.h" + +@protocol MGLMapSnapshotProtocol + +#if TARGET_OS_IPHONE +- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate; +- (CLLocationCoordinate2D)coordinateForPoint:(CGPoint)point; + +#else +- (NSPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate; +- (CLLocationCoordinate2D)coordinateForPoint:(NSPoint)point; +#endif + +@end diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index d636d94811..d11c9a5f95 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 ### Other changes +* Added `-[MGLMapSnapshotOverlay coordinateForPoint:]` and `-[MGLMapSnapshotOverlay pointForCoordinate:]` to convert between context and map coordinates, mirroring those of `MGLMapSnapshot`. ([#15746](https://github.com/mapbox/mapbox-gl-native/pull/15746)) * Suppress network requests for expired tiles update, if these tiles are invisible. ([#15741](https://github.com/mapbox/mapbox-gl-native/pull/15741)) * Fixed an issue that caused `MGLScaleBar` to have an incorrect size when resizing or rotating. ([#15703](https://github.com/mapbox/mapbox-gl-native/pull/15703)) diff --git a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m index 19718165b3..9ef2054dff 100644 --- a/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m +++ b/platform/ios/Integration Tests/Snapshotter Tests/MGLMapSnapshotterTest.m @@ -1,4 +1,5 @@ #import "MGLMapViewIntegrationTest.h" +#import "MGLMapSnapshotter_Private.h" @interface MGLMapSnapshotter () @property (nonatomic) BOOL cancelled; @@ -24,6 +25,21 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates return snapshotter; } +MGLMapSnapshotter* snapshotterWithBounds(MGLCoordinateBounds bounds, CGSize size) { + + MGLMapCamera* mapCamera = [[MGLMapCamera alloc] init]; + MGLMapSnapshotOptions* options = [[MGLMapSnapshotOptions alloc] initWithStyleURL:[MGLStyle satelliteStreetsStyleURL] + camera:mapCamera + size:size]; + options.coordinateBounds = bounds; + + // Create and start the snapshotter + MGLMapSnapshotter* snapshotter = [[MGLMapSnapshotter alloc] initWithOptions:options]; + return snapshotter; +} + + + @implementation MGLMapSnapshotterTest - (void)testMultipleSnapshotsWithASingleSnapshotter🔒 { @@ -462,4 +478,60 @@ MGLMapSnapshotter* snapshotterWithCoordinates(CLLocationCoordinate2D coordinates [self waitForExpectations:@[expectation] timeout:10.0]; } +- (void)testSnapshotCoordinatesWithOverlayHandler🔒 { + CGSize size = self.mapView.bounds.size; + + XCTestExpectation *expectation = [self expectationWithDescription:@"snapshot with overlay succeeds"]; + expectation.expectedFulfillmentCount = 2; + + CLLocationCoordinate2D london = { .latitude = 51.5074, .longitude = -0.1278 }; + CLLocationCoordinate2D paris = { .latitude = 48.8566, .longitude = 2.3522 }; + + MGLCoordinateBounds bounds = { + .ne = london, + .sw = paris + }; + + MGLMapSnapshotter *snapshotter = snapshotterWithBounds(bounds, size); + XCTAssertNotNil(snapshotter); + + void (^testCoordinates)(id) = ^(id snapshot){ + XCTAssertNotNil(snapshot); + + CGPoint londonPoint = [snapshot pointForCoordinate:london]; + CGPoint parisPoint = [snapshot pointForCoordinate:paris]; + + XCTAssertEqualWithAccuracy(londonPoint.x, 0, 0.1); + XCTAssertEqualWithAccuracy(parisPoint.x, size.width, 0.1); + + // Vertically, London and Paris are inset (due to the size vs coordinate bounds) + XCTAssert(parisPoint.y > londonPoint.y); + XCTAssert(londonPoint.y > 0.0); + XCTAssert(parisPoint.y < size.height); + + CLLocationCoordinate2D london2 = [snapshot coordinateForPoint:londonPoint]; + CLLocationCoordinate2D paris2 = [snapshot coordinateForPoint:parisPoint]; + + XCTAssertEqualWithAccuracy(london.latitude, london2.latitude, 0.0000001); + XCTAssertEqualWithAccuracy(london.longitude, london2.longitude, 0.0000001); + XCTAssertEqualWithAccuracy(paris.latitude, paris2.latitude, 0.0000001); + XCTAssertEqualWithAccuracy(paris.longitude, paris2.longitude, 0.0000001); + }; + + [snapshotter startWithOverlayHandler:^(MGLMapSnapshotOverlay *snapshotOverlay) { + XCTAssert([snapshotOverlay conformsToProtocol:@protocol(MGLMapSnapshotProtocol)]); + testCoordinates((id)snapshotOverlay); + + [expectation fulfill]; + } completionHandler:^(MGLMapSnapshot * _Nullable snapshot, NSError * _Nullable error) { + XCTAssert([snapshot conformsToProtocol:@protocol(MGLMapSnapshotProtocol)]); + testCoordinates((id)snapshot); + + [expectation fulfill]; + }]; + + [self waitForExpectations:@[expectation] timeout:10.0]; +} + + @end diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj index 20001c26a8..c873847b4f 100644 --- a/platform/ios/ios.xcodeproj/project.pbxproj +++ b/platform/ios/ios.xcodeproj/project.pbxproj @@ -528,6 +528,8 @@ CABE5DAD2072FAB40003AF3C /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; }; CAD9D0AA22A86D6F001B25EE /* MGLResourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CAD9D0A922A86D6F001B25EE /* MGLResourceTests.mm */; }; CAE7AD5520F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE7AD5420F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift */; }; + CAFB3C14234505D500399265 /* MGLMapSnapshotter_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CAFB3C13234505D500399265 /* MGLMapSnapshotter_Private.h */; }; + CAFB3C15234505D500399265 /* MGLMapSnapshotter_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = CAFB3C13234505D500399265 /* MGLMapSnapshotter_Private.h */; }; CF75A91522D85E860058A5C4 /* MGLLoggingConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF75A91422D85E860058A5C4 /* MGLLoggingConfiguration.mm */; }; CF75A91622D85E860058A5C4 /* MGLLoggingConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = CF75A91422D85E860058A5C4 /* MGLLoggingConfiguration.mm */; }; DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1072,8 +1074,8 @@ 5580B45A229570A10091291B /* MGLMapView+OpenGL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "MGLMapView+OpenGL.mm"; sourceTree = ""; }; 558DE79E1E5615E400C7916D /* MGLFoundation_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLFoundation_Private.h; sourceTree = ""; }; 558DE79F1E5615E400C7916D /* MGLFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFoundation.mm; sourceTree = ""; }; - 55CF752E213ED92000ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libmbgl-vendor-icu.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 55CF7530213ED92A00ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libmbgl-vendor-icu.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 55CF752E213ED92000ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-vendor-icu.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 55CF7530213ED92A00ED86C4 /* libmbgl-vendor-icu.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-vendor-icu.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 55D120A71F791007004B6D81 /* libmbgl-loop-darwin.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-loop-darwin.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 55D120A91F79100C004B6D81 /* libmbgl-filesource.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libmbgl-filesource.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 55D8C9941D0F133500F42F10 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = config.xcconfig; path = ../../build/ios/config.xcconfig; sourceTree = ""; }; @@ -1218,6 +1220,7 @@ CAD9D0A922A86D6F001B25EE /* MGLResourceTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLResourceTests.mm; path = ../../darwin/test/MGLResourceTests.mm; sourceTree = ""; }; CAE7AD5320F46EF5003B6782 /* integration-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "integration-Bridging-Header.h"; sourceTree = ""; }; CAE7AD5420F46EF5003B6782 /* MGLMapSnapshotterSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MGLMapSnapshotterSwiftTests.swift; sourceTree = ""; }; + CAFB3C13234505D500399265 /* MGLMapSnapshotter_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapSnapshotter_Private.h; sourceTree = ""; }; CF75A91422D85E860058A5C4 /* MGLLoggingConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLLoggingConfiguration.mm; 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 = ""; }; @@ -2151,6 +2154,7 @@ DA8847E21CBAFA5100AB86E3 /* MGLMapCamera.h */, DA8848031CBAFA6200AB86E3 /* MGLMapCamera.mm */, 927FBCFD1F4DB05500F8BF1F /* MGLMapSnapshotter.h */, + CAFB3C13234505D500399265 /* MGLMapSnapshotter_Private.h */, 927FBCFE1F4DB05500F8BF1F /* MGLMapSnapshotter.mm */, DD0902A41DB18F1B00C5BDCE /* MGLNetworkConfiguration.h */, 1F2B94BF221636D800210640 /* MGLNetworkConfiguration_Private.h */, @@ -2529,6 +2533,7 @@ 1F6A82A221360F9D00BA5B41 /* MGLLoggingConfiguration.h in Headers */, DA8848311CBAFA6200AB86E3 /* NSString+MGLAdditions.h in Headers */, 967C864B210A9D3C004DF794 /* UIDevice+MGLAdditions.h in Headers */, + CAFB3C14234505D500399265 /* MGLMapSnapshotter_Private.h in Headers */, 1FCAE2A220B872A400C577DD /* MGLLocationManager.h in Headers */, DACA86262019218600E9693A /* MGLRasterDEMSource.h in Headers */, 353933F81D3FB79F003F57D7 /* MGLLineStyleLayer.h in Headers */, @@ -2722,6 +2727,7 @@ 35E1A4D91D74336F007AA97F /* MGLValueEvaluator.h in Headers */, DABFB8701CBE9A0F00D62B32 /* MGLMapView+IBAdditions.h in Headers */, 9C6E283822A982670056B7BE /* MMEEventLogger.h in Headers */, + CAFB3C15234505D500399265 /* MGLMapSnapshotter_Private.h in Headers */, 6F018BAF220031BF003E7269 /* UIView+MGLAdditions.h in Headers */, 96E516EA2000560B00A02306 /* MGLAnnotationView_Private.h in Headers */, 96E516FB20005A4000A02306 /* MGLUserLocationHeadingBeamLayer.h in Headers */, diff --git a/platform/ios/sdk-files.json b/platform/ios/sdk-files.json index 0df4b381ba..dc59e179c8 100644 --- a/platform/ios/sdk-files.json +++ b/platform/ios/sdk-files.json @@ -243,6 +243,7 @@ "MMEDate.h": "platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEDate.h", "NSString+MGLAdditions.h": "platform/darwin/src/NSString+MGLAdditions.h", "UIDevice+MGLAdditions.h": "platform/ios/src/UIDevice+MGLAdditions.h", + "MGLMapSnapshotter_Private.h": "platform/darwin/src/MGLMapSnapshotter_Private.h", "MGLRendererFrontend.h": "platform/darwin/src/MGLRendererFrontend.h", "MGLStyleValue_Private.h": "platform/darwin/src/MGLStyleValue_Private.h", "MGLFillExtrusionStyleLayer_Private.h": "platform/darwin/src/MGLFillExtrusionStyleLayer_Private.h", -- cgit v1.2.1