Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ The `MapViewController` is provided via the `onMapViewControllerCreated` callbac
| `removeCircle(id: string)` | `void` | Remove a circle by its ID |
| `removeGroundOverlay(id: string)` | `void` | Remove a ground overlay by its ID |
| `moveCamera(position: CameraPosition)` | `void` | Move camera to a new position |
| `animateCamera(position: CameraPosition, duration?: number \| null)` | `Promise<void>` | Animate camera to a new position. Defaults to 500 ms when duration is omitted |
| `setZoomLevel(level: number)` | `void` | Set the map zoom level |
| `setPadding(padding: Padding)` | `void` | Set padding on the map |
| `getCameraPosition()` | `Promise<CameraPosition>` | Get the current camera position |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,15 @@ public void moveCamera(Map<String, Object> map) {
return;
}

LatLng latLng = ObjectTranslationUtil.getLatLngFromMap((Map<String, Object>) map.get("target"));
Object targetObj = map.get("target");
if (targetObj == null) {
return;
}

LatLng latLng = ObjectTranslationUtil.getLatLngFromMap((Map<String, Object>) targetObj);
if (latLng == null) {
return;
}

float zoom = (float) CollectionUtil.getDouble("zoom", map, 0);
float tilt = (float) CollectionUtil.getDouble("tilt", map, 0);
Expand All @@ -800,24 +808,38 @@ public void moveCamera(Map<String, Object> map) {
}

