Skip to content

Commit

Permalink
feat(ios): support edgePadding for fitToElements & fitToSuppliedMarkers
Browse files Browse the repository at this point in the history
BREAKING CHANGE: fitTo{Elements,SuppliedMarkers} no longer auto apply padding. Use edgePadding.
  • Loading branch information
monholm committed May 10, 2023
1 parent db5aadc commit cf58b84
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 26 deletions.
26 changes: 13 additions & 13 deletions docs/mapview.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,19 @@ To access event data, you will need to use `e.nativeEvent`. For example, `onPres

## Methods

| Method Name | Arguments | Notes |
| ---------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addressForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a address (`Address`). Returns a `Promise<Address>` **Note** Not supported on Google Maps for iOS. |
| `animateCamera` | `camera: Camera`, `duration?: number` | Animate the camera to a new view. You can pass a partial camera object here; any property not given will remain unmodified. Returns `Promise<void>` when the animation finishes. |
| `animateToRegion` | `region: Region`, `duration?: number` | Animates to region. Returns `Promise<void>` when the animation finishes. |
| `coordinateForPoint` | `point: Point` | Converts a view coordinate (`Point`) to a map coordinate. Returns a `Promise<Coordinate>`. |
| `fitToCoordinates` | `coordinates: Array<LatLng>, options?: { edgePadding?: EdgePadding, duration?: number }` | Returns `Promise<void>` when the animation finishes. |
| `fitToElements` | `options?: { edgePadding?: EdgePadding, duration?: number }` | **Note** edgePadding is Google Maps only. Returns `Promise<void>` when the animation finishes. |
| `fitToSuppliedMarkers` | `markerIDs: String[], options?: { edgePadding?: EdgePadding, duration?: number }` | If you need to use this in `ComponentDidMount`, make sure you put it in a timeout or it will cause performance problems. Returns `Promise<void>` when the animation finishes. **Note** edgePadding is Google Maps only |
| `getCamera` | | Returns a `Promise<Camera>` structure indicating the current camera configuration. |
| `getMapBoundaries` | | `Promise<{northEast: LatLng, southWest: LatLng}>` |
| `getMarkersFrames` | `onlyVisible: Boolean` | Get markers' centers and frames in view coordinates. Returns a `Promise<{ "markerID" : { point: Point, frame: Frame } }>`. **Note**: iOS only. |
| `pointForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a view coordinate (`Point`). Returns a `Promise<Point>`. |
| Method Name | Arguments | Notes |
| ---------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `addressForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a address (`Address`). Returns a `Promise<Address>` **Note** Not supported on Google Maps for iOS. |
| `animateCamera` | `camera: Camera`, `duration?: number` | Animate the camera to a new view. You can pass a partial camera object here; any property not given will remain unmodified. Returns `Promise<void>` when the animation finishes. |
| `animateToRegion` | `region: Region`, `duration?: number` | Animates to region. Returns `Promise<void>` when the animation finishes. |
| `coordinateForPoint` | `point: Point` | Converts a view coordinate (`Point`) to a map coordinate. Returns a `Promise<Coordinate>`. |
| `fitToCoordinates` | `coordinates: Array<LatLng>, options?: { edgePadding?: EdgePadding, duration?: number }` | Returns `Promise<void>` when the animation finishes. |
| `fitToElements` | `options?: { edgePadding?: EdgePadding, duration?: number }` | Returns `Promise<void>` when the animation finishes. |
| `fitToSuppliedMarkers` | `markerIDs: String[], options?: { edgePadding?: EdgePadding, duration?: number }` | Returns `Promise<void>` when the animation finishes. |
| `getCamera` | | Returns a `Promise<Camera>` structure indicating the current camera configuration. |
| `getMapBoundaries` | | `Promise<{northEast: LatLng, southWest: LatLng}>` |
| `getMarkersFrames` | `onlyVisible: Boolean` | Get markers' centers and frames in view coordinates. Returns a `Promise<{ "markerID" : { point: Point, frame: Frame } }>`. **Note**: iOS only. |
| `pointForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a view coordinate (`Point`). Returns a `Promise<Point>`. |

## Types

