diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 9ee2a86e4f1..cb038750c13 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.11.0 + +* Adds support for heatmap layers. + ## 2.10.0 * Converts Obj-C->Dart calls to Pigeon. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/README.md index 6b481626dbc..20fae87c885 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/README.md @@ -11,5 +11,15 @@ your app when you do, so you do not need to add it to your `pubspec.yaml`. However, if you `import` this package to use any of its APIs directly, you should add it to your `pubspec.yaml` as usual. +## Supported Heatmap Options + +| Field | Supported | +| ---------------------------- | :-------: | +| Heatmap.dissipating | x | +| Heatmap.maxIntensity | x | +| Heatmap.minimumZoomIntensity | ✓ | +| Heatmap.maximumZoomIntensity | ✓ | +| HeatmapGradient.colorMapSize | ✓ | + [1]: https://pub.dev/packages/google_maps_flutter [2]: https://flutter.dev/to/endorsed-federated-plugin diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Podfile index a5cfb3f7bfb..b1a5e23c9ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Podfile @@ -28,6 +28,9 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + use_frameworks! + use_modular_headers! + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj index 10545d750ed..cb8922d30b6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,11 @@ /* Begin PBXBuildFile section */ 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2BDE99378062AE3E60B40021 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */; }; 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; }; - 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; 521AB0032B876A76005F460D /* ExtractIconFromDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */; }; + 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -21,10 +21,10 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; }; + B3A7FA04ABB7B84780729949 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 61A9A8623F5CA9BBC813DC6B /* Pods_Runner.framework */; }; F269303B2BB389BF00BF17C4 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = F269303A2BB389BF00BF17C4 /* assets */; }; F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; }; F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */; }; - FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -61,13 +61,14 @@ 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTTileProviderControllerTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolylinesControllerTests.m; sourceTree = ""; }; - 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtractIconFromDataTests.m; sourceTree = ""; }; + 61A9A8623F5CA9BBC813DC6B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -84,7 +85,6 @@ B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F269303A2BB389BF00BF17C4 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; F7151F10265D7ED70028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsTests.m; sourceTree = ""; }; @@ -99,7 +99,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */, + B3A7FA04ABB7B84780729949 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -108,7 +108,7 @@ buildActionMask = 2147483647; files = ( 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */, - FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */, + 2BDE99378062AE3E60B40021 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -126,8 +126,8 @@ isa = PBXGroup; children = ( 68E472692836FF0C00BDDDAC /* MapKit.framework */, - 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */, - F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */, + 61A9A8623F5CA9BBC813DC6B /* Pods_Runner.framework */, + 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -241,6 +241,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, + A7D3A643E249522B15BA2B1D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -259,6 +260,7 @@ F7151F0C265D7ED70028CB91 /* Sources */, F7151F0D265D7ED70028CB91 /* Frameworks */, F7151F0E265D7ED70028CB91 /* Resources */, + DF182F6A1B9E41DA05BFCB87 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -411,6 +413,24 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + A7D3A643E249522B15BA2B1D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -453,6 +473,24 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + DF182F6A1B9E41DA05BFCB87 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index 79da52d8198..42e3fde2552 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -319,4 +319,54 @@ - (void)testLengthsFromPatterns { XCTAssertEqual(secondSpanLength.doubleValue, 6.4); } +- (void)testWeightedLatLngFromArray { + NSArray *weightedLatLng = @[ @[ @1, @2 ], @3 ]; + + GMUWeightedLatLng *weightedLocation = + [FLTGoogleMapJSONConversions weightedLatLngFromArray:weightedLatLng]; + + // The location gets projected to different values + XCTAssertEqual([weightedLocation intensity], 3); +} + +- (void)testWeightedLatLngFromArrayThrowsForInvalidInput { + NSArray *weightedLatLng = @[]; + + XCTAssertThrows([FLTGoogleMapJSONConversions weightedLatLngFromArray:weightedLatLng]); +} + +- (void)testWeightedDataFromArray { + NSNumber *intensity1 = @3; + NSNumber *intensity2 = @6; + NSArray *data = @[ @[ @[ @1, @2 ], intensity1 ], @[ @[ @4, @5 ], intensity2 ] ]; + + NSArray *weightedData = + [FLTGoogleMapJSONConversions weightedDataFromArray:data]; + XCTAssertEqual([weightedData[0] intensity], [intensity1 floatValue]); + XCTAssertEqual([weightedData[1] intensity], [intensity2 floatValue]); +} + +- (void)testGradientFromDictionary { + NSNumber *startPoint = @0.6; + NSNumber *colorMapSize = @200; + NSDictionary *gradientData = @{ + @"colors" : @[ + // Color.fromARGB(255, 0, 255, 255) + @4278255615, + ], + @"startPoints" : @[ startPoint ], + @"colorMapSize" : colorMapSize, + }; + + GMUGradient *gradient = [FLTGoogleMapJSONConversions gradientFromDictionary:gradientData]; + CGFloat red, green, blue, alpha; + [[gradient colors][0] getRed:&red green:&green blue:&blue alpha:&alpha]; + XCTAssertEqual(red, 0); + XCTAssertEqual(green, 1); + XCTAssertEqual(blue, 1); + XCTAssertEqual(alpha, 1); + XCTAssertEqualWithAccuracy([[gradient startPoints][0] doubleValue], [startPoint doubleValue], 0); + XCTAssertEqual([gradient mapSize], [colorMapSize intValue]); +} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml index 0c50df87f12..ad8a569e0ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../../ - google_maps_flutter_platform_interface: ^2.7.0 + google_maps_flutter_platform_interface: ^2.9.0 maps_example_dart: path: ../shared/maps_example_dart/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Podfile index 833aacf78d3..a41a54965ba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Podfile @@ -28,6 +28,9 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + use_frameworks! + use_modular_headers! + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj index dc2ad2532b9..defec130df0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */; }; + 5B5EF9A6C72A03092BDA553E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28D4666EF03E57DB7D03E916 /* Pods_RunnerTests.framework */; }; 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -17,9 +17,9 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; }; + AF2E6994ED025F4BCE652A48 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D457E156D08F0B45EA2F6FF /* Pods_Runner.framework */; }; F269303B2BB389BF00BF17C4 /* assets in Resources */ = {isa = PBXBuildFile; fileRef = F269303A2BB389BF00BF17C4 /* assets */; }; F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; }; - FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -46,12 +46,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0D457E156D08F0B45EA2F6FF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 28D4666EF03E57DB7D03E916 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -68,7 +69,6 @@ B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F269303A2BB389BF00BF17C4 /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; path = assets; sourceTree = ""; }; F7151F10265D7ED70028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsTests.m; sourceTree = ""; }; @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */, + AF2E6994ED025F4BCE652A48 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -89,7 +89,7 @@ buildActionMask = 2147483647; files = ( 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */, - FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */, + 5B5EF9A6C72A03092BDA553E /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -100,8 +100,8 @@ isa = PBXGroup; children = ( 68E472692836FF0C00BDDDAC /* MapKit.framework */, - 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */, - F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */, + 0D457E156D08F0B45EA2F6FF /* Pods_Runner.framework */, + 28D4666EF03E57DB7D03E916 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -200,6 +200,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, + 851CD952D11C6D91E216A90F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -218,6 +219,7 @@ F7151F0C265D7ED70028CB91 /* Sources */, F7151F0D265D7ED70028CB91 /* Frameworks */, F7151F0E265D7ED70028CB91 /* Resources */, + 7862D82E36C37F9B7558DA5E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -324,6 +326,42 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 7862D82E36C37F9B7558DA5E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/OCMock/OCMock.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OCMock.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 851CD952D11C6D91E216A90F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/pubspec.yaml index 0c50df87f12..ad8a569e0ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../../ - google_maps_flutter_platform_interface: ^2.7.0 + google_maps_flutter_platform_interface: ^2.9.0 maps_example_dart: path: ../shared/maps_example_dart/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml index 810adc8ee21..ed831fa3dcf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../../../ - google_maps_flutter_platform_interface: ^2.7.0 + google_maps_flutter_platform_interface: ^2.9.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.h new file mode 100644 index 00000000000..0c3407504a7 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +@import GoogleMapsUtils; + +#import "messages.g.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Controller of a single Heatmap on the map. +@interface FLTGoogleMapHeatmapController : NSObject + +/// Initializes an instance of this class with a heatmap tile layer, a map view, and additional +/// configuration options. +/// +/// @param heatmapTileLayer The heatmap tile layer (of type GMUHeatmapTileLayer) that will be used +/// to display heatmap data on the map. +/// @param mapView The map view (of type GMSMapView) where the heatmap layer will be overlaid. +/// @param options A dictionary (NSDictionary) containing any additional options or configuration +/// settings for customizing the heatmap layer. The options dictionary is expected to have the +/// following structure: +/// +/// @code +/// { +/// "heatmapId": NSString, +/// "data": NSArray, // Array of serialized weighted lat/lng +/// "gradient": NSDictionary?, // Serialized heatmap gradient +/// "opacity": NSNumber, +/// "radius": NSNumber, +/// "minimumZoomIntensity": NSNumber, +/// "maximumZoomIntensity": NSNumber +/// } +/// @endcode +/// +/// @return An initialized instance of this class, configured with the specified heatmap tile layer, +/// map view, and additional options. +- (instancetype)initWithHeatmapTileLayer:(GMUHeatmapTileLayer *)heatmapTileLayer + mapView:(GMSMapView *)mapView + options:(NSDictionary *)options; + +/// Removes this heatmap from the map. +- (void)removeHeatmap; + +/// Clears the tile cache in order to visually udpate this heatmap. +- (void)clearTileCache; +@end + +/// Controller of multiple Heatmaps on the map. +@interface FLTHeatmapsController : NSObject + +/// Initializes the controller with a GMSMapView. +- (instancetype)initWithMapView:(GMSMapView *)mapView; + +/// Adds heatmaps to the map from JSON data. +- (void)addJSONHeatmaps:(NSArray *> *)heatmapsToAdd; + +/// Adds heatmaps to the map. +- (void)addHeatmaps:(NSArray *)heatmapsToAdd; + +/// Updates heatmaps on the map. +- (void)changeHeatmaps:(NSArray *)heatmapsToChange; + +/// Removes heatmaps from the map. +- (void)removeHeatmapsWithIdentifiers:(NSArray *)identifiers; + +/// Returns true if a heatmap with the given identifier exists on the map. +- (BOOL)hasHeatmapWithIdentifier:(NSString *)identifier; + +/// Returns the JSON data of the heatmap with the given identifier. The JSON structure is equivalent +/// to the `options` parameter above. +- (nullable NSDictionary *)heatmapInfoWithIdentifier:(NSString *)identifier; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m new file mode 100644 index 00000000000..38b6b35ce55 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m @@ -0,0 +1,178 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTGoogleMapHeatmapController.h" +#import "FLTGoogleMapJSONConversions.h" +@import GoogleMapsUtils; + +@interface FLTGoogleMapHeatmapController () + +/// The heatmap tile layer this controller handles. +@property(nonatomic, strong) GMUHeatmapTileLayer *heatmapTileLayer; + +/// The GMSMapView to which the heatmaps are added. +@property(nonatomic, weak) GMSMapView *mapView; + +@end + +@implementation FLTGoogleMapHeatmapController +- (instancetype)initWithHeatmapTileLayer:(GMUHeatmapTileLayer *)heatmapTileLayer + mapView:(GMSMapView *)mapView + options:(NSDictionary *)options { + self = [super init]; + if (self) { + _heatmapTileLayer = heatmapTileLayer; + _mapView = mapView; + + [FLTGoogleMapHeatmapController interpretHeatmapOptions:_heatmapTileLayer + mapView:_mapView + options:options]; + } + return self; +} + +- (void)removeHeatmap { + _heatmapTileLayer.map = nil; +} + +- (void)clearTileCache { + [_heatmapTileLayer clearTileCache]; +} + +- (void)interpretHeatmapOptions:(NSDictionary *)data { + [FLTGoogleMapHeatmapController interpretHeatmapOptions:_heatmapTileLayer + mapView:_mapView + options:data]; +} + ++ (void)interpretHeatmapOptions:(GMUHeatmapTileLayer *)heatmapTileLayer + mapView:(GMSMapView *)mapView + options:(NSDictionary *)options { + id weightedData = options[kHeatmapDataKey]; + if ([weightedData isKindOfClass:[NSArray class]]) { + heatmapTileLayer.weightedData = + [FLTGoogleMapJSONConversions weightedDataFromArray:weightedData]; + } + + id gradient = options[kHeatmapGradientKey]; + if ([gradient isKindOfClass:[NSDictionary class]]) { + heatmapTileLayer.gradient = [FLTGoogleMapJSONConversions gradientFromDictionary:gradient]; + } + + id opacity = options[kHeatmapOpacityKey]; + if ([opacity isKindOfClass:[NSNumber class]]) { + heatmapTileLayer.opacity = [opacity doubleValue]; + } + + id radius = options[kHeatmapRadiusKey]; + if ([radius isKindOfClass:[NSNumber class]]) { + heatmapTileLayer.radius = [radius intValue]; + } + + id minimumZoomIntensity = options[kHeatmapMinimumZoomIntensityKey]; + if ([minimumZoomIntensity isKindOfClass:[NSNumber class]]) { + heatmapTileLayer.minimumZoomIntensity = [minimumZoomIntensity intValue]; + } + + id maximumZoomIntensity = options[kHeatmapMaximumZoomIntensityKey]; + if ([maximumZoomIntensity isKindOfClass:[NSNumber class]]) { + heatmapTileLayer.maximumZoomIntensity = [maximumZoomIntensity intValue]; + } + + // The map must be set each time for options to update. + heatmapTileLayer.map = mapView; +} +@end + +@interface FLTHeatmapsController () + +/// A map from heatmapId to the controller that manages it. +@property(nonatomic, strong) + NSMutableDictionary *heatmapIdToController; + +/// The map view owned by GoogmeMapController. +@property(nonatomic, weak) GMSMapView *mapView; + +@end + +@implementation FLTHeatmapsController +- (instancetype)initWithMapView:(GMSMapView *)mapView { + self = [super init]; + if (self) { + _mapView = mapView; + _heatmapIdToController = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)addJSONHeatmaps:(NSArray *> *)heatmapsToAdd { + for (NSDictionary *heatmap in heatmapsToAdd) { + NSString *heatmapId = [FLTHeatmapsController identifierForHeatmap:heatmap]; + GMUHeatmapTileLayer *heatmapTileLayer = [[GMUHeatmapTileLayer alloc] init]; + FLTGoogleMapHeatmapController *controller = + [[FLTGoogleMapHeatmapController alloc] initWithHeatmapTileLayer:heatmapTileLayer + mapView:_mapView + options:heatmap]; + _heatmapIdToController[heatmapId] = controller; + } +} + +- (void)addHeatmaps:(NSArray *)heatmapsToAdd { + for (FGMPlatformHeatmap *heatmap in heatmapsToAdd) { + NSString *heatmapId = [FLTHeatmapsController identifierForHeatmap:heatmap.json]; + GMUHeatmapTileLayer *heatmapTileLayer = [[GMUHeatmapTileLayer alloc] init]; + FLTGoogleMapHeatmapController *controller = + [[FLTGoogleMapHeatmapController alloc] initWithHeatmapTileLayer:heatmapTileLayer + mapView:_mapView + options:heatmap.json]; + _heatmapIdToController[heatmapId] = controller; + } +} + +- (void)changeHeatmaps:(NSArray *)heatmapsToChange { + for (FGMPlatformHeatmap *heatmap in heatmapsToChange) { + NSString *heatmapId = [FLTHeatmapsController identifierForHeatmap:heatmap.json]; + FLTGoogleMapHeatmapController *controller = _heatmapIdToController[heatmapId]; + + [controller interpretHeatmapOptions:heatmap.json]; + [controller clearTileCache]; + } +} + +- (void)removeHeatmapsWithIdentifiers:(NSArray *)identifiers { + for (NSString *heatmapId in identifiers) { + FLTGoogleMapHeatmapController *controller = _heatmapIdToController[heatmapId]; + if (!controller) { + continue; + } + [controller removeHeatmap]; + [_heatmapIdToController removeObjectForKey:heatmapId]; + } +} + +- (BOOL)hasHeatmapWithIdentifier:(NSString *)identifier { + return _heatmapIdToController[identifier] != nil; +} + +- (nullable NSDictionary *)heatmapInfoWithIdentifier:(NSString *)identifier { + FLTGoogleMapHeatmapController *heatmapController = self.heatmapIdToController[identifier]; + if (heatmapController) { + return @{ + kHeatmapDataKey : [FLTGoogleMapJSONConversions + arrayFromWeightedData:heatmapController.heatmapTileLayer.weightedData], + kHeatmapGradientKey : [FLTGoogleMapJSONConversions + dictionaryFromGradient:heatmapController.heatmapTileLayer.gradient], + kHeatmapOpacityKey : @(heatmapController.heatmapTileLayer.opacity), + kHeatmapRadiusKey : @(heatmapController.heatmapTileLayer.radius), + kHeatmapMinimumZoomIntensityKey : @(heatmapController.heatmapTileLayer.minimumZoomIntensity), + kHeatmapMaximumZoomIntensityKey : @(heatmapController.heatmapTileLayer.maximumZoomIntensity) + }; + } + return nil; +} + ++ (NSString *)identifierForHeatmap:(NSDictionary *)heatmap { + return heatmap[kHeatmapIdKey]; +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index 5d45ddd7be7..403e93af859 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -3,7 +3,9 @@ // found in the LICENSE file. #import +#import #import +@import GoogleMapsUtils; #import "messages.g.h" @@ -34,16 +36,35 @@ extern FGMPlatformCameraPosition *FGMGetPigeonCameraPositionForPosition( @interface FLTGoogleMapJSONConversions : NSObject +extern NSString *const kHeatmapsToAddKey; +extern NSString *const kHeatmapIdKey; +extern NSString *const kHeatmapDataKey; +extern NSString *const kHeatmapGradientKey; +extern NSString *const kHeatmapOpacityKey; +extern NSString *const kHeatmapRadiusKey; +extern NSString *const kHeatmapMinimumZoomIntensityKey; +extern NSString *const kHeatmapMaximumZoomIntensityKey; +extern NSString *const kHeatmapGradientColorsKey; +extern NSString *const kHeatmapGradientStartPointsKey; +extern NSString *const kHeatmapGradientColorMapSizeKey; + + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong; + (CGPoint)pointFromArray:(NSArray *)array; + (NSArray *)arrayFromLocation:(CLLocationCoordinate2D)location; + (UIColor *)colorFromRGBA:(NSNumber *)data; ++ (NSNumber *)RGBAFromColor:(UIColor *)color; + (NSArray *)pointsFromLatLongs:(NSArray *)data; + (NSArray *> *)holesFromPointsArray:(NSArray *)data; + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)channelValue; + (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs; + (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)value; + (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue; ++ (nullable GMUWeightedLatLng *)weightedLatLngFromArray:(NSArray *)data; ++ (NSArray *)arrayFromWeightedLatLng:(GMUWeightedLatLng *)weightedLatLng; ++ (NSArray *)weightedDataFromArray:(NSArray *> *)data; ++ (NSArray *> *)arrayFromWeightedData:(NSArray *)weightedData; ++ (GMUGradient *)gradientFromDictionary:(NSDictionary *)data; ++ (NSDictionary *)dictionaryFromGradient:(GMUGradient *)gradient; /// Return GMS strokestyle object array populated using the patterns and stroke colors passed in. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 56db3fbee0c..69ceb73620e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -41,6 +41,19 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng @implementation FLTGoogleMapJSONConversions +// These constants must match the corresponding constants in serialization.dart +NSString *const kHeatmapsToAddKey = @"heatmapsToAdd"; +NSString *const kHeatmapIdKey = @"heatmapId"; +NSString *const kHeatmapDataKey = @"data"; +NSString *const kHeatmapGradientKey = @"gradient"; +NSString *const kHeatmapOpacityKey = @"opacity"; +NSString *const kHeatmapRadiusKey = @"radius"; +NSString *const kHeatmapMinimumZoomIntensityKey = @"minimumZoomIntensity"; +NSString *const kHeatmapMaximumZoomIntensityKey = @"maximumZoomIntensity"; +NSString *const kHeatmapGradientColorsKey = @"colors"; +NSString *const kHeatmapGradientStartPointsKey = @"startPoints"; +NSString *const kHeatmapGradientColorMapSizeKey = @"colorMapSize"; + + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong { return CLLocationCoordinate2DMake([latlong[0] doubleValue], [latlong[1] doubleValue]); } @@ -61,6 +74,14 @@ + (UIColor *)colorFromRGBA:(NSNumber *)numberColor { alpha:((float)((value & 0xFF000000) >> 24)) / 255.0]; } ++ (NSNumber *)RGBAFromColor:(UIColor *)color { + CGFloat red, green, blue, alpha; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + unsigned long value = ((unsigned long)(alpha * 255) << 24) | ((unsigned long)(red * 255) << 16) | + ((unsigned long)(green * 255) << 8) | ((unsigned long)(blue * 255)); + return @(value); +} + + (NSArray *)pointsFromLatLongs:(NSArray *)data { NSMutableArray *points = [[NSMutableArray alloc] init]; for (unsigned i = 0; i < [data count]; i++) { @@ -163,4 +184,69 @@ + (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue { return lengths; } + ++ (GMUWeightedLatLng *)weightedLatLngFromArray:(NSArray *)data { + NSAssert(data.count == 2, @"WeightedLatLng data must have length of 2"); + if (data.count != 2) { + return nil; + } + return [[GMUWeightedLatLng alloc] + initWithCoordinate:[FLTGoogleMapJSONConversions locationFromLatLong:data[0]] + intensity:[data[1] doubleValue]]; +} + ++ (NSArray *)arrayFromWeightedLatLng:(GMUWeightedLatLng *)weightedLatLng { + GMSMapPoint point = {weightedLatLng.point.x, weightedLatLng.point.y}; + return @[ + [FLTGoogleMapJSONConversions arrayFromLocation:GMSUnproject(point)], @(weightedLatLng.intensity) + ]; +} + ++ (NSArray *)weightedDataFromArray:(NSArray *> *)data { + NSMutableArray *weightedData = + [[NSMutableArray alloc] initWithCapacity:data.count]; + for (NSArray *item in data) { + GMUWeightedLatLng *weightedLatLng = [FLTGoogleMapJSONConversions weightedLatLngFromArray:item]; + if (weightedLatLng == nil) continue; + [weightedData addObject:weightedLatLng]; + } + + return weightedData; +} + ++ (NSArray *> *)arrayFromWeightedData:(NSArray *)weightedData { + NSMutableArray *data = [[NSMutableArray alloc] initWithCapacity:weightedData.count]; + for (GMUWeightedLatLng *weightedLatLng in weightedData) { + [data addObject:[FLTGoogleMapJSONConversions arrayFromWeightedLatLng:weightedLatLng]]; + } + + return data; +} + ++ (GMUGradient *)gradientFromDictionary:(NSDictionary *)data { + NSArray *colorData = data[kHeatmapGradientColorsKey]; + NSMutableArray *colors = [[NSMutableArray alloc] initWithCapacity:colorData.count]; + for (NSNumber *colorCode in colorData) { + [colors addObject:[FLTGoogleMapJSONConversions colorFromRGBA:colorCode]]; + } + + return [[GMUGradient alloc] initWithColors:colors + startPoints:data[kHeatmapGradientStartPointsKey] + colorMapSize:[data[kHeatmapGradientColorMapSizeKey] intValue]]; +} + ++ (NSDictionary *)dictionaryFromGradient:(GMUGradient *)gradient { + NSMutableArray *colorCodes = + [[NSMutableArray alloc] initWithCapacity:gradient.colors.count]; + for (UIColor *color in gradient.colors) { + [colorCodes addObject:[FLTGoogleMapJSONConversions RGBAFromColor:color]]; + } + + return @{ + kHeatmapGradientColorsKey : colorCodes, + kHeatmapGradientStartPointsKey : gradient.startPoints, + kHeatmapGradientColorMapSizeKey : @(gradient.mapSize) + }; +} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 8a69f712ba3..0f1ca2e3ad9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "GoogleMapController.h" +#import "FLTGoogleMapHeatmapController.h" #import "FLTGoogleMapJSONConversions.h" #import "FLTGoogleMapTileOverlayController.h" #import "messages.g.h" @@ -119,6 +120,9 @@ @interface FLTGoogleMapController () @property(nonatomic, strong) FLTPolygonsController *polygonsController; @property(nonatomic, strong) FLTPolylinesController *polylinesController; @property(nonatomic, strong) FLTCirclesController *circlesController; + +// The controller that handles heatmaps +@property(nonatomic, strong) FLTHeatmapsController *heatmapsController; @property(nonatomic, strong) FLTTileOverlaysController *tileOverlaysController; // The resulting error message, if any, from the last attempt to set the map style. // This is used to provide access to errors after the fact, since the map style is generally set at @@ -183,6 +187,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView _circlesController = [[FLTCirclesController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler registrar:registrar]; + _heatmapsController = [[FLTHeatmapsController alloc] initWithMapView:_mapView]; _tileOverlaysController = [[FLTTileOverlaysController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler @@ -203,6 +208,10 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView if ([circlesToAdd isKindOfClass:[NSArray class]]) { [_circlesController addJSONCircles:circlesToAdd]; } + id heatmapsToAdd = args[kHeatmapsToAddKey]; + if ([heatmapsToAdd isKindOfClass:[NSArray class]]) { + [_heatmapsController addJSONHeatmaps:heatmapsToAdd]; + } id tileOverlaysToAdd = args[@"tileOverlaysToAdd"]; if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { [_tileOverlaysController addJSONTileOverlays:tileOverlaysToAdd]; @@ -532,6 +541,15 @@ - (void)updateCirclesByAdding:(nonnull NSArray *)toAdd [self.controller.circlesController removeCirclesWithIdentifiers:idsToRemove]; } +- (void)updateHeatmapsByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.heatmapsController addHeatmaps:toAdd]; + [self.controller.heatmapsController changeHeatmaps:toChange]; + [self.controller.heatmapsController removeHeatmapsWithIdentifiers:idsToRemove]; +} + - (void)updateWithMapConfiguration:(nonnull FGMPlatformMapConfiguration *)configuration error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { [self.controller interpretMapOptions:configuration.json]; @@ -741,8 +759,8 @@ - (nullable NSNumber *)areZoomGesturesEnabledWithError: } - (nullable FGMPlatformTileLayer *) - getInfoForTileOverlayWithIdentifier:(nonnull NSString *)tileOverlayId - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + tileOverlayWithIdentifier:(nonnull NSString *)tileOverlayId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { GMSTileLayer *layer = [self.controller.tileOverlaysController tileOverlayWithIdentifier:tileOverlayId].layer; if (!layer) { @@ -754,6 +772,17 @@ - (nullable NSNumber *)areZoomGesturesEnabledWithError: zIndex:layer.zIndex]; } +- (nullable FGMPlatformHeatmap *) + heatmapWithIdentifier:(nonnull NSString *)heatmapId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSDictionary *heatmapInfo = + [self.controller.heatmapsController heatmapInfoWithIdentifier:heatmapId]; + if (!heatmapInfo) { + return nil; + } + return [FGMPlatformHeatmap makeWithJson:heatmapInfo]; +} + - (nullable NSNumber *)isCompassEnabledWithError: (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return @(self.controller.mapView.settings.compassButton); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h index 4f331de069b..de245e43bd3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #import +#import #import #import #import diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index 6dd112c6baa..7752ff0f4ae 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @class FGMPlatformCameraPosition; @class FGMPlatformCameraUpdate; @class FGMPlatformCircle; +@class FGMPlatformHeatmap; @class FGMPlatformMarker; @class FGMPlatformPolygon; @class FGMPlatformPolyline; @@ -48,7 +49,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The update data, as JSON. This should only be set from -/// CameraUpdate.toJson, and the native code must intepret it according to the +/// CameraUpdate.toJson, and the native code must interpret it according to the /// internal implementation details of the CameraUpdate class. @property(nonatomic, strong) id json; @end @@ -59,7 +60,18 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The circle data, as JSON. This should only be set from -/// Circle.toJson, and the native code must intepret it according to the +/// Circle.toJson, and the native code must interpret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the Heatmap class. +@interface FGMPlatformHeatmap : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The heatmap data, as JSON. This should only be set from +/// Heatmap.toJson, and the native code must interpret it according to the /// internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -70,7 +82,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The marker data, as JSON. This should only be set from -/// Marker.toJson, and the native code must intepret it according to the +/// Marker.toJson, and the native code must interpret it according to the /// internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -81,7 +93,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The polygon data, as JSON. This should only be set from -/// Polygon.toJson, and the native code must intepret it according to the +/// Polygon.toJson, and the native code must interpret it according to the /// internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -92,7 +104,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The polyline data, as JSON. This should only be set from -/// Polyline.toJson, and the native code must intepret it according to the +/// Polyline.toJson, and the native code must interpret it according to the /// internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -115,7 +127,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The tile overlay data, as JSON. This should only be set from -/// TileOverlay.toJson, and the native code must intepret it according to the +/// TileOverlay.toJson, and the native code must interpret it according to the /// internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -145,7 +157,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithJson:(id)json; /// The configuration options, as JSON. This should only be set from -/// _jsonForMapConfiguration, and the native code must intepret it according +/// _jsonForMapConfiguration, and the native code must interpret it according /// to the internal implementation details of that method. @property(nonatomic, strong) id json; @end @@ -202,6 +214,11 @@ NSObject *FGMGetMessagesCodec(void); changing:(NSArray *)toChange removing:(NSArray *)idsToRemove error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of heatmaps on the map. +- (void)updateHeatmapsByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; /// Updates the set of markers on the map. - (void)updateMarkersByAdding:(NSArray *)toAdd changing:(NSArray *)toChange @@ -358,9 +375,11 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger - (nullable NSNumber *)isMyLocationButtonEnabledWithError:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable NSNumber *)isTrafficEnabledWithError:(FlutterError *_Nullable *_Nonnull)error; -- (nullable FGMPlatformTileLayer *) - getInfoForTileOverlayWithIdentifier:(NSString *)tileOverlayId - error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FGMPlatformTileLayer *)tileOverlayWithIdentifier:(NSString *)tileOverlayId + error: + (FlutterError *_Nullable *_Nonnull)error; +- (nullable FGMPlatformHeatmap *)heatmapWithIdentifier:(NSString *)heatmapId + error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable FGMPlatformZoomRange *)zoomRange:(FlutterError *_Nullable *_Nonnull)error; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index 4836d517b89..d541e4c8ade 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -57,6 +57,12 @@ + (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformHeatmap () ++ (FGMPlatformHeatmap *)fromList:(NSArray *)list; ++ (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformMarker () + (FGMPlatformMarker *)fromList:(NSArray *)list; + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list; @@ -198,6 +204,27 @@ + (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformHeatmap ++ (instancetype)makeWithJson:(id)json { + FGMPlatformHeatmap *pigeonResult = [[FGMPlatformHeatmap alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformHeatmap *)fromList:(NSArray *)list { + FGMPlatformHeatmap *pigeonResult = [[FGMPlatformHeatmap alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformHeatmap fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformMarker + (instancetype)makeWithJson:(id)json { FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; @@ -474,26 +501,28 @@ - (nullable id)readValueOfType:(UInt8)type { case 131: return [FGMPlatformCircle fromList:[self readValue]]; case 132: - return [FGMPlatformMarker fromList:[self readValue]]; + return [FGMPlatformHeatmap fromList:[self readValue]]; case 133: - return [FGMPlatformPolygon fromList:[self readValue]]; + return [FGMPlatformMarker fromList:[self readValue]]; case 134: - return [FGMPlatformPolyline fromList:[self readValue]]; + return [FGMPlatformPolygon fromList:[self readValue]]; case 135: - return [FGMPlatformTile fromList:[self readValue]]; + return [FGMPlatformPolyline fromList:[self readValue]]; case 136: - return [FGMPlatformTileOverlay fromList:[self readValue]]; + return [FGMPlatformTile fromList:[self readValue]]; case 137: - return [FGMPlatformLatLng fromList:[self readValue]]; + return [FGMPlatformTileOverlay fromList:[self readValue]]; case 138: - return [FGMPlatformLatLngBounds fromList:[self readValue]]; + return [FGMPlatformLatLng fromList:[self readValue]]; case 139: - return [FGMPlatformMapConfiguration fromList:[self readValue]]; + return [FGMPlatformLatLngBounds fromList:[self readValue]]; case 140: - return [FGMPlatformPoint fromList:[self readValue]]; + return [FGMPlatformMapConfiguration fromList:[self readValue]]; case 141: - return [FGMPlatformTileLayer fromList:[self readValue]]; + return [FGMPlatformPoint fromList:[self readValue]]; case 142: + return [FGMPlatformTileLayer fromList:[self readValue]]; + case 143: return [FGMPlatformZoomRange fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -514,39 +543,42 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FGMPlatformCircle class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { + } else if ([value isKindOfClass:[FGMPlatformHeatmap class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { + } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { [self writeByte:133]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTile class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { + } else if ([value isKindOfClass:[FGMPlatformTile class]]) { [self writeByte:136]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { [self writeByte:137]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { + } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { [self writeByte:140]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { [self writeByte:141]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { [self writeByte:142]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + [self writeByte:143]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -664,6 +696,36 @@ void SetUpFGMMapsApiWithSuffix(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Updates the set of heatmaps on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updateHeatmaps", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateHeatmapsByAdding:changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updateHeatmapsByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updateHeatmapsByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Updates the set of markers on the map. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1688,16 +1750,40 @@ void SetUpFGMMapsInspectorApiWithSuffix(id binaryMesseng binaryMessenger:binaryMessenger codec:FGMGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(getInfoForTileOverlayWithIdentifier:error:)], + NSCAssert([api respondsToSelector:@selector(tileOverlayWithIdentifier:error:)], @"FGMMapsInspectorApi api (%@) doesn't respond to " - @"@selector(getInfoForTileOverlayWithIdentifier:error:)", + @"@selector(tileOverlayWithIdentifier:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_tileOverlayId = GetNullableObjectAtIndex(args, 0); FlutterError *error; - FGMPlatformTileLayer *output = [api getInfoForTileOverlayWithIdentifier:arg_tileOverlayId - error:&error]; + FGMPlatformTileLayer *output = [api tileOverlayWithIdentifier:arg_tileOverlayId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsInspectorApi.getHeatmapInfo", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(heatmapWithIdentifier:error:)], + @"FGMMapsInspectorApi api (%@) doesn't respond to " + @"@selector(heatmapWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_heatmapId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + FGMPlatformHeatmap *output = [api heatmapWithIdentifier:arg_heatmapId error:&error]; callback(wrapResult(output, error)); }]; } else { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec index ce652f3b41b..2715a7dcb15 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec @@ -25,8 +25,17 @@ Downloaded by pub (not CocoaPods). # Versions earlier than 8.4 can't be supported because that's the first version # that supports privacy manifests. s.dependency 'GoogleMaps', '>= 8.4', '< 10.0' + # Google-Maps-iOS-Utils 5.x supports GoogleMaps 8.x and iOS 14.0+ + # Google-Maps-iOS-Utils 6.x supports GoogleMaps 9.x and iOS 15.0+ + s.dependency 'Google-Maps-iOS-Utils', '>= 5.0', '< 7.0' s.static_framework = true s.platform = :ios, '14.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + # "Google-Maps-iOS-Utils" is static and contains Swift classes. + # Find the Swift runtime when these plugins are built as libraries without `use_frameworks!` + s.swift_version = '5.0' + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '$(inherited) /usr/lib/swift', + } s.resource_bundles = {'google_maps_flutter_ios_privacy' => ['Resources/PrivacyInfo.xcprivacy']} end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart index 990505bcb13..35a5e73c30f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'messages.g.dart'; +import 'serialization.dart'; /// An Android of implementation of [GoogleMapsInspectorPlatform]. @visibleForTesting @@ -73,6 +74,34 @@ class GoogleMapsInspectorIOS extends GoogleMapsInspectorPlatform { ); } + @override + bool supportsGettingHeatmapInfo() => true; + + @override + Future getHeatmapInfo(HeatmapId heatmapId, + {required int mapId}) async { + final PlatformHeatmap? heatmapInfo = + await _inspectorProvider(mapId)!.getHeatmapInfo(heatmapId.value); + if (heatmapInfo == null) { + return null; + } + + final Map json = + (heatmapInfo.json as Map).cast(); + return Heatmap( + heatmapId: heatmapId, + data: (json['data']! as List) + .map(deserializeWeightedLatLng) + .whereType() + .toList(), + gradient: deserializeHeatmapGradient(json['gradient']), + opacity: json['opacity']! as double, + radius: HeatmapRadius.fromPixels(json['radius']! as int), + minimumZoomIntensity: json['minimumZoomIntensity']! as int, + maximumZoomIntensity: json['maximumZoomIntensity']! as int, + ); + } + @override Future isCompassEnabled({required int mapId}) async { return _inspectorProvider(mapId)!.isCompassEnabled(); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index 8e9a96d094b..483b1e7b19d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -13,6 +13,7 @@ import 'package:stream_transform/stream_transform.dart'; import 'google_map_inspector_ios.dart'; import 'messages.g.dart'; +import 'serialization.dart'; // TODO(stuartmorgan): Remove the dependency on platform interface toJson // methods. Channel serialization details should all be package-internal. @@ -273,6 +274,20 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { ); } + @override + Future updateHeatmaps( + HeatmapUpdates heatmapUpdates, { + required int mapId, + }) { + return _hostApi(mapId).updateHeatmaps( + heatmapUpdates.heatmapsToAdd.map(_platformHeatmapFromHeatmap).toList(), + heatmapUpdates.heatmapsToChange.map(_platformHeatmapFromHeatmap).toList(), + heatmapUpdates.heatmapIdsToRemove + .map((HeatmapId id) => id.value) + .toList(), + ); + } + @override Future updateTileOverlays({ required Set newTileOverlays, @@ -421,6 +436,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { 'polygonsToAdd': serializePolygonSet(mapObjects.polygons), 'polylinesToAdd': serializePolylineSet(mapObjects.polylines), 'circlesToAdd': serializeCircleSet(mapObjects.circles), + 'heatmapsToAdd': mapObjects.heatmaps.map(serializeHeatmap).toList(), 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays), }; @@ -535,6 +551,10 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return PlatformCircle(json: circle.toJson()); } + static PlatformHeatmap _platformHeatmapFromHeatmap(Heatmap heatmap) { + return PlatformHeatmap(json: heatmap.toJson()); + } + static PlatformMarker _platformMarkerFromMarker(Marker marker) { return PlatformMarker(json: marker.toJson()); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index be309537588..e5041ef24e1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -73,7 +73,7 @@ class PlatformCameraUpdate { }); /// The update data, as JSON. This should only be set from - /// CameraUpdate.toJson, and the native code must intepret it according to the + /// CameraUpdate.toJson, and the native code must interpret it according to the /// internal implementation details of the CameraUpdate class. Object json; @@ -98,7 +98,7 @@ class PlatformCircle { }); /// The circle data, as JSON. This should only be set from - /// Circle.toJson, and the native code must intepret it according to the + /// Circle.toJson, and the native code must interpret it according to the /// internal implementation details of that method. Object json; @@ -116,6 +116,31 @@ class PlatformCircle { } } +/// Pigeon equivalent of the Heatmap class. +class PlatformHeatmap { + PlatformHeatmap({ + required this.json, + }); + + /// The heatmap data, as JSON. This should only be set from + /// Heatmap.toJson, and the native code must interpret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformHeatmap decode(Object result) { + result as List; + return PlatformHeatmap( + json: result[0]!, + ); + } +} + /// Pigeon equivalent of the Marker class. class PlatformMarker { PlatformMarker({ @@ -123,7 +148,7 @@ class PlatformMarker { }); /// The marker data, as JSON. This should only be set from - /// Marker.toJson, and the native code must intepret it according to the + /// Marker.toJson, and the native code must interpret it according to the /// internal implementation details of that method. Object json; @@ -148,7 +173,7 @@ class PlatformPolygon { }); /// The polygon data, as JSON. This should only be set from - /// Polygon.toJson, and the native code must intepret it according to the + /// Polygon.toJson, and the native code must interpret it according to the /// internal implementation details of that method. Object json; @@ -173,7 +198,7 @@ class PlatformPolyline { }); /// The polyline data, as JSON. This should only be set from - /// Polyline.toJson, and the native code must intepret it according to the + /// Polyline.toJson, and the native code must interpret it according to the /// internal implementation details of that method. Object json; @@ -230,7 +255,7 @@ class PlatformTileOverlay { }); /// The tile overlay data, as JSON. This should only be set from - /// TileOverlay.toJson, and the native code must intepret it according to the + /// TileOverlay.toJson, and the native code must interpret it according to the /// internal implementation details of that method. Object json; @@ -309,7 +334,7 @@ class PlatformMapConfiguration { }); /// The configuration options, as JSON. This should only be set from - /// _jsonForMapConfiguration, and the native code must intepret it according + /// _jsonForMapConfiguration, and the native code must interpret it according /// to the internal implementation details of that method. Object json; @@ -431,39 +456,42 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformCircle) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PlatformMarker) { + } else if (value is PlatformHeatmap) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PlatformPolygon) { + } else if (value is PlatformMarker) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PlatformPolyline) { + } else if (value is PlatformPolygon) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is PlatformTile) { + } else if (value is PlatformPolyline) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is PlatformTileOverlay) { + } else if (value is PlatformTile) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLng) { + } else if (value is PlatformTileOverlay) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformLatLng) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PlatformMapConfiguration) { + } else if (value is PlatformLatLngBounds) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformMapConfiguration) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformPoint) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformTileLayer) { buffer.putUint8(142); writeValue(buffer, value.encode()); + } else if (value is PlatformZoomRange) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -479,26 +507,28 @@ class _PigeonCodec extends StandardMessageCodec { case 131: return PlatformCircle.decode(readValue(buffer)!); case 132: - return PlatformMarker.decode(readValue(buffer)!); + return PlatformHeatmap.decode(readValue(buffer)!); case 133: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformMarker.decode(readValue(buffer)!); case 134: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 135: - return PlatformTile.decode(readValue(buffer)!); + return PlatformPolyline.decode(readValue(buffer)!); case 136: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformTile.decode(readValue(buffer)!); case 137: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformTileOverlay.decode(readValue(buffer)!); case 138: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformLatLng.decode(readValue(buffer)!); case 139: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformLatLngBounds.decode(readValue(buffer)!); case 140: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformMapConfiguration.decode(readValue(buffer)!); case 141: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformPoint.decode(readValue(buffer)!); case 142: + return PlatformTileLayer.decode(readValue(buffer)!); + case 143: return PlatformZoomRange.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -603,6 +633,32 @@ class MapsApi { } } + /// Updates the set of heatmaps on the map. + Future updateHeatmaps(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateHeatmaps$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + /// Updates the set of markers on the map. Future updateMarkers(List toAdd, List toChange, List idsToRemove) async { @@ -1794,6 +1850,30 @@ class MapsInspectorApi { } } + Future getHeatmapInfo(String heatmapId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsInspectorApi.getHeatmapInfo$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([heatmapId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as PlatformHeatmap?); + } + } + Future getZoomRange() async { final String __pigeon_channelName = 'dev.flutter.pigeon.google_maps_flutter_ios.MapsInspectorApi.getZoomRange$__pigeon_messageChannelSuffix'; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/serialization.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/serialization.dart new file mode 100644 index 00000000000..8c80f19fb61 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/serialization.dart @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +// These constants must match the corresponding constants in FLTGoogleMapJSONConversions.m +const String _heatmapIdKey = 'heatmapId'; +const String _heatmapDataKey = 'data'; +const String _heatmapGradientKey = 'gradient'; +const String _heatmapOpacityKey = 'opacity'; +const String _heatmapRadiusKey = 'radius'; +const String _heatmapMinimumZoomIntensityKey = 'minimumZoomIntensity'; +const String _heatmapMaximumZoomIntensityKey = 'maximumZoomIntensity'; +const String _heatmapGradientColorsKey = 'colors'; +const String _heatmapGradientStartPointsKey = 'startPoints'; +const String _heatmapGradientColorMapSizeKey = 'colorMapSize'; + +void _addIfNonNull(Map map, String fieldName, Object? value) { + if (value != null) { + map[fieldName] = value; + } +} + +/// Serialize [Heatmap] +Object serializeHeatmap(Heatmap heatmap) { + final Map json = {}; + + _addIfNonNull(json, _heatmapIdKey, heatmap.heatmapId.value); + _addIfNonNull( + json, + _heatmapDataKey, + heatmap.data.map(serializeWeightedLatLng).toList(), + ); + + final HeatmapGradient? gradient = heatmap.gradient; + if (gradient != null) { + _addIfNonNull( + json, _heatmapGradientKey, serializeHeatmapGradient(gradient)); + } + _addIfNonNull(json, _heatmapOpacityKey, heatmap.opacity); + _addIfNonNull(json, _heatmapRadiusKey, heatmap.radius.radius); + _addIfNonNull( + json, _heatmapMinimumZoomIntensityKey, heatmap.minimumZoomIntensity); + _addIfNonNull( + json, _heatmapMaximumZoomIntensityKey, heatmap.maximumZoomIntensity); + + return json; +} + +/// Serialize [WeightedLatLng] +Object serializeWeightedLatLng(WeightedLatLng wll) { + return [serializeLatLng(wll.point), wll.weight]; +} + +/// Deserialize [WeightedLatLng] +WeightedLatLng? deserializeWeightedLatLng(Object? json) { + if (json == null) { + return null; + } + assert(json is List && json.length == 2); + final List list = json as List; + final LatLng latLng = deserializeLatLng(list[0])!; + return WeightedLatLng(latLng, weight: list[1] as double); +} + +/// Serialize [LatLng] +Object serializeLatLng(LatLng latLng) { + return [latLng.latitude, latLng.longitude]; +} + +/// Deserialize [LatLng] +LatLng? deserializeLatLng(Object? json) { + if (json == null) { + return null; + } + assert(json is List && json.length == 2); + final List list = json as List; + return LatLng(list[0]! as double, list[1]! as double); +} + +/// Serialize [HeatmapGradient] +Object serializeHeatmapGradient(HeatmapGradient gradient) { + final Map json = {}; + + _addIfNonNull( + json, + _heatmapGradientColorsKey, + gradient.colors.map((HeatmapGradientColor e) => e.color.value).toList(), + ); + _addIfNonNull( + json, + _heatmapGradientStartPointsKey, + gradient.colors.map((HeatmapGradientColor e) => e.startPoint).toList(), + ); + _addIfNonNull(json, _heatmapGradientColorMapSizeKey, gradient.colorMapSize); + + return json; +} + +/// Deserialize [HeatmapGradient] +HeatmapGradient? deserializeHeatmapGradient(Object? json) { + if (json == null) { + return null; + } + assert(json is Map); + final Map map = (json as Map).cast(); + final List colors = (map[_heatmapGradientColorsKey]! as List) + .whereType() + .map((int e) => Color(e)) + .toList(); + final List startPoints = + (map[_heatmapGradientStartPointsKey]! as List) + .whereType() + .toList(); + final List gradientColors = []; + for (int i = 0; i < colors.length; i++) { + gradientColors.add(HeatmapGradientColor(colors[i], startPoints[i])); + } + return HeatmapGradient( + gradientColors, + colorMapSize: map[_heatmapGradientColorMapSizeKey] as int? ?? 256, + ); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index 19db3aa8502..cd6cab64c91 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -32,7 +32,7 @@ class PlatformCameraUpdate { PlatformCameraUpdate(this.json); /// The update data, as JSON. This should only be set from - /// CameraUpdate.toJson, and the native code must intepret it according to the + /// CameraUpdate.toJson, and the native code must interpret it according to the /// internal implementation details of the CameraUpdate class. // TODO(stuartmorgan): Update the google_maps_platform_interface CameraUpdate // class to provide a structured representation of an update. Currently it @@ -47,7 +47,19 @@ class PlatformCircle { PlatformCircle(this.json); /// The circle data, as JSON. This should only be set from - /// Circle.toJson, and the native code must intepret it according to the + /// Circle.toJson, and the native code must interpret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Heatmap class. +class PlatformHeatmap { + PlatformHeatmap(this.json); + + /// The heatmap data, as JSON. This should only be set from + /// Heatmap.toJson, and the native code must interpret it according to the /// internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -59,7 +71,7 @@ class PlatformMarker { PlatformMarker(this.json); /// The marker data, as JSON. This should only be set from - /// Marker.toJson, and the native code must intepret it according to the + /// Marker.toJson, and the native code must interpret it according to the /// internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -71,7 +83,7 @@ class PlatformPolygon { PlatformPolygon(this.json); /// The polygon data, as JSON. This should only be set from - /// Polygon.toJson, and the native code must intepret it according to the + /// Polygon.toJson, and the native code must interpret it according to the /// internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -83,7 +95,7 @@ class PlatformPolyline { PlatformPolyline(this.json); /// The polyline data, as JSON. This should only be set from - /// Polyline.toJson, and the native code must intepret it according to the + /// Polyline.toJson, and the native code must interpret it according to the /// internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -104,7 +116,7 @@ class PlatformTileOverlay { PlatformTileOverlay(this.json); /// The tile overlay data, as JSON. This should only be set from - /// TileOverlay.toJson, and the native code must intepret it according to the + /// TileOverlay.toJson, and the native code must interpret it according to the /// internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -132,7 +144,7 @@ class PlatformMapConfiguration { PlatformMapConfiguration({required this.json}); /// The configuration options, as JSON. This should only be set from - /// _jsonForMapConfiguration, and the native code must intepret it according + /// _jsonForMapConfiguration, and the native code must interpret it according /// to the internal implementation details of that method. // TODO(stuartmorgan): Replace this with structured data. This exists only to // allow incremental migration to Pigeon. @@ -193,6 +205,14 @@ abstract class MapsApi { void updateCircles(List toAdd, List toChange, List idsToRemove); + /// Updates the set of heatmaps on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updateHeatmapsByAdding:changing:removing:') + void updateHeatmaps(List toAdd, + List toChange, List idsToRemove); + /// Updates the set of markers on the map. // TODO(stuartmorgan): Make the generic type non-nullable once supported. // https://github.com/flutter/flutter/issues/97848 @@ -360,8 +380,10 @@ abstract class MapsInspectorApi { bool isCompassEnabled(); bool isMyLocationButtonEnabled(); bool isTrafficEnabled(); - @ObjCSelector('getInfoForTileOverlayWithIdentifier:') + @ObjCSelector('tileOverlayWithIdentifier:') PlatformTileLayer? getTileOverlayInfo(String tileOverlayId); + @ObjCSelector('heatmapWithIdentifier:') + PlatformHeatmap? getHeatmapInfo(String heatmapId); @ObjCSelector('zoomRange') PlatformZoomRange getZoomRange(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index d73df0ee638..2ba981a262b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.10.0 +version: 2.11.0 environment: sdk: ^3.2.3 @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.7.0 + google_maps_flutter_platform_interface: ^2.9.0 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart index ca2dbb086af..c8f9ca752fa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart @@ -99,6 +99,25 @@ class MockMapsApi extends _i1.Mock implements _i2.MapsApi { returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override + _i3.Future updateHeatmaps( + List<_i2.PlatformHeatmap?>? toAdd, + List<_i2.PlatformHeatmap?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateHeatmaps, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + @override _i3.Future updateMarkers( List<_i2.PlatformMarker?>? toAdd,