Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fit to coordinates #545

Merged
Merged
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,12 @@ Pass an array of marker identifiers to have the map re-focus.

![](http://i.giphy.com/3o7qEbOQnO0yoXqKJ2.gif) ![](http://i.giphy.com/l41YdrQZ7m6Dz4h0c.gif)

### Zoom to Specified Coordinates

Pass an array of coordinates to focus a map region on said coordinates.

![](https://cloud.githubusercontent.com/assets/1627824/18609960/da5d9e06-7cdc-11e6-811e-34e255093df9.gif)

### Troubleshooting

#### My map is blank
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class AirMapManager extends ViewGroupManager<AirMapView> {
private static final int ANIMATE_TO_COORDINATE = 2;
private static final int FIT_TO_ELEMENTS = 3;
private static final int FIT_TO_SUPPLIED_MARKERS = 4;
private static final int FIT_TO_COORDINATES = 5;

private final Map<String, Integer> MAP_TYPES = MapBuilder.of(
"standard", GoogleMap.MAP_TYPE_NORMAL,
Expand Down Expand Up @@ -218,6 +219,9 @@ public void receiveCommand(AirMapView view, int commandId, @Nullable ReadableArr
case FIT_TO_SUPPLIED_MARKERS:
view.fitToSuppliedMarkers(args.getArray(0), args.getBoolean(1));
break;
case FIT_TO_COORDINATES:
view.fitToCoordinates(args.getArray(0), args.getMap(1), args.getBoolean(2));
break;
}
}

Expand Down Expand Up @@ -251,7 +255,8 @@ public Map<String, Integer> getCommandsMap() {
"animateToRegion", ANIMATE_TO_REGION,
"animateToCoordinate", ANIMATE_TO_COORDINATE,
"fitToElements", FIT_TO_ELEMENTS,
"fitToSuppliedMarkers", FIT_TO_SUPPLIED_MARKERS
"fitToSuppliedMarkers", FIT_TO_SUPPLIED_MARKERS,
"fitToCoordinates", FIT_TO_COORDINATES
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public class AirMapView extends MapView implements GoogleMap.InfoWindowAdapter,
private Boolean isMapLoaded = false;
private Integer loadingBackgroundColor = null;
private Integer loadingIndicatorColor = null;
private final int baseMapPadding = 50;

private LatLngBounds boundsToMove;
private boolean showUserLocation = false;
private boolean isMonitoringRegion = false;
Expand Down Expand Up @@ -488,10 +490,9 @@ public void fitToElements(boolean animated) {
}
// TODO(lmr): may want to include shapes / etc.
}

if (addedPosition) {
LatLngBounds bounds = builder.build();
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 50);
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
if (animated) {
startMonitoringRegion();
map.animateCamera(cu);
Expand Down Expand Up @@ -526,7 +527,7 @@ public void fitToSuppliedMarkers(ReadableArray markerIDsArray, boolean animated)

if (addedPosition) {
LatLngBounds bounds = builder.build();
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 50);
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);
if (animated) {
startMonitoringRegion();
map.animateCamera(cu);
Expand All @@ -536,6 +537,32 @@ public void fitToSuppliedMarkers(ReadableArray markerIDsArray, boolean animated)
}
}

public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding, boolean animated) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();

for (int i = 0; i < coordinatesArray.size(); i++) {
ReadableMap latLng = coordinatesArray.getMap(i);
Double lat = latLng.getDouble("latitude");
Double lng = latLng.getDouble("longitude");
builder.include(new LatLng(lat, lng));
}

LatLngBounds bounds = builder.build();
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, baseMapPadding);

if (edgePadding != null) {
map.setPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), edgePadding.getInt("right"), edgePadding.getInt("bottom"));
}

if (animated) {
startMonitoringRegion();
map.animateCamera(cu);
} else {
map.moveCamera(cu);
}
map.setPadding(0, 0, 0, 0); // Without this, the Google logo is moved up by the value of edgePadding.bottom
}

// InfoWindowAdapter interface

@Override
Expand Down
9 changes: 9 additions & 0 deletions components/MapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,15 @@ class MapView extends React.Component {
this._runCommand('fitToSuppliedMarkers', [markers, animated]);
}

fitToCoordinates(coordinates = [], options) {
const {
edgePadding = { top: 0, right: 0, bottom: 0, left: 0 },
animated = true,
} = options;

this._runCommand('fitToCoordinates', [coordinates, edgePadding, animated]);
}