Expand Down
112 changes: 99 additions & 13 deletions ios/Maps/RNMMapViewModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -287,21 +287,62 @@ - (dispatch_queue_t)methodQueue
RCTLogError(@"Invalid view returned from registry, expecting RNMMap, got: %@", view);
} else {
RNMMap *mapView = (RNMMap *)view;

if(mapView.annotations.count < 1) {
resolve(nil);
} else {
CLLocationDegrees minLatitude = DBL_MAX;
CLLocationDegrees maxLatitude = -DBL_MAX;
CLLocationDegrees minLongitude = DBL_MAX;
CLLocationDegrees maxLongitude = -DBL_MAX;

for(id<MKAnnotation> annotation in mapView.annotations) {
double annotationLat = annotation.coordinate.latitude;
double annotationLong = annotation.coordinate.longitude;
minLatitude = fmin(annotationLat, minLatitude);
maxLatitude = fmax(annotationLat, maxLatitude);
minLongitude = fmin(annotationLong, minLongitude);
maxLongitude = fmax(annotationLong, maxLongitude);
}

double latitudeDelta = maxLatitude - minLatitude;
double longitudeDelta = maxLongitude - minLongitude;

double mapViewHeight = mapView.bounds.size.height;
double mapViewWidth = mapView.bounds.size.width;

double latPerHeight = latitudeDelta / mapViewHeight;
double lngPerWidth = longitudeDelta / mapViewWidth;

CGFloat topPadding = [RCTConvert CGFloat:edgePadding[@"top"]];
CGFloat rightPadding = [RCTConvert CGFloat:edgePadding[@"right"]];
CGFloat bottomPadding = [RCTConvert CGFloat:edgePadding[@"bottom"]];
CGFloat leftPadding = [RCTConvert CGFloat:edgePadding[@"left"]];

maxLatitude = maxLatitude + topPadding * latPerHeight;
minLatitude = minLatitude - bottomPadding * latPerHeight;
maxLongitude = maxLongitude + rightPadding * lngPerWidth;
minLongitude = minLongitude - leftPadding * lngPerWidth;

MKCoordinateRegion region;
region.center.latitude = (minLatitude + maxLatitude) / 2;
region.center.longitude = (minLongitude + maxLongitude) / 2;
// if fitting a single marker or if all markers are aligned, the delta will be zero (infinite zoom)
// this will cause mapkit not to zoom at all. Picking an arbitrary small number to circumvent
region.span.latitudeDelta = fmax(maxLatitude - minLatitude, 0.000001);
region.span.longitudeDelta = fmax(maxLongitude - minLongitude, 0.000001);

// TODO(lmr): we potentially want to include overlays here... and could concat the two arrays together.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if(duration > 0.0f) {
[RNMMap animateWithDuration:duration/1000 animations:^{
[mapView showAnnotations:mapView.annotations animated:YES];
[mapView setRegion:region animated:YES];
} completion:^(BOOL finished){
resolve(nil);
}];
} else {
[mapView showAnnotations:mapView.annotations animated:NO];
[mapView setRegion:region animated:NO];
resolve(nil);
}

});
}
}
}];
}
Expand All @@ -328,17 +369,62 @@ - (dispatch_queue_t)methodQueue
}];

NSArray *filteredMarkers = [mapView.annotations filteredArrayUsingPredicate:filterMarkers];

if(filteredMarkers.count < 1) {
resolve(nil);
} else {
CLLocationDegrees minLatitude = DBL_MAX;
CLLocationDegrees maxLatitude = -DBL_MAX;
CLLocationDegrees minLongitude = DBL_MAX;
CLLocationDegrees maxLongitude = -DBL_MAX;

for(id<MKAnnotation> annotation in filteredMarkers) {
double annotationLat = annotation.coordinate.latitude;
double annotationLong = annotation.coordinate.longitude;
minLatitude = fmin(annotationLat, minLatitude);
maxLatitude = fmax(annotationLat, maxLatitude);
minLongitude = fmin(annotationLong, minLongitude);
maxLongitude = fmax(annotationLong, maxLongitude);
}

double latitudeDelta = maxLatitude - minLatitude;
double longitudeDelta = maxLongitude - minLongitude;

double mapViewHeight = mapView.bounds.size.height;
double mapViewWidth = mapView.bounds.size.width;

double latPerHeight = latitudeDelta / mapViewHeight;
double lngPerWidth = longitudeDelta / mapViewWidth;

CGFloat topPadding = [RCTConvert CGFloat:edgePadding[@"top"]];
CGFloat rightPadding = [RCTConvert CGFloat:edgePadding[@"right"]];
CGFloat bottomPadding = [RCTConvert CGFloat:edgePadding[@"bottom"]];
CGFloat leftPadding = [RCTConvert CGFloat:edgePadding[@"left"]];

maxLatitude = maxLatitude + topPadding * latPerHeight;
minLatitude = minLatitude - bottomPadding * latPerHeight;
maxLongitude = maxLongitude + rightPadding * lngPerWidth;
minLongitude = minLongitude - leftPadding * lngPerWidth;

MKCoordinateRegion region;
region.center.latitude = (minLatitude + maxLatitude) / 2;
region.center.longitude = (minLongitude + maxLongitude) / 2;
// if fitting a single marker or if all markers are aligned, the delta will be zero (infinite zoom)
// this will cause mapkit not to zoom at all. Picking an arbitrary small number to circumvent
region.span.latitudeDelta = fmax(maxLatitude - minLatitude, 0.000001);
region.span.longitudeDelta = fmax(maxLongitude - minLongitude, 0.000001);

if(duration > 0.0f) {
[RNMMap animateWithDuration:duration/1000 animations:^{
[mapView showAnnotations:filteredMarkers animated:YES];
} completion:^(BOOL finished){
resolve(nil);
}];
if(duration > 0.0f) {
[RNMMap animateWithDuration:duration/1000 animations:^{
[mapView setRegion:region animated:YES];
} completion:^(BOOL finished){
resolve(nil);
}];
} else {
[mapView showAnnotations:filteredMarkers animated:NO];
[mapView setRegion:region animated:NO];
resolve(nil);
}
}
}
}];
}
Expand Down

0 comments on commit cf58b84

Please sign in to comment.