Skip to content

Commit

Permalink
New methods to convert between LatLng and Point (react-native-maps#1851)
Browse files Browse the repository at this point in the history
* New methods to convert between LatLng and Point

* Clarification for `pointForCoordinate` and `coordinateForPoint`
  • Loading branch information
danielgindi authored and pedrolopes10 committed Jan 10, 2018
1 parent b4ad434 commit d7226ab
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/mapview.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ To access event data, you will need to use `e.nativeEvent`. For example, `onPres
| `fitToElements` | `animated: Boolean` |
| `fitToSuppliedMarkers` | `markerIDs: String[]`, `animated: Boolean` | If you need to use this in `ComponentDidMount`, make sure you put it in a timeout or it will cause performance problems.
| `fitToCoordinates` | `coordinates: Array<LatLng>, options: { edgePadding: EdgePadding, animated: Boolean }` | If called in `ComponentDidMount` in android, it will cause an exception. It is recommended to call it from the MapView `onLayout` event.
| `pointForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a view coordinate (`Point`). Returns a `Promise<Point>`.
| `coordinateForPoint` | `point: Point` | Converts a view coordinate (`Point`) to a map coordinate. Returns a `Promise<Coordinate>`.



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.net.Uri;
import android.util.Base64;
import android.util.DisplayMetrics;
Expand All @@ -11,11 +12,13 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.UIBlock;
import com.facebook.react.uimanager.UIManagerModule;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.maps.model.LatLng;

import java.io.ByteArrayOutputStream;
import java.io.Closeable;
Expand Down Expand Up @@ -135,4 +138,76 @@ public void onSnapshotReady(@Nullable Bitmap snapshot) {
}
});
}

@ReactMethod
public void pointForCoordinate(final int tag, ReadableMap coordinate, final Promise promise) {
final LatLng coord = new LatLng(
coordinate.hasKey("latitude") ? coordinate.getDouble("latitude") : 0.0,
coordinate.hasKey("longitude") ? coordinate.getDouble("longitude") : 0.0
);

final ReactApplicationContext context = getReactApplicationContext();
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
uiManager.addUIBlock(new UIBlock()
{
@Override
public void execute(NativeViewHierarchyManager nvhm)
{
AirMapView view = (AirMapView) nvhm.resolveView(tag);
if (view == null) {
promise.reject("AirMapView not found");
return;
}
if (view.map == null) {
promise.reject("AirMapView.map is not valid");
return;
}

Point pt = view.map.getProjection().toScreenLocation(coord);

WritableMap ptJson = new WritableNativeMap();
ptJson.putDouble("x", pt.x);
ptJson.putDouble("y", pt.y);

promise.resolve(ptJson);
}
});
}

@ReactMethod
public void coordinateForPoint(final int tag, ReadableMap point, final Promise promise) {
final Point pt = new Point(
point.hasKey("x") ? point.getInt("x") : 0,
point.hasKey("y") ? point.getInt("y") : 0
);

final ReactApplicationContext context = getReactApplicationContext();
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
uiManager.addUIBlock(new UIBlock()
{
@Override
public void execute(NativeViewHierarchyManager nvhm)
{
AirMapView view = (AirMapView) nvhm.resolveView(tag);
if (view == null)
{
promise.reject("AirMapView not found");
return;
}
if (view.map == null)
{
promise.reject("AirMapView.map is not valid");
return;
}

LatLng coord = view.map.getProjection().fromScreenLocation(pt);

WritableMap coordJson = new WritableNativeMap();
coordJson.putDouble("latitude", coord.latitude);
coordJson.putDouble("longitude", coord.longitude);

promise.resolve(coordJson);
}
});
}
}
58 changes: 58 additions & 0 deletions lib/components/MapView.js
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,64 @@ class MapView extends React.Component {
return Promise.reject('takeSnapshot not supported on this platform');
}

/**
* Convert a map coordinate to user-space point
*
* @param coordinate Coordinate
* @param [coordinate.latitude] Latitude
* @param [coordinate.longitude] Longitude
*
* @return Promise Promise with the point ({ x: Number, y: Number })
*/
pointForCoordinate(coordinate) {
if (Platform.OS === 'android') {
return NativeModules.AirMapModule.pointForCoordinate(this._getHandle(), coordinate);
} else if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
this._runCommand('pointForCoordinate', [
coordinate,
(err, snapshot) => {
if (err) {
reject(err);
} else {
resolve(snapshot);
}
},
]);
});
}
return Promise.reject('pointForCoordinate not supported on this platform');
}