takeSnapshot(width, height, region, callback) {
const finalRegion = region || this.props.region || this.props.initialRegion;
this._runCommand('takeSnapshot', [width, height, finalRegion, callback]);
Expand Down
10 changes: 10 additions & 0 deletions docs/mapview.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
| `animateToCoordinate` | `region: Coordinate`, `duration: Number` |
| `fitToElements` | `animated: Boolean` |
| `fitToSuppliedMarkers` | `markerIDs: String[]` |
| `fitToCoordinates` | `coordinates: Array<LatLng>, edgePadding: EdgePadding, animated: Boolean` |



Expand Down Expand Up @@ -93,3 +94,12 @@ enum MapType : String {
"terrain" //Android only
}
```

```
type EdgePadding {
top: Number,
right: Number,
bottom: Number,
left: Number
}
```
2 changes: 2 additions & 0 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import CachedMap from './examples/CachedMap';
import LoadingMap from './examples/LoadingMap';
import TakeSnapshot from './examples/TakeSnapshot';
import FitToSuppliedMarkers from './examples/FitToSuppliedMarkers';
import FitToCoordinates from './examples/FitToCoordinates';
import LiteMapView from './examples/LiteMapView';
import CustomTiles from './examples/CustomTiles';
import StaticMap from './examples/StaticMap';
Expand Down Expand Up @@ -132,6 +133,7 @@ class App extends React.Component {
[CachedMap, 'Cached Map'],
[LoadingMap, 'Map with loading'],
[FitToSuppliedMarkers, 'Focus Map On Markers'],
[FitToCoordinates, 'Fit Map To Coordinates'],
[LiteMapView, 'Android Lite MapView'],
[CustomTiles, 'Custom Tiles'],
]
Expand Down
132 changes: 132 additions & 0 deletions example/examples/FitToCoordinates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import React from 'react';
import {
StyleSheet,
View,
Dimensions,
TouchableOpacity,
Text,
} from 'react-native';

import MapView from 'react-native-maps';

const { width, height } = Dimensions.get('window');

const ASPECT_RATIO = width / height;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const SPACE = 0.01;

function createMarker(modifier = 1) {
return {
latitude: LATITUDE - (SPACE * modifier),
longitude: LONGITUDE - (SPACE * modifier),
};
}

const MARKERS = [
createMarker(),
createMarker(2),
createMarker(3),
createMarker(4),
];

const DEFAULT_PADDING = { top: 40, right: 40, bottom: 40, left: 40 };

class FitToCoordinates extends React.Component {
fitPadding() {
this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], {
edgePadding: { top: 100, right: 100, bottom: 100, left: 100 },
animated: true,
});
}

fitBottomTwoMarkers() {
this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}

fitAllMarkers() {
this.map.fitToCoordinates(MARKERS, {
edgePadding: DEFAULT_PADDING,
animated: true,
});
}

render() {
return (
<View style={styles.container}>
<MapView
ref={ref => { this.map = ref; }}
style={styles.map}
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
>
{MARKERS.map((marker, i) => (
<MapView.Marker
key={i}
coordinate={marker}
/>
))}
</MapView>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={() => this.fitPadding()}
style={[styles.bubble, styles.button]}
>
<Text>Fit Bottom Two Markers with Padding</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.fitBottomTwoMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit Bottom Two Markers</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.fitAllMarkers()}
style={[styles.bubble, styles.button]}
>
<Text>Fit All Markers</Text>
</TouchableOpacity>
</View>
</View>
);
}
}

const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
justifyContent: 'flex-end',
alignItems: 'center',
},
map: {
...StyleSheet.absoluteFillObject,
},
bubble: {
backgroundColor: 'rgba(255,255,255,0.7)',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 20,
},
button: {
marginTop: 12,
paddingHorizontal: 12,
alignItems: 'center',
marginHorizontal: 10,
},
buttonContainer: {
flexDirection: 'column',
marginVertical: 20,
backgroundColor: 'transparent',
},
});

module.exports = FitToCoordinates;
32 changes: 32 additions & 0 deletions ios/AirMaps/AIRMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,38 @@ - (UIView *)view
}];
}

RCT_EXPORT_METHOD(fitToCoordinates:(nonnull NSNumber *)reactTag
coordinates:(nonnull NSArray<AIRMapCoordinate *> *)coordinates
edgePadding:(nonnull NSDictionary *)edgePadding
animated:(nonnull BOOL)animated)
{
[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;

// Create Polyline with coordinates
CLLocationCoordinate2D coords[coordinates.count];
for(int i = 0; i < coordinates.count; i++)
{
coords[i] = coordinates[i].coordinate;
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordinates.count];

// Set Map viewport
CGFloat top = [RCTConvert CGFloat:edgePadding[@"top"]];
CGFloat right = [RCTConvert CGFloat:edgePadding[@"right"]];
CGFloat bottom = [RCTConvert CGFloat:edgePadding[@"bottom"]];
CGFloat left = [RCTConvert CGFloat:edgePadding[@"left"]];

[mapView setVisibleMapRect:[polyline boundingMapRect] edgePadding:UIEdgeInsetsMake(top, left, bottom, right) animated:animated];

}
}];
}

RCT_EXPORT_METHOD(takeSnapshot:(nonnull NSNumber *)reactTag
withWidth:(nonnull NSNumber *)width
withHeight:(nonnull NSNumber *)height
Expand Down