Skip to content

Commit ea55831

Browse files
committed
feat: add more MapView events and controls
1 parent 2349ab9 commit ea55831

32 files changed

+786
-229
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ This project follows
4242
follow [GitHub's directions](https://help.github.com/articles/generating-ssh-keys/)
4343
to generate an SSH key.
4444
- `git clone [email protected]:<your_name_here>/googlemaps/react-native-navigation-sdk.git`
45-
- `git remote add upstream [email protected]:googlemaps/react-native-sdk.git` (So that you
45+
- `git remote add upstream [email protected]:googlemaps/react-native-navigation-sdk.git` (So that you
4646
fetch from the master repository, not your clone, when running `git fetch`
4747
et al.)
4848

4949
#### Create branch
5050

5151
1. `git fetch upstream`
52-
2. `git checkout upstream/master -b <name_of_your_branch>`
52+
2. `git checkout upstream/main -b <name_of_your_branch>`
5353
3. Start coding!
5454

5555
#### Commit changes

android/src/main/java/com/google/android/react/navsdk/Constants.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,15 @@
1616
public class Constants {
1717
public static final String LAT_FIELD_KEY = "lat";
1818
public static final String LNG_FIELD_KEY = "lng";
19+
20+
public static final String URI_KEY = "uri";
21+
22+
public static final String X_KEY = "x";
23+
public static final String Y_KEY = "y";
24+
25+
public static final String CAMERA_POSITION_KEY = "cameraPosition";
26+
public static final String TARGET_KEY = "target";
27+
public static final String BEARING_KEY = "bearing";
28+
public static final String TILT_KEY = "tilt";
29+
public static final String ZOOM_KEY = "zoom";
1930
}

android/src/main/java/com/google/android/react/navsdk/INavigationViewCallback.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
package com.google.android.react.navsdk;
1515

16+
import com.google.android.gms.maps.model.CameraPosition;
1617
import com.google.android.gms.maps.model.Circle;
1718
import com.google.android.gms.maps.model.GroundOverlay;
1819
import com.google.android.gms.maps.model.LatLng;
@@ -36,4 +37,8 @@ public interface INavigationViewCallback {
3637
void onMarkerInfoWindowTapped(Marker marker);
3738

3839
void onMapClick(LatLng latLng);
40+
41+
void onMapDrag(CameraPosition cameraPosition);
42+
43+
void onMapDragEnd(CameraPosition cameraPosition);
3944
}

android/src/main/java/com/google/android/react/navsdk/MapViewController.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ public void setupMapListeners(INavigationViewCallback navigationViewCallback) {
8282
mGoogleMap.setOnInfoWindowClickListener(
8383
marker -> mNavigationViewCallback.onMarkerInfoWindowTapped(marker));
8484
mGoogleMap.setOnMapClickListener(latLng -> mNavigationViewCallback.onMapClick(latLng));
85+
mGoogleMap.setOnCameraMoveListener(
86+
() -> mNavigationViewCallback.onMapDrag(mGoogleMap.getCameraPosition()));
87+
mGoogleMap.setOnCameraIdleListener(
88+
() -> mNavigationViewCallback.onMapDragEnd(mGoogleMap.getCameraPosition()));
8589
}
8690

8791
public GoogleMap getGoogleMap() {

android/src/main/java/com/google/android/react/navsdk/MapViewFragment.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.android.gms.maps.GoogleMap;
2828
import com.google.android.gms.maps.OnMapReadyCallback;
2929
import com.google.android.gms.maps.SupportMapFragment;
30+
import com.google.android.gms.maps.model.CameraPosition;
3031
import com.google.android.gms.maps.model.Circle;
3132
import com.google.android.gms.maps.model.GroundOverlay;
3233
import com.google.android.gms.maps.model.LatLng;
@@ -126,6 +127,24 @@ public void onMapClick(LatLng latLng) {
126127
emitEvent("onMapClick", ObjectTranslationUtil.getMapFromLatLng(latLng));
127128
}
128129

130+
@Override
131+
public void onMapDrag(CameraPosition cameraPosition) {
132+
WritableMap map = Arguments.createMap();
133+
map.putMap(
134+
Constants.CAMERA_POSITION_KEY,
135+
ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
136+
emitEvent("onMapDrag", map);
137+
}
138+
139+
@Override
140+
public void onMapDragEnd(CameraPosition cameraPosition) {
141+
WritableMap map = Arguments.createMap();
142+
map.putMap(
143+
Constants.CAMERA_POSITION_KEY,
144+
ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
145+
emitEvent("onMapDragEnd", map);
146+
}
147+
129148
public MapViewController getMapController() {
130149
return mMapViewController;
131150
}

android/src/main/java/com/google/android/react/navsdk/NavViewFragment.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.facebook.react.uimanager.events.EventDispatcher;
2727
import com.google.android.gms.maps.GoogleMap;
2828
import com.google.android.gms.maps.OnMapReadyCallback;
29+
import com.google.android.gms.maps.model.CameraPosition;
2930
import com.google.android.gms.maps.model.Circle;
3031
import com.google.android.gms.maps.model.GroundOverlay;
3132
import com.google.android.gms.maps.model.LatLng;
@@ -166,6 +167,24 @@ public void onMapClick(LatLng latLng) {
166167
emitEvent("onMapClick", ObjectTranslationUtil.getMapFromLatLng(latLng));
167168
}
168169

170+
@Override
171+
public void onMapDrag(CameraPosition cameraPosition) {
172+
WritableMap map = Arguments.createMap();
173+
map.putMap(
174+
Constants.CAMERA_POSITION_KEY,
175+
ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
176+
emitEvent("onMapDrag", ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
177+
}
178+
179+
@Override
180+
public void onMapDragEnd(CameraPosition cameraPosition) {
181+
WritableMap map = Arguments.createMap();
182+
map.putMap(
183+
Constants.CAMERA_POSITION_KEY,
184+
ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
185+
emitEvent("onMapDragEnd", ObjectTranslationUtil.getMapFromCameraPosition(cameraPosition));
186+
}
187+
169188
@Override
170189
public void onDestroy() {
171190
super.onDestroy();

android/src/main/java/com/google/android/react/navsdk/NavViewManager.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
import com.facebook.react.bridge.ReadableArray;
2727
import com.facebook.react.bridge.ReadableMap;
2828
import com.facebook.react.common.MapBuilder;
29-
import com.facebook.react.uimanager.SimpleViewManager;
3029
import com.facebook.react.uimanager.ThemedReactContext;
30+
import com.facebook.react.uimanager.ViewGroupManager;
3131
import com.facebook.react.uimanager.annotations.ReactProp;
3232
import com.google.android.gms.maps.GoogleMap;
3333
import java.lang.ref.WeakReference;
@@ -38,7 +38,7 @@
3838
// NavViewManager is responsible for managing both the regular map fragment as well as the
3939
// navigation map view fragment.
4040
//
41-
public class NavViewManager extends SimpleViewManager<NavViewLayout> {
41+
public class NavViewManager extends ViewGroupManager<NavViewLayout> {
4242

4343
public static final String REACT_CLASS = "NavViewManager";
4444

@@ -528,6 +528,8 @@ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
528528
MapBuilder.of("registrationName", "onPromptVisibilityChanged"))
529529
.put("onMapReady", MapBuilder.of("registrationName", "onMapReady"))
530530
.put("onMapClick", MapBuilder.of("registrationName", "onMapClick"))
531+
.put("onMapDrag", MapBuilder.of("registrationName", "onMapDrag"))
532+
.put("onMapDragEnd", MapBuilder.of("registrationName", "onMapDragEnd"))
531533
.put("onMarkerClick", MapBuilder.of("registrationName", "onMarkerClick"))
532534
.put("onPolylineClick", MapBuilder.of("registrationName", "onPolylineClick"))
533535
.put("onPolygonClick", MapBuilder.of("registrationName", "onPolygonClick"))

android/src/main/java/com/google/android/react/navsdk/NavViewModule.java

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
package com.google.android.react.navsdk;
1515

16+
import android.graphics.Point;
1617
import android.location.Location;
1718
import com.facebook.react.bridge.Arguments;
1819
import com.facebook.react.bridge.Promise;
@@ -22,14 +23,18 @@
2223
import com.facebook.react.bridge.ReadableMap;
2324
import com.facebook.react.bridge.UiThreadUtil;
2425
import com.facebook.react.bridge.WritableMap;
26+
import com.google.android.gms.maps.CameraUpdate;
27+
import com.google.android.gms.maps.CameraUpdateFactory;
2528
import com.google.android.gms.maps.UiSettings;
2629
import com.google.android.gms.maps.model.CameraPosition;
2730
import com.google.android.gms.maps.model.Circle;
2831
import com.google.android.gms.maps.model.GroundOverlay;
2932
import com.google.android.gms.maps.model.LatLng;
33+
import com.google.android.gms.maps.model.LatLngBounds;
3034
import com.google.android.gms.maps.model.Marker;
3135
import com.google.android.gms.maps.model.Polygon;
3236
import com.google.android.gms.maps.model.Polyline;
37+
import com.google.android.gms.maps.model.VisibleRegion;
3338
import java.util.HashMap;
3439
import java.util.Map;
3540

@@ -75,12 +80,7 @@ public void getCameraPosition(Integer viewId, final Promise promise) {
7580
return;
7681
}
7782

78-
LatLng target = cp.target;
79-
WritableMap map = Arguments.createMap();
80-
map.putDouble("bearing", cp.bearing);
81-
map.putDouble("tilt", cp.tilt);
82-
map.putDouble("zoom", cp.zoom);
83-
map.putMap("target", ObjectTranslationUtil.getMapFromLatLng(target));
83+
WritableMap map = ObjectTranslationUtil.getMapFromCameraPosition(cp);
8484

8585
promise.resolve(map);
8686
});
@@ -110,6 +110,116 @@ public void getMyLocation(Integer viewId, final Promise promise) {
110110
});
111111
}
112112

113+
@ReactMethod
114+
public void coordinateForPoint(Integer viewId, ReadableMap pointMap, final Promise promise) {
115+
UiThreadUtil.runOnUiThread(
116+
() -> {
117+
if (mNavViewManager.getGoogleMap(viewId) == null) {
118+
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
119+
return;
120+
}
121+
122+
try {
123+
float density = getReactApplicationContext().getResources().getDisplayMetrics().density;
124+
int x = (int) density * CollectionUtil.getInt("x", pointMap.toHashMap(), 0);
125+
int y = (int) density * CollectionUtil.getInt("y", pointMap.toHashMap(), 0);
126+
Point point = new Point(x, y);
127+
LatLng latLng =
128+
mNavViewManager.getGoogleMap(viewId).getProjection().fromScreenLocation(point);
129+
130+
promise.resolve(ObjectTranslationUtil.getMapFromLatLng(latLng));
131+
} catch (Exception e) {
132+
promise.resolve(null);
133+
return;
134+
}
135+
});
136+
}
137+
138+
@ReactMethod
139+
public void pointForCoordinate(Integer viewId, ReadableMap latLngMap, final Promise promise) {
140+
UiThreadUtil.runOnUiThread(
141+
() -> {
142+
if (mNavViewManager.getGoogleMap(viewId) == null) {
143+
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
144+
return;
145+
}
146+
147+
LatLng latLng = ObjectTranslationUtil.getLatLngFromMap(latLngMap.toHashMap());
148+
Point point =
149+
mNavViewManager.getGoogleMap(viewId).getProjection().toScreenLocation(latLng);
150+
float density = getReactApplicationContext().getResources().getDisplayMetrics().density;
151+
point.x = (int) (point.x / density);
152+
point.y = (int) (point.y / density);
153+
154+
promise.resolve(ObjectTranslationUtil.getMapFromPoint(point));
155+
});
156+
}
157+
158+
@ReactMethod
159+
public void fitBounds(Integer viewId, ReadableMap boundsOptions, final Promise promise) {
160+
UiThreadUtil.runOnUiThread(
161+
() -> {
162+
if (mNavViewManager.getGoogleMap(viewId) == null) {
163+
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
164+
return;
165+
}
166+
167+
LatLng northEast =
168+
ObjectTranslationUtil.getLatLngFromMap(
169+
boundsOptions.getMap("bounds").getMap("northEast").toHashMap());
170+
LatLng southWest =
171+
ObjectTranslationUtil.getLatLngFromMap(
172+
boundsOptions.getMap("bounds").getMap("southWest").toHashMap());
173+
174+
if (northEast == null || southWest == null) {
175+
promise.resolve(null);
176+
return;
177+
}
178+
179+
ReadableMap paddingMap = boundsOptions.getMap("padding");
180+
if (paddingMap != null) {
181+
double density =
182+
getReactApplicationContext().getResources().getDisplayMetrics().density;
183+
int left = (int) (paddingMap.getInt("left") * density);
184+
int top = (int) (paddingMap.getInt("top") * density);
185+
int right = (int) (paddingMap.getInt("right") * density);
186+
int bottom = (int) (paddingMap.getInt("bottom") * density);
187+
mNavViewManager.getGoogleMap(viewId).setPadding(left, top, right, bottom);
188+
}
189+
190+
CameraUpdate cameraUpdate =
191+
CameraUpdateFactory.newLatLngBounds(new LatLngBounds(southWest, northEast), 0);
192+
mNavViewManager.getGoogleMap(viewId).animateCamera(cameraUpdate);
193+
194+
promise.resolve(null);
195+
});
196+
}
197+
198+
@ReactMethod
199+
public void getBounds(Integer viewId, final Promise promise) {
200+
UiThreadUtil.runOnUiThread(
201+
() -> {
202+
if (mNavViewManager.getGoogleMap(viewId) == null) {
203+
promise.reject(JsErrors.NO_MAP_ERROR_CODE, JsErrors.NO_MAP_ERROR_MESSAGE);
204+
return;
205+
}
206+
207+
VisibleRegion visibleRegion =
208+
mNavViewManager.getGoogleMap(viewId).getProjection().getVisibleRegion();
209+
LatLng northEast = visibleRegion.farRight;
210+
LatLng southWest = visibleRegion.nearLeft;
211+
212+
WritableMap northEastMap = ObjectTranslationUtil.getMapFromLatLng(northEast);
213+
WritableMap southWestMap = ObjectTranslationUtil.getMapFromLatLng(southWest);
214+
215+
WritableMap map = Arguments.createMap();
216+
map.putMap("northEast", northEastMap);
217+
map.putMap("southWest", southWestMap);
218+
219+
promise.resolve(map);
220+
});
221+
}
222+
113223
@ReactMethod
114224
public void getUiSettings(Integer viewId, final Promise promise) {
115225
UiThreadUtil.runOnUiThread(

android/src/main/java/com/google/android/react/navsdk/ObjectTranslationUtil.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
*/
1414
package com.google.android.react.navsdk;
1515

16+
import android.graphics.Point;
1617
import android.location.Location;
1718
import android.os.Build;
1819
import com.facebook.react.bridge.Arguments;
1920
import com.facebook.react.bridge.WritableArray;
2021
import com.facebook.react.bridge.WritableMap;
22+
import com.google.android.gms.maps.model.CameraPosition;
2123
import com.google.android.gms.maps.model.Circle;
2224
import com.google.android.gms.maps.model.GroundOverlay;
2325
import com.google.android.gms.maps.model.LatLng;
@@ -79,6 +81,23 @@ public static WritableMap getMapFromLatLng(LatLng latLng) {
7981
return map;
8082
}
8183

84+
public static WritableMap getMapFromPoint(Point point) {
85+
WritableMap map = Arguments.createMap();
86+
map.putDouble(Constants.X_KEY, point.x);
87+
map.putDouble(Constants.Y_KEY, point.y);
88+
return map;
89+
}
90+
91+
public static WritableMap getMapFromCameraPosition(CameraPosition cameraPosition) {
92+
WritableMap map = Arguments.createMap();
93+
map.putMap(Constants.TARGET_KEY, getMapFromLatLng(cameraPosition.target));
94+
map.putDouble(Constants.BEARING_KEY, cameraPosition.bearing);
95+
map.putDouble(Constants.TILT_KEY, cameraPosition.tilt);
96+
map.putDouble(Constants.ZOOM_KEY, cameraPosition.zoom);
97+
98+
return map;
99+
}
100+
82101
public static WritableMap getMapFromWaypoint(Waypoint waypoint) {
83102
WritableMap map = Arguments.createMap();
84103

example/src/controls/mapsControls.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,36 @@ const MapsControls: React.FC<MapControlsProps> = ({ mapViewController }) => {
212212
setCustomPaddingEnabled(!customPaddingEnabled);
213213
};
214214

215+
const coordinateForPoint = async () => {
216+
const cameraPosition = await mapViewController.getCameraPosition();
217+
const point = await mapViewController.pointForCoordinate(
218+
cameraPosition.target
219+
);
220+
const coordinate = await mapViewController.coordinateForPoint(point);
221+
console.log({ point, coordinate });
222+
};
223+
224+
const pointForCoordinate = async () => {
225+
const { target: coordinate } = await mapViewController.getCameraPosition();
226+
const point = await mapViewController.pointForCoordinate(coordinate);
227+
console.log({ point, coordinate });
228+
};
229+
230+
const fitBounds = async () => {
231+
const bounds = await mapViewController.getBounds();
232+
bounds.northEast.lat -= 1;
233+
bounds.northEast.lng -= 1;
234+
bounds.southWest.lat += 1;
235+
bounds.southWest.lng += 1;
236+
237+
await mapViewController.fitBounds({ bounds });
238+
};
239+
240+
const getBounds = async () => {
241+
const bounds = await mapViewController.getBounds();
242+
console.log(bounds);
243+
};
244+
215245
return (
216246
<View>
217247
<TextInput
@@ -256,6 +286,10 @@ const MapsControls: React.FC<MapControlsProps> = ({ mapViewController }) => {
256286
onPress={getIsMyLocationEnabled}
257287
/>
258288
<Button title="Get camera position" onPress={getCameraPositionClicked} />
289+
<Button title="Coordinate for point" onPress={coordinateForPoint} />
290+
<Button title="Point for coordinate" onPress={pointForCoordinate} />
291+
<Button title="Get bounds" onPress={getBounds} />
292+
<Button title="Fit bounds (Shrink edges by 1°)" onPress={fitBounds} />
259293
<View style={styles.rowContainer}>
260294
<Text>Location marker</Text>
261295
<Button

0 commit comments

Comments
 (0)