Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Some improvements #32

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
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
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@


# react-native-geocoder
# react-native-geocoder

[![CircleCI](https://circleci.com/gh/devfd/react-native-geocoder/tree/master.svg?style=shield)](https://circleci.com/gh/devfd/react-native-geocoder/tree/master)

Expand Down Expand Up @@ -114,13 +114,17 @@ both iOS and Android will return the following object:
```js
{
position: {lat, lng},
region: {
center: {lat, lng},
radius: Number,
} | null,
formattedAddress: String, // the full address
feature: String | null, // ex Yosemite Park, Eiffel Tower
streetNumber: String | null,
streetName: String | null,
postalCode: String | null,
locality: String | null, // city name
country: String,
country: String,
countryCode: String
adminArea: String | null
subAdminArea: String | null,
Expand All @@ -135,5 +139,4 @@ iOS does not allow sending multiple geocoding requests simultaneously, unless yo

### Android
geocoding may not work on older android devices (4.1) and will not work if Google play services are not available.


`region` will always be `null` on Android since it doesn't support the feature. In this case, `Geocoder.geocodePositionFallback` and `Geocoder.geocodeAddressFallback` may be used to get the `region` value.
34 changes: 32 additions & 2 deletions android/src/main/java/com/devfd/RNGeocoder/RNGeocoderModule.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.devfd.RNGeocoder;

import android.content.Context;
import android.location.Address;
import android.location.Geocoder;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
Expand All @@ -15,21 +17,41 @@

import java.io.IOException;
import java.util.List;
import java.util.Locale;

public class RNGeocoderModule extends ReactContextBaseJavaModule {

private Context context;
private Locale locale;
private Geocoder geocoder;

public RNGeocoderModule(ReactApplicationContext reactContext) {
super(reactContext);
geocoder = new Geocoder(reactContext.getApplicationContext());
context = reactContext.getApplicationContext();

locale = context.getResources().getConfiguration().locale;
geocoder = new Geocoder(context, locale);
}

@Override
public String getName() {
return "RNGeocoder";
}

@ReactMethod
public void setLanguage(String language, Callback callback) {
Locale target = new Locale(language);
if (!context.getResources().getConfiguration().locale.equals(target) && !locale.equals(target)) {
locale = target;
geocoder = new Geocoder(context, locale);

callback.invoke(language);
return;
}

callback.invoke((String) null);
}

@ReactMethod
public void geocodeAddress(String addressName, Promise promise) {
if (!geocoder.isPresent()) {
Expand All @@ -39,7 +61,12 @@ public void geocodeAddress(String addressName, Promise promise) {

try {
List<Address> addresses = geocoder.getFromLocationName(addressName, 20);
promise.resolve(transform(addresses));
if(addresses != null && addresses.size() > 0) {
promise.resolve(transform(addresses));
} else {
promise.reject("NOT_AVAILABLE", "Geocoder returned an empty list");
}

}

catch (IOException e) {
Expand Down Expand Up @@ -74,6 +101,9 @@ WritableArray transform(List<Address> addresses) {
position.putDouble("lng", address.getLongitude());
result.putMap("position", position);

// There is no region field in the `Address` object.
result.putString("region", null);

final String feature_name = address.getFeatureName();
if (feature_name != null && !feature_name.equals(address.getSubThoroughfare()) &&
!feature_name.equals(address.getThoroughfare()) &&
Expand Down
4 changes: 2 additions & 2 deletions ios/RNGeocoder/RNGeocoder.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#import "RCTBridgeModule.h"
#import "RCTConvert.h"
#import <React/RCTBridgeModule.h>
#import <React/RCTConvert.h>

#import <CoreLocation/CoreLocation.h>

Expand Down
23 changes: 21 additions & 2 deletions ios/RNGeocoder/RNGeocoder.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#import <CoreLocation/CoreLocation.h>

#import "RCTConvert.h"
#import <React/RCTConvert.h>

@implementation RCTConvert (CoreLocation)

Expand All @@ -22,6 +22,17 @@ @implementation RNGeocoder

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(setLanguage:(NSString *)language
callback:(RCTResponseSenderBlock)callback)
{
NSString *deviceLanguage = [[NSLocale preferredLanguages] objectAtIndex:0];
if ([deviceLanguage isEqualToString:language]) {
return callback(@[[NSNull null]]);
}

callback(@[language]);
}

RCT_EXPORT_METHOD(geocodePosition:(CLLocation *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
Expand Down Expand Up @@ -81,6 +92,7 @@ - (NSArray *)placemarksToDictionary:(NSArray *)placemarks {

for (int i = 0; i < placemarks.count; i++) {
CLPlacemark* placemark = [placemarks objectAtIndex:i];
CLCircularRegion* region = placemark.region;

NSString* name = [NSNull null];

Expand All @@ -100,6 +112,13 @@ - (NSArray *)placemarksToDictionary:(NSArray *)placemarks {
@"lat": [NSNumber numberWithDouble:placemark.location.coordinate.latitude],
@"lng": [NSNumber numberWithDouble:placemark.location.coordinate.longitude],
},
@"region": placemark.region ? @{
@"center": @{
@"lat": [NSNumber numberWithDouble:region.center.latitude],
@"lng": [NSNumber numberWithDouble:region.center.longitude],
},
@"radius": [NSNumber numberWithDouble:region.radius],
} : [NSNull null],
@"country": placemark.country ?: [NSNull null],
@"countryCode": placemark.ISOcountryCode ?: [NSNull null],
@"locality": placemark.locality ?: [NSNull null],
Expand All @@ -109,7 +128,7 @@ - (NSArray *)placemarksToDictionary:(NSArray *)placemarks {
@"postalCode": placemark.postalCode ?: [NSNull null],
@"adminArea": placemark.administrativeArea ?: [NSNull null],
@"subAdminArea": placemark.subAdministrativeArea ?: [NSNull null],
@"formattedAddress": [lines componentsJoinedByString:@", "]
@"formattedAddress": [lines componentsJoinedByString:@", "],
};

[results addObject:result];
Expand Down
37 changes: 32 additions & 5 deletions js/geocoder.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
import { NativeModules } from 'react-native';
import { NativeModules, Platform } from 'react-native';
import GoogleApi from './googleApi.js';

const { RNGeocoder } = NativeModules;

export default {
apiKey: null,
language: null,

fallbackToGoogle(key) {
this.apiKey = key;
},

setLanguage(language) {
RNGeocoder.setLanguage(language, (result) => {
this.language = result;
});
},

geocodePositionFallback(position) {
if (!this.apiKey) { throw new Error("Google API key required"); }

return GoogleApi.geocodePosition(this.apiKey, position, this.language);
},

geocodeAddressFallback(address) {
if (!this.apiKey) { throw new Error("Google API key required"); }

return GoogleApi.geocodeAddress(this.apiKey, address, this.language);
},

geocodePosition(position) {
if (!position || !position.lat || !position.lng) {
return Promise.reject(new Error("invalid position: {lat, lng} required"));
}

if (this.language && (Platform.OS === 'ios')) {
return this.geocodePositionFallback(position);
}

return RNGeocoder.geocodePosition(position).catch(err => {
if (!this.apiKey || err.code !== 'NOT_AVAILABLE') { throw err; }
return GoogleApi.geocodePosition(this.apiKey, position);
if (err.code !== 'NOT_AVAILABLE') { throw err; }
return this.geocodePositionFallback(position);
});
},

Expand All @@ -26,9 +49,13 @@ export default {
return Promise.reject(new Error("address is null"));
}

if (this.language && (Platform.OS === 'ios')) {
return this.geocodeAddressFallback(address);
}

return RNGeocoder.geocodeAddress(address).catch(err => {
if (!this.apiKey || err.code !== 'NOT_AVAILABLE') { throw err; }
return GoogleApi.geocodeAddress(this.apiKey, address);
if (err.code !== 'NOT_AVAILABLE') { throw err; }
return this.geocodeAddressFallback(address);
});
},
}
Loading