Skip to content

Commit

Permalink
Added animateToNavigation method to MapView (react-native-maps#2049)
Browse files Browse the repository at this point in the history
* added animateToNavigation method to MapView

* Added AnimatedNavigation example

* Updated Fork

* Deleted IDE files and Unnecessary .class files
  • Loading branch information
Guilherme Eduardo Gulinoski Teixeira authored and fatalsun committed Aug 29, 2018
1 parent e0c0c4d commit ae3e06e
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/mapview.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ To access event data, you will need to use `e.nativeEvent`. For example, `onPres

| Method Name | Arguments | Notes
|---|---|---|
| `animateToNavigation` | `location: LatLng`, `bearing: Number`, `angle: Number`, `duration: Number` |
| `animateToRegion` | `region: Region`, `duration: Number` |
| `animateToCoordinate` | `coordinate: LatLng`, `duration: Number` |
| `animateToBearing` | `bearing: Number`, `duration: Number` |
Expand Down
2 changes: 2 additions & 0 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import MapKml from './examples/MapKml';
import BugMarkerWontUpdate from './examples/BugMarkerWontUpdate';
import ImageOverlayWithAssets from './examples/ImageOverlayWithAssets';
import ImageOverlayWithURL from './examples/ImageOverlayWithURL';
import AnimatedNavigation from './examples/AnimatedNavigation';
import OnPoiClick from './examples/OnPoiClick';

const IOS = Platform.OS === 'ios';
Expand Down Expand Up @@ -159,6 +160,7 @@ class App extends React.Component {
[BugMarkerWontUpdate, 'BUG: Marker Won\'t Update (Android)', true],
[ImageOverlayWithAssets, 'Image Overlay Component with Assets', true],
[ImageOverlayWithURL, 'Image Overlay Component with URL', true],
[AnimatedNavigation, 'Animated Map Navigation', true],
[OnPoiClick, 'On Poi Click', true],
]
// Filter out examples that are not yet supported for Google Maps on iOS.
Expand Down
23 changes: 23 additions & 0 deletions example/android/app/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>example-android</name>
<comment>Project example-android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
Binary file not shown.
Binary file not shown.
137 changes: 137 additions & 0 deletions example/examples/AnimatedNavigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { Component } from 'react';

import {
View,
StyleSheet,
TouchableOpacity,
Text,
} from 'react-native';

import MapView from 'react-native-maps';
import carImage from './assets/car.png';

export default class NavigationMap extends Component {

constructor(props) {
super(props);
this.state = {
prevPos: null,
curPos: { latitude: 37.420814, longitude: -122.081949 },
curAng: 45,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
};
this.changePosition = this.changePosition.bind(this);
this.getRotation = this.getRotation.bind(this);
this.updateMap = this.updateMap.bind(this);
}

changePosition(latOffset, lonOffset) {
const latitude = this.state.curPos.latitude + latOffset;
const longitude = this.state.curPos.longitude + lonOffset;
this.setState({ prevPos: this.state.curPos, curPos: { latitude, longitude } });
this.updateMap();
}

getRotation(prevPos, curPos) {
if (!prevPos) return 0;
const xDiff = curPos.latitude - prevPos.latitude;
const yDiff = curPos.longitude - prevPos.longitude;
return (Math.atan2(yDiff, xDiff) * 180.0) / Math.PI;
}

updateMap() {
const { curPos, prevPos, curAng } = this.state;
const curRot = this.getRotation(prevPos, curPos);
this.map.animateToNavigation(curPos, curRot, curAng);
}

render() {
return (
<View style={styles.flex}>
<MapView
ref={(el) => (this.map = el)}
style={styles.flex}
minZoomLevel={15}
initialRegion={{
...this.state.curPos,
latitudeDelta: this.state.latitudeDelta,
longitudeDelta: this.state.longitudeDelta,
}}
>
<MapView.Marker
coordinate={this.state.curPos}
anchor={{ x: 0.5, y: 0.5 }}
image={carImage}
/>
</MapView>
<View style={styles.buttonContainerUpDown}>
<TouchableOpacity
style={[styles.button, styles.up]}
onPress={() => this.changePosition(0.0001, 0)}
>
<Text>+ Lat</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.down]}
onPress={() => this.changePosition(-0.0001, 0)}
>
<Text>- Lat</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainerLeftRight}>
<TouchableOpacity
style={[styles.button, styles.left]}
onPress={() => this.changePosition(0, -0.0001)}
>
<Text>- Lon</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.right]}
onPress={() => this.changePosition(0, 0.0001)}
>
<Text>+ Lon</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

const styles = StyleSheet.create({
flex: {
flex: 1,
width: '100%',
},
buttonContainerUpDown: {
...StyleSheet.absoluteFillObject,
flexDirection: 'row',
justifyContent: 'center',
},
buttonContainerLeftRight: {
...StyleSheet.absoluteFillObject,
flexDirection: 'column',
justifyContent: 'center',
},
button: {
backgroundColor: 'rgba(100,100,100,0.2)',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 20,
height: 50,
width: 50,
},
up: {
alignSelf: 'flex-start',
},
down: {
alignSelf: 'flex-end',
},
left: {
alignSelf: 'flex-start',
},
right: {
alignSelf: 'flex-end',
},
});
Binary file added example/examples/assets/car.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ declare module "react-native-maps" {
}

export default class MapView extends React.Component<MapViewProps, any> {
animateToNavigation(location: LatLng, bearing: number, angle: number, duration?: number): void;
animateToRegion(region: Region, duration?: number): void;
animateToCoordinate(latLng: LatLng, duration?: number): void;
animateToBearing(bearing: number, duration?: number): void;
Expand Down
23 changes: 23 additions & 0 deletions lib/android/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>react-native-maps-lib</name>
<comment>Project react-native-maps-lib created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.maps.android.data.kml.KmlLayer;

import java.util.Map;
import java.util.HashMap;

import javax.annotation.Nullable;

Expand All @@ -36,6 +37,7 @@ public class AirMapManager extends ViewGroupManager<AirMapView> {
private static final int FIT_TO_SUPPLIED_MARKERS = 6;
private static final int FIT_TO_COORDINATES = 7;
private static final int SET_MAP_BOUNDARIES = 8;
private static final int ANIMATE_TO_NAVIGATION = 9;


private final Map<String, Integer> MAP_TYPES = MapBuilder.of(
Expand Down Expand Up @@ -251,6 +253,17 @@ public void receiveCommand(AirMapView view, int commandId, @Nullable ReadableArr
ReadableMap region;

switch (commandId) {
case ANIMATE_TO_NAVIGATION:
region = args.getMap(0);
lng = region.getDouble("longitude");
lat = region.getDouble("latitude");
LatLng location = new LatLng(lat, lng);
bearing = (float)args.getDouble(1);
angle = (float)args.getDouble(2);
duration = args.getInt(3);
view.animateToNavigation(location, bearing, angle, duration);
break;

case ANIMATE_TO_REGION:
region = args.getMap(0);
duration = args.getInt(1);
Expand Down Expand Up @@ -332,14 +345,15 @@ public Map getExportedCustomDirectEventTypeConstants() {
@Nullable
@Override
public Map<String, Integer> getCommandsMap() {
Map<String, Integer> map = MapBuilder.of(
Map<String, Integer> map = this.CreateMap(
"animateToRegion", ANIMATE_TO_REGION,
"animateToCoordinate", ANIMATE_TO_COORDINATE,
"animateToViewingAngle", ANIMATE_TO_VIEWING_ANGLE,
"animateToBearing", ANIMATE_TO_BEARING,
"fitToElements", FIT_TO_ELEMENTS,
"fitToSuppliedMarkers", FIT_TO_SUPPLIED_MARKERS,
"fitToCoordinates", FIT_TO_COORDINATES
"fitToCoordinates", FIT_TO_COORDINATES,
"animateToNavigation", ANIMATE_TO_NAVIGATION
);

map.putAll(MapBuilder.of(
Expand All @@ -349,6 +363,20 @@ public Map<String, Integer> getCommandsMap() {
return map;
}

public static <K, V> Map<K, V> CreateMap(
K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) {
Map map = new HashMap<K, V>();
map.put(k1, v1);
map.put(k2, v2);
map.put(k3, v3);
map.put(k4, v4);
map.put(k5, v5);
map.put(k6, v6);
map.put(k7, v7);
map.put(k8, v8);
return map;
}

@Override
public LayoutShadowNode createShadowNodeInstance() {
// A custom shadow node is needed in order to pass back the width/height of the map to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,16 @@ public void updateExtraData(Object extraData) {
}
}

public void animateToNavigation(LatLng location, float bearing, float angle, int duration) {
if (map == null) return;
CameraPosition cameraPosition = new CameraPosition.Builder(map.getCameraPosition())
.bearing(bearing)
.tilt(angle)
.target(location)
.build();
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), duration, null);
}

public void animateToRegion(LatLngBounds bounds, int duration) {
if (map == null) return;
map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null);
Expand Down
4 changes: 4 additions & 0 deletions lib/components/MapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,10 @@ class MapView extends React.Component {
}
}

animateToNavigation(location, bearing, angle, duration) {
this._runCommand('animateToNavigation', [location, bearing, angle, duration || 500]);
}

animateToRegion(region, duration) {
this._runCommand('animateToRegion', [region, duration || 500]);
}
Expand Down
23 changes: 23 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,29 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(maxZoomLevel, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(kmlSrc, NSString)

RCT_EXPORT_METHOD(animateToNavigation:(nonnull NSNumber *)reactTag
withRegion:(MKCoordinateRegion)region
withBearing:(CGFloat)bearing
withAngle:(double)angle
withDuration:(CGFloat)duration)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[AIRGoogleMap class]]) {
RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMap, got: %@", view);
} else {
[CATransaction begin];
[CATransaction setAnimationDuration:duration/1000];
AIRGoogleMap *mapView = (AIRGoogleMap *)view;
GMSCameraPosition *camera = [AIRGoogleMap makeGMSCameraPositionFromMap:mapView andMKCoordinateRegion:region];
[mapView animateToCameraPosition:camera];
[mapView animateToViewingAngle:angle];
[mapView animateToBearing:bearing];
[CATransaction commit];
}
}];
}

RCT_EXPORT_METHOD(animateToRegion:(nonnull NSNumber *)reactTag
withRegion:(MKCoordinateRegion)region
withDuration:(CGFloat)duration)
Expand Down
24 changes: 24 additions & 0 deletions lib/ios/AirMaps/AIRMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,30 @@ - (UIView *)view

#pragma mark exported MapView methods

RCT_EXPORT_METHOD(animateToNavigation:(nonnull NSNumber *)reactTag
withRegion:(MKCoordinateRegion)region
withBearing:(CGFloat)bearing
withAngle:(double)angle
withDuration:(CGFloat)duration)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[AIRMap class]]) {
RCTLogError(@"Invalid view returned from registry, expecting AIRMap, got: %@", view);
} else {
AIRMap *mapView = (AIRMap *)view;
MKMapCamera *mapCamera = [[mapView camera] copy];
[mapCamera setPitch:angle];
[mapCamera setHeading:bearing];

[AIRMap animateWithDuration:duration/1000 animations:^{
[(AIRMap *)view setRegion:region animated:YES];
[mapView setCamera:mapCamera animated:YES];
}];
}
}];
}

RCT_EXPORT_METHOD(animateToRegion:(nonnull NSNumber *)reactTag
withRegion:(MKCoordinateRegion)region
withDuration:(CGFloat)duration)
Expand Down

0 comments on commit ae3e06e

Please sign in to comment.