/**
* Convert a user-space point to a map coordinate
*
* @param point Point
* @param [point.x] X
* @param [point.x] Y
*
* @return Promise Promise with the coordinate ({ latitude: Number, longitude: Number })
*/
coordinateFromPoint(point) {
if (Platform.OS === 'android') {
return NativeModules.AirMapModule.coordinateFromPoint(this._getHandle(), point);
} else if (Platform.OS === 'ios') {
return new Promise((resolve, reject) => {
this._runCommand('coordinateFromPoint', [
point,
(err, snapshot) => {
if (err) {
reject(err);
} else {
resolve(snapshot);
}
},
]);
});
}
return Promise.reject('coordinateFromPoint not supported on this platform');
}

_uiManagerCommand(name) {
return NativeModules.UIManager[getAirMapName(this.props.provider)].Commands[name];
}
Expand Down
53 changes: 53 additions & 0 deletions lib/ios/AirGoogleMaps/AIRGoogleMapManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,59 @@ - (UIView *)view
}];
}

RCT_EXPORT_METHOD(pointForCoordinate:(nonnull NSNumber *)reactTag
coordinate:(NSDictionary *)coordinate
withCallback:(RCTResponseSenderBlock)callback)
{
CLLocationCoordinate2D coord =
CLLocationCoordinate2DMake(
[coordinate[@"latitude"] doubleValue],
[coordinate[@"longitude"] doubleValue]
);

[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 AIRMap, got: %@", view);
} else {
AIRGoogleMap *mapView = (AIRGoogleMap *)view;

CGPoint touchPoint = [mapView.projection pointForCoordinate:coord];

callback(@[[NSNull null], @{
@"x": @(touchPoint.x),
@"y": @(touchPoint.y),
}]);
}
}];
}

RCT_EXPORT_METHOD(coordinateForPoint:(nonnull NSNumber *)reactTag
point:(NSDictionary *)point
withCallback:(RCTResponseSenderBlock)callback)
{
CGPoint pt = CGPointMake(
[point[@"x"] doubleValue],
[point[@"y"] doubleValue]
);

[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 AIRMap, got: %@", view);
} else {
AIRGoogleMap *mapView = (AIRGoogleMap *)view;

CLLocationCoordinate2D coordinate = [mapView.projection coordinateForPoint:pt];

callback(@[[NSNull null], @{
@"latitude": @(coordinate.latitude),
@"longitude": @(coordinate.longitude),
}]);
}
}];
}

RCT_EXPORT_METHOD(setMapBoundaries:(nonnull NSNumber *)reactTag
northEast:(CLLocationCoordinate2D)northEast
southWest:(CLLocationCoordinate2D)southWest)
Expand Down
32 changes: 32 additions & 0 deletions lib/ios/AirMaps/AIRMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,38 @@ - (void)setLoadingIndicatorColor:(UIColor *)loadingIndicatorColor {
self.activityIndicatorView.color = loadingIndicatorColor;
}

RCT_EXPORT_METHOD(pointForCoordinate:(NSDictionary *)coordinate resolver: (RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
CGPoint touchPoint = [self convertCoordinate:
CLLocationCoordinate2DMake(
[coordinate[@"lat"] doubleValue],
[coordinate[@"lng"] doubleValue]
)
toPointToView:self];

resolve(@{
@"x": @(touchPoint.x),
@"y": @(touchPoint.y),
});
}

RCT_EXPORT_METHOD(coordinateForPoint:(NSDictionary *)point resolver: (RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
CLLocationCoordinate2D coordinate = [self convertPoint:
CGPointMake(
[point[@"x"] doubleValue],
[point[@"y"] doubleValue]
)
toCoordinateFromView:self];

resolve(@{
@"lat": @(coordinate.latitude),
@"lng": @(coordinate.longitude),
});
}

// Include properties of MKMapView which are only available on iOS 9+
// and check if their selector is available before calling super method.

Expand Down

0 comments on commit d7226ab

Please sign in to comment.