public void animateCamera(Map<String, Object> map) {
if (mGoogleMap != null) {
int zoom = CollectionUtil.getInt("zoom", map, 0);
int tilt = CollectionUtil.getInt("tilt", map, 0);
int bearing = CollectionUtil.getInt("bearing", map, 0);
int animationDuration = CollectionUtil.getInt("duration", map, 0);

CameraPosition cameraPosition =
new CameraPosition.Builder()
.target(
ObjectTranslationUtil.getLatLngFromMap(
(Map<String, Object>) map.get("target"))) // Set the target location
.zoom(zoom) // Set the desired zoom level
.tilt(tilt) // Set the desired tilt angle (0 for straight down, 90 for straight up)
.bearing(bearing) // Set the desired bearing (rotation angle in degrees)
.build();
if (mGoogleMap == null) {
return;
}

Object targetObj = map.get("target");
if (targetObj == null) {
return;
}

LatLng latLng = ObjectTranslationUtil.getLatLngFromMap((Map<String, Object>) targetObj);
if (latLng == null) {
return;
}

int zoom = CollectionUtil.getInt("zoom", map, 0);
int tilt = CollectionUtil.getInt("tilt", map, 0);
int bearing = CollectionUtil.getInt("bearing", map, 0);
int animationDuration = CollectionUtil.getInt("duration", map, 0);

CameraPosition cameraPosition =
new CameraPosition.Builder()
.target(latLng)
.zoom(zoom) // Set the desired zoom level
.tilt(tilt) // Set the desired tilt angle (0 for straight down, 90 for straight up)
.bearing(bearing) // Set the desired bearing (rotation angle in degrees)
.build();

if (animationDuration > 0) {
mGoogleMap.animateCamera(
CameraUpdateFactory.newCameraPosition(cameraPosition), animationDuration, null);
} else {
mGoogleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,25 @@ public void moveCamera(ReadableMap cameraPosition, final Promise promise) {
});
}

@Override
public void animateCamera(
ReadableMap cameraPosition, @Nullable Double duration, final Promise promise) {
UiThreadUtil.runOnUiThread(
() -> {
if (mMapViewController == null) {
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
return;
}

Map<String, Object> map = cameraPosition.toHashMap();
if (duration != null) {
map.put("duration", duration.doubleValue());
}
mMapViewController.animateCamera(map);
promise.resolve(null);
});
}

@Override
public void isAutoScreenAvailable(final Promise promise) {
promise.resolve(mMapViewController != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package com.google.android.react.navsdk;

import android.location.Location;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
Expand Down Expand Up @@ -261,6 +262,29 @@ public void moveCamera(String nativeID, ReadableMap cameraPosition, final Promis
});
}

@Override
public void animateCamera(
String nativeID,
ReadableMap cameraPosition,
@Nullable Double duration,
final Promise promise) {
UiThreadUtil.runOnUiThread(
() -> {
IMapViewFragment fragment = mNavViewManager.getFragmentByNativeId(nativeID);
if (fragment == null) {
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
return;
}

Map<String, Object> map = cameraPosition.toHashMap();
if (duration != null) {
map.put("duration", duration.doubleValue());
}
fragment.getMapController().animateCamera(map);
promise.resolve(null);
});
}

@Override
public void showRouteOverview(String nativeID, final Promise promise) {
UiThreadUtil.runOnUiThread(
Expand Down
23 changes: 15 additions & 8 deletions example/e2e/map.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,56 +41,63 @@ describe('Map view tests', () => {
await expectSuccess();
});

it('MT03 - initialize map and test camera tilt bearing zoom', async () => {
it('MT03 - initialize map and test animate camera', async () => {
await selectTestByName('testAnimateCamera');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT04 - initialize map and test camera tilt bearing zoom', async () => {
await selectTestByName('testTiltZoomBearingCamera');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT04 - test adding and removing markers', async () => {
it('MT05 - test adding and removing markers', async () => {
await selectTestByName('testMapMarkers');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT05 - test adding and removing circles', async () => {
it('MT06 - test adding and removing circles', async () => {
await selectTestByName('testMapCircles');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT06 - test adding and removing polylines', async () => {
it('MT07 - test adding and removing polylines', async () => {
await selectTestByName('testMapPolylines');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT07 - test adding and removing polygons', async () => {
it('MT08 - test adding and removing polygons', async () => {
await selectTestByName('testMapPolygons');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT08 - test adding and removing ground overlays', async () => {
it('MT09 - test adding and removing ground overlays', async () => {
await selectTestByName('testMapGroundOverlays');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT09 - test setting map style via JSON', async () => {
it('MT10 - test setting map style via JSON', async () => {
await selectTestByName('testMapStyle');
await waitForTestToFinish();
await expectNoErrors();
await expectSuccess();
});

it('MT10 - test min and max zoom level constraints', async () => {
it('MT11 - test min and max zoom level constraints', async () => {
await selectTestByName('testMinMaxZoomLevels');
await waitForTestToFinish();
await expectNoErrors();
Expand Down
10 changes: 10 additions & 0 deletions example/src/controls/mapsControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ const MapsControls: React.FC<MapControlsProps> = ({
});
};

const animateCamera = () => {
mapViewController.animateCamera({
target: { lat: Number(latitude), lng: Number(longitude) },
zoom: 1,
bearing: 60,
tilt: 60,
});
};

const setMapType = (newMapType: MapType) => {
onMapTypeChange?.(newMapType);
};
Expand Down Expand Up @@ -410,6 +419,7 @@ const MapsControls: React.FC<MapControlsProps> = ({
keyboardType="numeric"
/>
<ExampleAppButton title="Move camera" onPress={moveCamera} />
<ExampleAppButton title="Animate camera" onPress={animateCamera} />
<ExampleAppButton
title="Zoom in"
onPress={() => {
Expand Down
11 changes: 11 additions & 0 deletions example/src/screens/IntegrationTestsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
testRouteSegments,
testGetCurrentTimeAndDistance,
testMoveCamera,
testAnimateCamera,
testTiltZoomBearingCamera,
testMapMarkers,
testMapCircles,
Expand Down Expand Up @@ -279,6 +280,9 @@ const IntegrationTestsScreen = () => {
case 'testMoveCamera':
await testMoveCamera(getTestTools());
break;
case 'testAnimateCamera':
await testAnimateCamera(getTestTools());
break;
case 'testTiltZoomBearingCamera':
await testTiltZoomBearingCamera(getTestTools());
break;
Expand Down Expand Up @@ -451,6 +455,13 @@ const IntegrationTestsScreen = () => {
}}
testID="testMoveCamera"
/>
<ExampleAppButton
title="testAnimateCamera"
onPress={() => {
runTest('testAnimateCamera');
}}
testID="testAnimateCamera"
/>
<ExampleAppButton
title="testTiltZoomBearingCamera"
onPress={() => {
Expand Down
49 changes: 49 additions & 0 deletions example/src/screens/integration_tests/integration_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,55 @@ export const testMoveCamera = async (testTools: TestTools) => {
passTest();
};

export const testAnimateCamera = async (testTools: TestTools) => {
const { mapViewController, passTest, failTest, expectFalseError } = testTools;
if (!mapViewController) {
return failTest('mapViewController was expected to exist');
}

// Animate camera to Hong Kong
await mapViewController.animateCamera({
target: {
lat: 22.2987849,
lng: 114.1719271,
},
});

const hongKongPosition = await waitForCondition(
() => mapViewController.getCameraPosition(),
position =>
roundDown(position.target.lat) === 22 &&
roundDown(position.target.lng) === 114
);
if (!hongKongPosition) {
expectFalseError(
'roundDown(hongKongPosition.target.lat) !== 22 || roundDown(hongKongPosition.target.lng) !== 114'
);
}

// Animate camera to Tokyo
await mapViewController.animateCamera({
target: {
lat: 35.6805707,
lng: 139.7658596,
},
});

const tokyoPosition = await waitForCondition(
() => mapViewController.getCameraPosition(),
position =>
roundDown(position.target.lat) === 35 &&
roundDown(position.target.lng) === 139
);
if (!tokyoPosition) {
expectFalseError(
'roundDown(tokyoPosition.target.lat) !== 35 || roundDown(tokyoPosition.target.lng) !== 139'
);
}

passTest();
};

export const testTiltZoomBearingCamera = async (testTools: TestTools) => {
const { mapViewController, passTest, failTest, expectFalseError } = testTools;
if (!mapViewController) {
Expand Down
37 changes: 37 additions & 0 deletions ios/react-native-navigation-sdk/NavAutoModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,43 @@ - (void)moveCamera:(CameraPositionSpec &)cameraPosition
}
}

- (void)animateCamera:(CameraPositionSpec &)cameraPosition
duration:(double)duration
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
CameraPositionSpec positionCopy(cameraPosition);
if (_viewController) {
dispatch_async(dispatch_get_main_queue(), ^{
GMSMutableCameraPosition *position = [[GMSMutableCameraPosition alloc] init];

if (positionCopy.target().has_value()) {
auto target = positionCopy.target().value();
position.target = CLLocationCoordinate2DMake(target.lat(), target.lng());
}

if (positionCopy.zoom().has_value()) {
position.zoom = positionCopy.zoom().value();
}

if (positionCopy.bearing().has_value()) {
position.bearing = positionCopy.bearing().value();
}

if (positionCopy.tilt().has_value()) {
position.viewingAngle = positionCopy.tilt().value();
}

[self->_viewController animateCameraToPosition:position
duration:duration
result:^(BOOL success) {
resolve(@(success));
}];
});
} else {
reject(@"NO_VIEW_CONTROLLER", @"No view controller found", nil);
}
}

- (void)removeMarker:(NSString *)id
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
Expand Down
3 changes: 3 additions & 0 deletions ios/react-native-navigation-sdk/NavViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ typedef void (^OnArrayResult)(NSArray *_Nullable result);
- (void)setRecenterButtonEnabled:(BOOL)isEnabled;
- (void)resetMinMaxZoomLevel;
- (void)animateCamera:(GMSCameraUpdate *)update;
- (void)animateCameraToPosition:(GMSCameraPosition *)position
duration:(double)duration
result:(OnBooleanResult)completionBlock;
- (void)setMapStyle:(GMSMapStyle *)mapStyle;
- (void)setMapType:(GMSMapViewType)mapType;
- (void)clearMapView;
Expand Down
Loading